First, it could definitely make it easier to an existing security hole.
For example, let's say you have code that does exec
, eval
, SQL queries or URLs built via string formatting, etc. And let's say you're passing, say, locals()
or a filtered __dict__
to the formatting command or as the eval
context or whatever. Using setattr
clearly widens the security hole, making it much easier for me to find ways to attack your code, because you can no longer be sure what you're going to be passing to those functions.
But what if you don't do anything else unsafe? Is setattr
safe then?
Not as bad, but it's still not safe. If I can influence the names of the attributes you're setting, I can, e.g., replace any method I want on your objects.
You can try to protect against this by, e.g., first checking that the old value was not callable, or not a method-type descriptor, or whatever. In the same way you can try to protect against people calling functions in eval
or putting quotes and semicolons in SQL parameters and so on. This is effectively the same as any of those other cases. And it's a lot harder to try to close all illegitimate paths to an open door, than to just not open the door in the first place.
What if the name never comes from anything that can be influenced by the user?
Well, in that case, why are you using setattr
? There is no good reason to call setattr
with a literal.
Anyway, when Lattyware said that there are often better ways to solve the problem at hand, he was almost certainly talking about readability, maintainability, and idiomaticness. But the side effect of using those better ways is that you also often avoid any security implications.
90% of the time, the solution is to use a dict
instead of an object. Unlike Javascript, they're not the same thing in Python, and they're not meant to be used the same way. A dict
doesn't have methods, or inheritance, or built-in special names, so you don't have to worry about any of that. It also has a more convenient syntax, where you can say d['foo']
instead of setattr(o, 'foo')
. And it's probably more efficient. And so on. But ultimately, the reason to use a dict
is the conceptual reason: a dict
is a named collection of values; a class instance is a representation of a model-space object, and those are not the same thing.
So, why does setattr
even exist?
It's there for the same basic reasons as other low-level features, like being able to access im_func
or func_closure
, or having modules like traceback
and imp
, or treating special methods just like any other methods, or for that matter exec
and eval
.
First, you can build higher-level things out of these low-level tools. For example, to build collections.namedtuple
, you'd need either exec
or setattr
.
Second, you occasionally need to monkey-patch code at runtime because you can't modify it (or maybe even see it) at compile time, and tools like setattr
can be essential to doing that.
The setattr
feature—much like eval
—is often misused by people coming from Javascript, Tcl, or a few other languages. But as long as it can be used for good, you don't want to take it out of the language. (TOOWTDI shouldn't be taken so literally that only one program can ever be written.)
But that doesn't mean you should go around using this stuff whenever possible. You wouldn't write mylist.__getitem__(slice(1, 10, 2))
instead of mylist[1:10:2]
. Sometimes, being able to call __getitem__
directly or build slice
objects explicitly is a foundation to something that lets the rest of your code be more pythonic, or way to localize a workaround to avoid infecting the rest of your code. Otherwise, there are clearer and simpler ways to do it.
getattr
is really relevant—everyone there is talking about performance, not security. – Righteous