Is it safe to rely on Content-Type: text/plain to mitigate malicious javascript execution in response?

12.6k views Asked by At

we have a web application that returns


HTTP/1.1 400 Bad Request
...
Content-Type: text/plain;charset=UTF-8
Content-Length: 57
Date: Tue, 14 Apr 2015 19:24:54 GMT
Connection: close

Invalid project area item id 

<script>alert(1086)</script>

It is my understanding that relying on Content-Type: text/plain;charset=UTF-8 as the defense to prevent javascript from executing is NOT enough. Rather the output should be encoded, and the input should likely be input validated and garbage thrown out.

What I'm looking for is some crystal clear and official answer on the right approach for handling responses that have javascript where the Content-Type has been set to text/plain.

Anyone have a link (or answer) to an official example of this scenario and right way to handle it? Or is Content-Type: text/plain;charset=UTF-8 all that is needed?

3

There are 3 answers

2
Frugal Guy On

Drakes answer above troubled me, so I created a simple proof of concept to see if I was correct. I am. Even with Content-Type: text/plain;charset=UTF-8 an application can be compromised with a simple XSS attack.

The reason, as I tried to explain below the first time, is that it is the data handling that is important, and the eventual destination and rendering context of the data. The transport is not as important. I created a simple servlet that returns a response just like that of the OP, including the Content-Type header. Here's that response:

HTTP/1.1 400 Bad Request
Server: Apache-Coyote/1.1
Cache-Control: no-cache
Content-Type: text/plain;charset=UTF-8
Content-Length: 73
Date: Thu, 18 Jun 2015 22:49:01 GMT
Connection: close


Invalid project area item id <iframe src=javascript:alert(1)></iframe> 

And here's an image of the result. Note that the attack payload was executed: https://flic.kr/p/uRnSgo

Again, the reason is very simple. The data are not being rendered in an AJAX request, but in a consuming web application page, which is an HTML page.

Anyhow, I hope that removes any doubt about being vulnerable in some cases... especially when the response is to an AJAX request that will be rendered in a consuming page.

----- Below is my original response. -----

A 400 response with an error message smells of a REST API response to me.

If this was a REST request (X-Requested-With: XMLHttpRequest or Accept: application/json in the request headers), then you face a serious issue. Although this response is not affected, the data will likely be picked up and displayed in the end user's UI by the consuming page. Since it is not properly encoded, it will execute. It's not always this response you have to worry about, but the eventual disposition of the attack payload. Assuming this is the response to an XMLHttpRequest or REST call, this is a serious bug.

You could test with an attack payload of <iframe src=javascript:alert(1)></iframe> and I bet you'll see that pop in the consuming application.

I would suggest: Invalid project area item id and leave out the invalid value. Cheapest solution.

So, no you cannot in general rely on Content-Type to save you. The data may be displayed in another context where it will execute.

Always validate input and properly handle output, which may include encoding in some format or other, depending on the context in which it will be rendered. Anybody telling you otherwise is trying to get out of some required work. :-)

1
Drakes On

Here are two scenarios.

Top-level XSS

In the case that an attacker can manipulate a top-level URL into responding to a malformed request like

HTTP/1.1 400 Bad Request
...
<script>alert(document.cookie)</script>

then setting Content-Type: text/plain mitigates an XSS attack (see detailed answer below).

AJAX XSS

If, however, the attacker can manipulate some AJAX function in the target web page and trick it into doing something like $("#result").html(xss_request_result) then that effectively loads text into a web context and it will be parsed by the browser (including JS), and all bets are off.

It is wise to entity-encode or tag-strip the response of such error messages.


Regarding the first scenario, according to w3.org,

The primary subtype of text is "plain". This indicates plain (unformatted) text. The default Content-Type for Internet mail, "text/plain; charset=us-ascii", describes existing Internet practice, that is, it is the type of body defined by RFC 822.

This means text/plain should not be interpreted and not processed. However, Google (Updated March 30, 2011) states,

If Content-Type matches one of generic values, such as application/octet-stream, application/unknown, or even text/plain, many browsers treat this as a permission to second-guess the value based on the aforementioned signals, and try to come up with something more specific. The rationale for this step is that some badly configured web servers fall back to these types on all returned content.

According to a survey of browser sniffing titled Is HTML sniffed on text/plain documents (with or without file extension in URL)?, the results are:

  • Internet Explorer 9+: No
  • FireFox: No
  • Opera: No
  • Chrome: No
  • Android: No
  • Safari: Yes

So yes, setting the content type to text/plain with explicit charset set will mitigate XSS attacks, but from the above survey Safari may be a hold out.

Test your browser for this vulnerability. Go to a more generous online PHP fiddle site (e.g. http://phptester.net/) and execute the following. You should not get a popup.

<?php
header("Content-Type: text/plain");
echo "<script>alert(1)</script>";

More reading on Content-sniffing XSS attacks (PDF)

0
Wood On

https://www.rfc-editor.org/rfc/rfc2046 'text/plain' should not process instruction kind, and is seen simply as a linear sequence of characters.