Everyone knows the invaluable XSS cheat sheet maintained by “RSnake”. It is all about breaking things and features all the scenarios that can result in XSS. To complement his efforts, there is an excellent XSS prevention cheat sheet created by “Jeff Williams” (Founder and CEO, Aspect Security). As far as I have seen, this wiki page provides the most comprehensive information on protecting yourself from XSS on the internet. It advises using the OWASP ESAPI api to mitigate any XSS arising from untrusted user input.
I was evaluating this ESAPI api and the recommendations given on the wiki to see if there are any potential flaws. Any weakness impacts a very large number of users since many developers are using it to strengthen their web applications throughout the world. This is my way of contributing back to the community, but can never match the immense efforts put by Jeff and other OWASP team members in developing this library.
I want to give you a little bit of background before diving into the real vulnerability. The XSS prevention cheat sheet classifies XSS protections by dividing them into broadly four buckets – HTML Body injection, HTML Attribute injection, Javascript injection and CSS injection. For each of these four buckets, there is an ESAPI function reference you can use for output escaping/encoding.
If you allow any untrusted user input into javascript functions document.write() OR eval(), it can still execute the XSS even after you do the scrubbing using the ESAPI encodeForJavaScript() function. The reason being that hex escaped chars are converted back into normal chars at the time of execution of these functions.
Here is the proof of concept jsp code:
<%@page import="org.owasp.esapi.*"%> <%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>ESAPI XSS Protection Bypass</title> </head> <body> <h1>ESAPI XSS Protection Bypass</h1> <p id="tb1"/><br> <p id="tb2"/> <script> //in real scenario, these three strings come from request.getParameter or user input <% String vulstr1 = "-1';alert(0);"; String vulstr2 = "<img src=x onerror=alert(1)>"; String vulstr3 = "0,x setter=alert,x=2"; %> // you can safely use it in places like this // Ex. vulstr1 is completely encapsulated in a and alert(0) not executed. var a='<%= ESAPI.encoder().encodeForJavaScript(vulstr1) %>'; alert(a); // However, you can bypass protection in places like these // Ex. vulstr2 gets written to html and alert(1) executes document.write("<%= ESAPI.encoder().encodeForJavaScript(vulstr2) %>"); // Ex. part of vulstr3 get assigned to u, rest alert(2) executes eval("u=<%= ESAPI.encoder().encodeForJavaScript(vulstr3) %>"); </script> </body> </html>
Much thanks to Jeremiah Grossman and Jeff Williams for taking the time to review my idea and providing their insights. Jeremiah told me that he has seen such injections from time to time at WhiteHat and these do exist in the wild.
Jeff confirmed that some documentation changes will fix this. I agree that no esapi code change is required, because function themselves are not insecure.
But, if you are currently using esapi functions inside your javascript code, it is important that you re-review your javascript code and the places where your make calls to esapi functions.
If you use the esapi function encodeForJavaScript() inside document.write, it is advised that you change them with other appropriate esapi functions depending on the context where the data is ultimately landing. For example, if you have document.write(“<script>alert(‘XSS’)</script>”), you know the data is landing in html body context, so it is appropriate to use encodeForHTML() wrapper. Using user input inside eval is less common, but more disastrous. The reason for this is you can still begin another command context using , and (space) char and it won’t be encoded by function encodeForHTML(). So, it is better to avoid putting user input inside eval.
Any more suggestions or discussion on fixes is highly welcome.
Tags: esapi, Javascript, owasp, XSS, xss prevention cheatsheet
Thanks for this research! Developers – please be very careful when putting user input into a JavaScript context (script block or event handler). What you’re doing is very dangerous and difficult to get right, even with strong JavaScript escaping. You’re basically asking the user for *code* and then running it. I do see this very frequently in applications, which is one reason we’re going to be fighting XSS for a very long time.
I’m trying to resolve the statement “I agree that no esapi code change is required, because function themselves are not insecure” with the title of the post “bypassing owasp esapi xss protections”
Besides that, good post. Example #2 shows HTML-within-JS-within-HTML; can anyone explain to me how a low-paid programmer (probably outsourced?) is ever going to care enough about his job to do this right?
I think it’s good idea to use json_encode/to_json functions for this, like:
var a = <%= user_data.to_json %>; // Note absence of quotes
Also you don’t have to deal with data types, because json encoder checks for user_data type and yields string, integers, floats, etc. accordingly. Do you see any flaws in this approach?
BTW it is worth mentioning that those who use mixed HTML/XHTML and famous //<![CDATA[ //]]> snippets should
also ‘escape-for-cdata’, that is replace "]]>" with "]]>]]><![CDATA[" to prevent ]]> injection. Of course only when document is served as XHTML, not HTML. I use some guerilla patching of "javascript_tag" method in my Rails applications to make it automatic.
Since commenting system on this blog does not work properly (isn’t now the time to use proper escaping instead of eating „dangerous” words? ;^)), i put my original comment here: http://mcv.mulabs.org/comment-0.txt
[...] here to read the rest: Bypassing OWASP ESAPI XSS Protection inside Javascript … SHARETHIS.addEntry({ title: "Bypassing OWASP ESAPI XSS Protection inside Javascript …", [...]
hi mcv,
i corrected your original comments, since some of it inside < and > was getting stripped. i will see what i can do to fix my commenting engine.
Hi Arshan,
Thanks for your feedback. I can understand your confusion. What i meant to convey was esapi protection function encodeForJavaScript does not protect against all cases of javascript injection. The title essentially says that you can bypass the xss protection if you use this function for scrubbing. But, this is not a vulnerability of this function and no code fix can prevent this. It is required that people either use the correct function or don’t put insecure code there at all. If you need more clarity, lets discuss this over email.
For the low-paid programmer, i don’t think he or she will even know what xss is, no point in talking about security anyways there. My main concern was for people who know XSS and have sometime seen and used the xss prevention cheat sheet. Once Jeff fixes the defense recommendation on that page, those people will have a better idea of use the esapi correctly and securely.
mcv, you asked a question whether it is a good idea to use json_encode or not. i tried on my latest php 5.3 install and don’t think it will provide any protection.
Json_encode uses unicode escapes which is bypassable inside document.write and eval, just like hex escapes for encodeForJavaScript.
“,
e.x. if i use json_encode($str,JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP) inside document.write where malicious $str=”
Then i get this – document.write(“\u003Cimg src=x:x onerror=alert(1)\u003E”) on my client and XSS still executes.
Hi again,
Of course json_encode is meant to create code for JS, it does not escape for HTML, so you’re right — document.write attack will work. I meant only to use json_encode instead of threating everything like strings and putting it inside “”.
And to prevent this attack additional HTML escaping is needed there, or XML->JSON->again-XML escaping if document is XML, or XML->JSON->CDATA escaping if script is enclosed into CDATA section. Holy God… But again, document.write does not work in XHTML (but one day one browser might support it out of the blue). And all this is the reason why I prefer to use javascript_tag method which automatically decides what escaping should be done for js code for current document type.
As for eval, since it’s embedding JS code inside JS string, I’d use double json_encode and concat two JS strings, NOT embed one into another:
eval(“u=” + );
Attack should not work then. If you add + “; alert(u)” at the end you will notice that whole “0,x setter=alert,x=2″ has been assigned to variable u.
The truth is that programmer should be aware of what language in what another language is he embedding.
Regards.
of course i actually understood what you were saying, i was just calling you out on having a sensationalist title