The XSS prevention support in recent versions Ruby on Rails allows some string operations which, when combined with user supplied data, may leave an ‘unsafe string’ incorrectly considered safe. It is unlikely that applications call these methods, however we are shipping new versions today which prevent their use to ensure they’re not called unintentionally.
How the XSS Prevention Works
When strings are rendered to the client, if the string is not marked as “html safe”, the string will be automatically escaped and marked as “html safe”. Some helper methods automatically return strings already marked as safe.
<%= link_to('hello world', @user) %>
link_to method will return a string marked as html safe. Since
link_to returns an “html safe” string (also known as a safe buffer), the text will be output directly, meaning the user sees a link tag rather than escaped HTML.
Safe buffers are allowed to be mutated in place via methods like
sub!. These methods can add unsafe strings to a safe buffer, and the safe buffer will continue to be marked safe.
An example problem would be something like this:
<%= link_to('hello world', @user).sub!(/hello/, params[:xss]) %>
In the above example, an untrusted string (
params[:xss]) is added to the safe buffer returned by
link_to, and the untrusted content is successfully sent to the client without being escaped. To prevent this from happening
sub! and other similar methods will now raise an exception when they are called on a safe buffer.
In addition to the in-place versions, some of the versions of these methods which return a copy of the string will incorrectly mark strings as safe. For example:
<%= link_to('hello world', @user).sub(/hello/, params[:xss]) %>
The new versions will now ensure that all strings returned by these methods on safe buffers are marked unsafe.
This problem affects all versions of rails: 3.1.0.rc1, 3.0.7, and 2.3.11.
Any methods that mutate the safe buffer without escaping input will now raise an exception.
If you need to modify a safe buffer, cast it to a Ruby string first by calling the
<%= link_to('hello world', @user).to_str.sub!(/hello/, params[:xss]) %>
This problem is fixed in Rails 3.1.0.rc2, 3.0.8, and 2.3.12 (with
rails_xss). If for some reason you cannot upgrade your Rails installation, please apply these patches:
Thanks to Bruno Michel of LinuxFr.org and Brett Valantine who each independently reported the issue to us.