6

How can I protect my variables from this kind of attack:

MyClass.__dict__ = {}
MyClass.__dict__.__setitem__('_MyClass__protectedVariable','...but it is not')

The above changes the variable dictionary and after that it is childs play to change all the variables. The upper line is crucial for this to work. The above does not work if your dictionary's __setitem__ is tweaked like below).

I want to force user to use my method setProtectedVariable(value) to change the variable, but I seem to find no way of doing that in Python 2.7. Any ideas?

I appreciate also if you find other similar holes from the code below (I noticed that I should add also the file name and line number to my inspect.stack check in myDict.__setitem__).

This is what I have tried so far:

import inspect

class ProtectionTest:

    __myPrivate = 0

    def __init__(self):
        md = myDict()
        setattr(self,'__dict__', md)

    def __setattr__(self, name, val):     
        if name == '__myPrivate':
            print "failed setattr attempt: __myPrivate"
            pass
        elif name == '_ProtectionTest__myPrivate':
            print "failed setattr attempt: _ProtectionTest__myPrivate"  
            pass
        elif name == '__dict__':
            print "failed setattr attempt: __dict__"
            pass
        else: 
            self.__dict__[name] = val             

    def getMyPrivate(self):
        return self.__myPrivate

    def setMyPrivate(self, myPrivate):
        #self.__dict__['_ProtectionTest__stack'] = inspect.stack()[0][1:]
        self.__dict__['_ProtectionTest__myPrivate'] = -myPrivate

class myDict(dict):

    def __init__(self):
        dict.__init__(self)

    def __setitem__(self, key, value):
        if inspect.stack()[1][3] == 'setMyPrivate':
            dict.__setitem__(self,key,value)
        else:
            print "failed dict attempt"
            pass

pt = ProtectionTest()

print "trying to change... (success: 1): "
pt.__myPrivate = 1
print pt.getMyPrivate(), '\n'

print "trying to change... (success: 2): "
pt._ProtectionTest__myPrivate = 2
print pt.getMyPrivate() , '\n'

print "trying to change... (success: 3): "
pt.__dict__['_ProtectionTest__myPrivate'] = 3
print pt.getMyPrivate() , '\n'

print "trying to change the function (success: 4): "
def setMyPrivate(self, myPrivate):
    self.__dict__['_ProtectionTest__myPrivate'] = 4
pt.setMyPrivate = setMyPrivate
pt.setMyPrivate(0)
print pt.getMyPrivate(), '\n'

print "trying to change the dict (success: 5): "
pt.__dict__ = {}
pt.__dict__.__setitem__('_ProtectionTest__myPrivate',5)
print pt.getMyPrivate(), '\n'

print "Still working (correct output = -input = -100): "    
pt.setMyPrivate(100)
print pt.getMyPrivate()  
Wooble
  • 87,717
  • 12
  • 108
  • 131
Juha
  • 2,053
  • 23
  • 44
  • 5
    Why would you want to do this? Why do you care what another programmer does with your class? You're in charge of making your code work properly according to the spec, if another programmer wants to abuse it that's his/her problem, isn't it? – Daniel Roseman Feb 07 '12 at 15:47
  • I doubt you'll find a bulletproof way to protect against every possible misuse by a malicious user. You might as well give up now. – NPE Feb 07 '12 at 15:47
  • 1
    You're being positive today... This is also an answer to the question: Do private variables and methods exist in python and why they (don't) exist. – Juha Feb 07 '12 at 15:52
  • @DSM I will test your 'evilness' ;) – Juha Feb 07 '12 at 16:09
  • 6
    Not worth the effort. "I hacked your class in your app and wiped out all my data". is the point where you press the mute button on the phone so they don't hear you laughing your arse off while wondering if you can get paid ( a LOT) to fix it. – Tony Hopkinson Feb 07 '12 at 16:10
  • @DanielRoseman Answer to the question "why I would do this?" would be "I want to learn python". I still don't know how to modify/disable dict or builtins from python (in c I would just hack gcc and libc), but I guess asking it this way is going to solve my problem. The correct way to ask this would be "How can I modify builtin functions in python if I am the only one having administration rights". Anyway I have learned a lot from your answers, so thank you all for participating the discussion. – Juha Feb 08 '12 at 11:39

4 Answers4

18

I feel that there is some deep confusion motivating this question. Private variables aren't there to keep the evil "hackers" away. They have nothing to do with security. They're there to promote good programming practices like maintaining low coupling.

If an "evil programmer" has access to your source code, he or she can do whatever he or she wants to with it. Calling a variable "private" won't change that. If said evil programmer is trying to compromise your program executing on another system... calling a variable "private" will do you no good. It doesn't change anything about the way the program is stored and manipulated in memory. It just enforces (in an unnecessarily complex way IMO) separation of concerns.

Also, it's worth noting that under normal circumstances you don't have to go through all these shenanigans...

MyClass.__dict__ = {}
MyClass.__dict__.__setitem__('_MyClass__protectedVariable','...but it is not')

...to assign to a protected var. You don't even have to overwrite __dict__. You can just do this:

MyClass._MyClass__protectedVariable = '...but it is not'

Cause it's really not. Protected, I mean. The main purpose of name mangling is to prevent namespace collisions. If you just want a "private" attribute, simply prefix it with a single underscore. Expect your users to respect convention, and expect your abusers to break it no matter what you do.

senderle
  • 145,869
  • 36
  • 209
  • 233
  • While I was studying your answer I noticed that the two "shenanigans" you mention has one big difference: former calls the `object.__dict__.__setitem__` and the latter calls `object.__setattr__`. This does not make a big difference in the overall result, but I think is a worth mentioning if you have slightly exotic system (like in this case). Anyway, good answer, thanks. – Juha Feb 08 '12 at 12:26
  • @Juha, OK, I see what you mean -- it wasn't clear to me that you were talking about "this kind of attack" _on your customized_ `__setattr__`. I changed the above to comport with that understanding. – senderle Feb 08 '12 at 14:07
8

In Python, you cannot "protect" attributes this way. Why are you in an antagonistic relationship with your caller? You and he need to agree on some things, this is one of them. Write better docs, become better friends with him. I don't know what your real problem is, but it cannot be solved with code.

Other languages like Java and C++ offer separation with private and so on, but Python simply does not. You cannot bolt it on after the fact.

If you tell us more about who this evil programmer is, and why you and he disagree about how your code should be used, we might be able to give you other ideas for solutions.

Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
7

AFAIK there's not really a way to do this in Python. No matter what you do, anyone could always copy your source and remove your hacks, or (in most cases) either inherit from the class and override it or just directly reassign the methods.

But: why do you care so much? If you name it __whatever that's very clear documentation that if users mess with this, anything bad that happens is their own damn fault.

Danica
  • 28,423
  • 6
  • 90
  • 122
3

Python isn't very protective regarding this kind of data alteration. But this is seen to be a feature. Maybe a different question about constants/finals at `final` keyword equivalent for variables in Python? is of some help. To answer your question: There is probably no way to protect data against manipulation in your class running with foreign code in the same executable. You would most probably need to store your data in a separate process and provide some kind of api to exchange data with the foreign process.

Community
  • 1
  • 1
itsafire
  • 5,607
  • 3
  • 37
  • 48