Can using setattr (and getattr) be considered bad practice?

setattr and getattr look into my programming style (mostly scientific material, my knowledge of python is self-explanatory).

Given that exec and eval inherit a potential danger, since in some cases they can lead to security problems, I was wondering if for setattr the same argument is considered valid. (About getattr I found this question that contains some information - although the argument is not very convincing.)

From what I know, setattr can be used without worrying about a lot, but to be honest, I don't trust my knowledge of python enough to be sure, and if I'm wrong, I would like to try to get rid of the habit of using setattr .

Any input is much appreciated!

+7
source share
1 answer

Firstly, it will definitely ease an existing security hole.

For example, let's say you have code that does exec , eval , SQL queries, or URLs built using string formatting, etc. And let them say that you pass, say, locals() or filtered __dict__ into a format command or in the context of eval or something else. Using setattr clearly expands the security area, which makes it much easier for me to find ways to attack your code, because you can no longer be sure what you are going to pass to these functions.

But what if you don't do anything else unsafe? Is setattr ?

Not so bad, but it's still not safe. If I can influence the names of the attributes that you set, I can, for example, replace any method that I want with your objects.

You can try to protect against this, for example, by first checking that the old value was not called, or not a method type descriptor, or something else. In the same way, you can try to protect against people calling functions in eval or by placing quotation marks and semicolons in SQL parameters and so on. This is practically the same as any other case. And it’s much harder to try to close all the illegal paths to an open door than just not opening the door in the first place.

What if the name never comes from what the user can influence?

Well then, why are you using setattr ? There is no good reason to call setattr literal.

In any case, when Lattyware stated that there are often ways to solve this problem, it almost certainly speaks of readability, maintainability and idiom. But a side effect of using these best practices is that you also often avoid any security implications.

90% of the time, the solution should use a dict instead of an object. Unlike Javascript, they are not the same in Python, and they are not intended to be used the same way. A dict has no methods, inheritance, or built-in special names, so you don't need to worry about that. It also has a more convenient syntax where you can say d['foo'] instead of setattr(o, 'foo') . And this is probably more effective. And so on. But ultimately, the reason for using dict is a conceptual reason: a dict is a named set of values; an instance of a class is a representation of an object of a model space, and this is not the same thing.

So why does setattr exist?

It exists for the same main reasons as other low-level functions, for example, the ability to access im_func or func_closure or the presence of modules such as traceback and imp , or the processing of special methods, like any other methods or, if it is important, exec and eval .

First, you can create higher-level things from these low-level tools. For example, to build collections.namedtuple , you need exec or setattr .

Secondly, you sometimes need the monkey-patch code at runtime because you cannot modify it (or maybe even see it) at compile time, and tools like setattr may be needed for this.

The setattr function, which is very similar to eval , is often abused by people coming from Javascript, Tcl, or several other languages. But as long as it can be used forever, you do not want to take it out of the language. (TOOWTDI should not be taken so literally that only one program can be written.)

But this does not mean that you should use this material whenever possible. You do not write mylist.__getitem__(slice(1, 10, 2)) instead of mylist[1:10:2] . Sometimes the ability to directly call __getitem__ or build slice objects is the basis for something that allows the rest of your code to be more pythonic or a way to localize a workaround to avoid infecting the rest of your code. Otherwise, there are simpler and simpler ways to do this.

+10
source

All Articles