Wednesday
Feb132008
The Tainted Edition
Wednesday, February 13, 2008 at 12:19PM There has been a discussion about whether to untaint or not. A string becomes tainted in Ruby when it comes from an external source, for example. The standard Ruby method untaint marks it as untainted. Plugins such as SafeErb do not allow the programmer to output tainted strings (in Erb) in order to protect the application from XSS. This works fine in my experience (even in Rails 2) and won't need you to untaint manually too often.
There is a good comment in the mentioned discussion (and the author has written an interesting article about safe strings):
"What most tainting-based solutions seem to miss is that 'safeness' or 'taintedness' is not a property of a string but a relationship between the string and the contexts in which it is used."
Indeed, SafeErb escapes and untaints the strings according to one context only - HTML (SGML) - and that is the purpose of the plugin. But there are a lot of other contexts you will use strings in:
SQL
Thanks to clever helper methods, this is hardly a problem in most Rails applications. However, you have to follow the rules and remember the problem whenever you use tainted strings in plain SQL or uncommon places. Use sanitize_sql() to escape in this context.
SGML (HTML, XML, RSS...)
CSS
Beware of CSS Injection if you render style sheets using tainted strings. This was a hole the Samy worm exploited.
Update: Textile
Textile (by means of RedCloth) should be used only together with a white-list sanitizer like Rails' sanitize(). In addition to my previous post, it is possible to inject scripts in image titles like this: !bunny.gif(Bunny" onclick="alert(1))!
Will become:<p><img .src="bunny.gif" title="Bunny" onclick="alert(1)" alt="Bunny" onclick="alert(1)" /></p>
JavaScript
Do escape strings in a JS context as well if you render code using tainted strings.
JSON
Rails
Do you use the params hash in Rails methods that accept hashes? How about redirect_to(params) or similar? Imagine what will happen when the hash contains a :host key! This is logic injection and can't be sanitized.
Log files
Someone could fill up your server's disk or manipulate your log files using log file injection. Remember to sanitize, filter (see filter_parameter_logging()) and truncate here.
Second level
Beware of command line injection.
All these different contexts raise the question when to sanitize a string, before or after storing it (if it is stored). Of course for some contexts (SQL, shell), it certainly has to be sanitized before being stored/processed. But what about the most important context in web applications, SGML? Will I save a sanitized string or the raw input? I prefer to sanitize (or escape actually) strings according to a context when using it in that context, because in a large application you will never know in which contexts you will use the input data in the future.
For performance reasons I store one untouched and one escaped (and even textiled and white-listed) version of the input. The raw version will be used if the user wants to edit the data, and the untainted version will be displayed.
There have come up quite a lot of plugins recently trying to automatically escape the user input data.
- Sanitize_params walks the params hash and sanitizes the strings using the white_list plugin.
- XSS_terminate votes for retrospective sanitization by automatically escaping all the fields in a model when loaded.
- CrossSiteSniper seems to do pretty much the same.
Heiko |
9 Comments | 



Reader Comments (9)
[...] Ruby on Rails Security Blog - The Tainted Edition [...]
"Imagine what will happen when the hash contains a :host key!"
I don't know.
if you have
redirect_to(params.update(:action=>'main'))
you expect that the user will be redirected to the main action, preserving all the parameters.
But if an attacker adds &host=www.attacker.com to the URL, it will redirect the victim to that host.
[...] View post:The Tainted Edition [...]
no doubt, makes always a part of the national capital but it drawn upon foreign countries for the pay and provisions of the jevbuaumpp
Stop me if a saying peanuts but it seems that the ruby tainted mode got an important limitation in the sense that it doesn't keep track of indirect data flows.
Let's say for instance that you've got :
role = params[:role] action)
(Sorry if the syntax isnt exactly good)
Even if this example is quite silly, the variable action should be tainted as there is an indirect data flow from role to action, but it seems that ruby doesn't get it ...
Any idea of how to deal with taht kind of problems ?
Olivier
Sorry, i got a problem with my previous post ...
The example was
role = params[:role]
if role == admin
action = admin
else
action = public
end
redirect_to :action -> action
Olivier,
so you mean this example should throw an error, too, that's the question? Good point, but SafeErb is actually meant for views, so it won't be thrown here.
This one here is logic security. Good thing: you check the role and allow only a limited number of options (admin and public). Less good in my opinion: The user may supply the role without further checking, so adding ?role=admin to the URL allows the user to see something that he's not allowed to.
Idea: Make your own check for taintedness, either in the redirect_to method (may be nasty), or in your controller.
Thanks for your answer Heiko,
In fact, i wish ruby could deal with that kind of indirect data flow and so support "indirect" tainting as it may be of a great importance in proving some kind of security properties on a application. I think it is less a "problem" from rails or SafeErb (which is a really interesting plugin) than from ruby. I wonder if it would be a good idea to report it to ruby developpers (depending on the way they treat the taintness, statically or dynamically, it could require a lot of work) so that they could think on implementing this fonctionnality for future versions ...