Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to protect python class variables from an evil programmer?

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()  
like image 730
Juha Avatar asked Feb 07 '12 15:02

Juha


People also ask

Can you have private variables in Python?

In Python, there is no existence of “Private” instance variables that cannot be accessed except inside an object.

How do you access a private variable outside the class in Python?

Python doesn't have real private variables, so use the __ prefix (two underlines at the beginning make a variable) from PEP 8. Use instance _class-name__private-attribute try to access private variables outside the class in Python.

How do you make a class variable private in Python?

In actual terms (practically), python doesn't have anything called private member variable in Python. However, adding two underlines(__) at the beginning makes a variable or a method private is the convention used by most python code.

How do you make a protected variable in Python?

Python's convention to make an instance variable protected is to add a prefix _ (single underscore) to it. This effectively prevents it from being accessed unless it is from within a sub-class.


1 Answers

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.

like image 67
senderle Avatar answered Sep 30 '22 07:09

senderle