prototype: Automatically strip security delimiter comments from JSON strings before evaling them. The default delimiter is '/*-secure- ... */' or you can specify your own with the Prototype.JSONFilter regular expression. If you wrap your JSON response bodies in this delimiter on the server side, rogue external sites can't hijack potentially sensitive data via <script> tags. Closes #7910.

This commit is contained in:
Sam Stephenson 2007-04-24 03:31:14 +00:00
parent 643b7bec37
commit fea6bc1a21
5 changed files with 26 additions and 9 deletions

View File

@ -1,5 +1,8 @@
*SVN*
* Automatically strip security delimiter comments from JSON strings before evaling them. The default delimiter is '/*-secure- ... */' or you can specify your own with the Prototype.JSONFilter regular expression. If you wrap your JSON response bodies in this delimiter on the server side, rogue external sites can't hijack potentially sensitive data via <script> tags. Closes #7910. [Tobie Langel]
For more details on potential security problems, see: http://www.fortifysoftware.com/servlet/downloads/public/JavaScript_Hijacking.pdf
* Add extra spacing so Array#toJSON and Hash#toJSON generate YAML-loadable JSON. Closes #7883. [Andrew Dupont]
* Fix Form.request for forms containing an input element with name="action". Closes #8063. [Thomas Fuchs, Mislav Marohnić]

View File

@ -212,13 +212,13 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
evalJSON: function() {
try {
var json = this.getHeader('X-JSON');
return json ? eval('(' + json + ')') : null;
return json ? json.evalJSON() : null;
} catch (e) { return null }
},
evalResponse: function() {
try {
return eval(this.transport.responseText);
return eval((this.transport.responseText || '').unfilterJSON());
} catch (e) {
this.dispatchException(e);
}

7
src/prototype.js vendored
View File

@ -9,6 +9,7 @@ var Prototype = {
WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
},
BrowserFeatures: {
XPath: !!document.evaluate,
ElementExtensions: !!window.HTMLElement,
@ -16,9 +17,11 @@ var Prototype = {
(document.createElement('div').__proto__ !==
document.createElement('form').__proto__)
},
ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
emptyFunction: function() {},
JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
emptyFunction: function() { },
K: function(x) { return x }
}

View File

@ -163,12 +163,17 @@ Object.extend(String.prototype, {
return this.inspect(true);
},
unfilterJSON: function(filter) {
return this.sub(filter || Prototype.JSONFilter, '#{1}');
},
evalJSON: function(sanitize) {
var json = this.unfilterJSON();
try {
if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(this)))
return eval('(' + this + ')');
} catch (e) {}
throw new SyntaxError('Badly formated JSON string: ' + this.inspect());
if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
return eval('(' + json + ')');
} catch (e) { }
throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
},
include: function(pattern) {

View File

@ -430,7 +430,13 @@
attackTarget = "Not scared!";
assertRaise('SyntaxError', function(){dangerous.evalJSON(true)});
assertEqual("Not scared!", attackTarget);
}}
assertEqual('hello world!', ('/*-secure-\n' + valid + '\n*/').evalJSON().test);
var temp = Prototype.JSONFilter;
Prototype.JSONFilter = /^\/\*(.*)\*\/$/; // test custom delimiters.
assertEqual('hello world!', ('/*' + valid + '*/').evalJSON().test);
Prototype.JSONFilter = temp;
}}
}, 'testlog');
// ]]>
</script>