« Intranet and Admin Security | Main | InvalidAuthenticityToken for in_place_editing? »
Wednesday
Feb132008

The Tainted Edition

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...)
Escape strings using html_escape() or h() against XSS.
 
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
There has been a problem with the to_json method earlier. Solved in Rails 1.2.5.
 
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.
What I don't like about these solutions is that it might be seen as plug-n-play security to install and forget, because it does it all automatically. In my opinion security is a process and you have to reconcile for each and every string what it represents and how to escape it in this context. Of course an automatic escaper can be a great help, but only if you remember to sanitize it differently in an other context.

PrintView Printer Friendly Version

EmailEmail Article to Friend

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.

February 22, 2008 | Unregistered CommenterGregL

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.

February 22, 2008 | Unregistered CommenterHeiko

[...] 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

March 29, 2008 | Unregistered CommenterLillie

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

June 27, 2008 | Unregistered CommenterOlivier

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

June 27, 2008 | Unregistered CommenterOlivier

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.

June 27, 2008 | Unregistered CommenterHeiko

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 ...

June 27, 2008 | Unregistered CommenterOlivier

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>