Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #8371 (closed defect: fixed)

Opened 2 years ago

Last modified 1 year ago

to_json cross site scripting security issue (XSS)

Reported by: BCC Assigned to: core
Priority: high Milestone:
Component: ActiveSupport Version: 1.2.3
Severity: major Keywords: XSS, to_json, Cross site Scripting
Cc:

Description

To json is almost only used for injecting object hashes into javascript.

var client = <%= client.to_json %>;

Because to_json does not escape its values, it's easy to construct a Cross Site Scripting exploit. If client has a name attribute, to_json will come up with something like: var client = {attributes: {name: "TEST"}};

If we change the name to say: TEST"}}; alert('XSS!!') ;a={{" we have no problem in the rest of our application, as we use <%= h client.name %>, but when we render our javascript, there is a problem:

var client = {attributes: {name: "TEST"}}; alert('XSS!!');a={{""}};

There is currently no easy way to safely escape to_json as escaping the result will result in a broken hash. The implementation of the current to_json is as such that no difference is made between the value and the key, making an easy fix dificult.

This seems to be somewhat refactored in the trunk, but the problem is still there. I understand that this is not really a to_json problem, but as 99% of the users probably uses it this way, something like a :secure_values option would be nice.

Attachments

json_xss.tar.gz (58.7 kB) - added by BCC on 05/15/07 19:58:39.
TO JSON XSS Exploit proof of concept

Change History

05/15/07 17:00:10 changed by mattwestcott

  • status changed from new to closed.
  • resolution set to worksforme.

"Because to_json does not escape its values"

What leads you to that conclusion? Escaping of strings in JSON happens here: http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/json/encoders/string.rb

Also, I'm unable to recreate an XSS exploit through your example:

x = <<END
TEST"}}; alert('XSS!!') ;a={{"
END
client = {:attributes => {:name => x}}
puts client.to_json

returns

{attributes: {name: "TEST\"}}; alert('XSS!!') ;a={{\"\n"}}

with the embedded quotes escaped, as expected.

Please reopen if you're able to provide a full test case.

05/15/07 19:58:09 changed by BCC

  • status changed from closed to reopened.
  • version changed from edge to 1.2.3.
  • resolution deleted.

My example was to simple. I'll attach a proof of concept. Follow the steps in the README. Tested it with two people on Rails 1.2.3.

05/15/07 19:58:39 changed by BCC

  • attachment json_xss.tar.gz added.

TO JSON XSS Exploit proof of concept

05/15/07 20:00:04 changed by BCC

Howto: Untar Attachment Check config/database.yml Create MYSQL DATABASE json_xss_dev rake db:migrate to create the client table ruby script/server to start the application

Head via your browser to: 0.0.0.0:3000 Head to /clients

Add a few names, visit the xss page (/clientxss) Take a look at the source, you'll see your names in the javascript client variable.

Add a new client by copy-pasting the textarea contents into the the firstname or lastname field

Head to /clients, this should work perfectly Head to /clientxss, this should result in a popup saying XSS

05/15/07 20:12:33 changed by dbussink

Being one of the two people who tested this, I've simplified the input string to the following:

</SCRIPT><SCRIPT>alert('XSS')</SCRIPT>

05/22/07 19:12:46 changed by BCC

The problem lies in the fact that the attributes are merely escaped, which is not enough. PHP (Zend) and Java use HTMLEntities for this, which is probably preferable.

05/25/07 22:41:06 changed by technoweenie

  • status changed from reopened to closed.
  • resolution set to invalid.
var clients = <%=h @clients.to_json %>;

05/25/07 22:57:27 changed by technoweenie

Actually, that doesn't work because javascript doesn't decode the string. Use html comments instead:

<script type="text/javascript">
<!--
ar clients = <%= @clients.to_json %>;
-->
</script>

05/29/07 05:41:33 changed by bgreenlee

Don't you mean:

<script type="text/javascript">
<!--
var clients = <%= h @clients.to_json %>;
-->
</script>

05/29/07 06:32:46 changed by technoweenie

No. You don't want to sanitize it, because the clients string will have encoded tags in the content, and not the actual content.

Oh, did you mean the 'ar clients' part? Heh, I suck at copy/paste obviously.

Also, JS may want the html comment tags commented out.

<script type="text/javascript">
//<!--
var clients = <%= @clients.to_json %>;
//-->
</script>

05/29/07 07:18:16 changed by BCC

  • status changed from closed to reopened.
  • resolution deleted.

Using h to sanitize is off course impossible, as that would mangle the json array, making it useless. The comment 'hack' in javascript is both ugly and not preventing the XSS.

The only real solution is to use htmlentities or escaping for the value fields.

05/29/07 07:35:23 changed by BCC

And please look at the attached proof of concept, the original message is wrong as my example was too simple.

05/29/07 07:41:09 changed by technoweenie

  • status changed from reopened to closed.
  • resolution set to invalid.

I did look at it, and it didn't pop up for me once I added the comments.

05/29/07 07:44:33 changed by BCC

I just added the comment and inserted the full length exploit string in both the firstname and lastname. I gave me an instant popup (Firefox 2.0.0.3). The comment does not help because the exploit string can contain an end comment string.

05/29/07 07:44:50 changed by BCC

  • status changed from closed to reopened.
  • resolution deleted.

Reopen

05/29/07 08:32:30 changed by technoweenie

Ok that makes sense. Browsers suck.

05/29/07 08:44:28 changed by technoweenie

this patch works (edge rails): http://pastie.caboo.se/65550.txt

Using this to test it:

<script type="text/javascript">
var foo = <%= {:a => "</script><script>alert('hello')</script>"}.to_json %>
</script>

05/29/07 09:14:58 changed by technoweenie

  • status changed from reopened to closed.
  • resolution set to fixed.

committed [6893]. if it causes issues, please open a new ticket w/ a failing test case + patch.

05/29/07 09:15:55 changed by BCC

I'll test it tonight, after work :)

05/29/07 09:24:49 changed by technoweenie

  • status changed from closed to reopened.
  • resolution deleted.

If someone wants to throw in a patch for these failures, you'd be my best friend forever. It's late.

http://cruisecontrolrb.thoughtworks.com/builds/RubyOnRails/6893

05/29/07 09:42:17 changed by rick

  • status changed from reopened to closed.
  • resolution set to fixed.

(In [6894]) fix test cases to match new json output. Closes #8371

05/30/07 16:09:12 changed by BCC

Checked with edge (9606). Seems to be fixed.