11

When a user attempts to access any URL without authentication (including a non-existent URL), my web service returns an HTTP 401 response. This response is JSON encoded, and the body of this response contains the path requested by the user, presumably for diagnostic purposes. The response also includes the www-authenticate header. If a user visits this URL in the browser, this triggers their browser to ask for credentials.

However, automated scanning tools report that this is an XSS vulnerability. This appears to be because the requested path is included, and if the requested path also happens to be valid JavaScript, this path is returned along with the response. See cURL output below:

curl -i  'https://myservice.example.com/<script>alert(1)</script>'
HTTP/2 401
server: nginx
date: Tue, 19 May 2020 15:02:20 GMT
content-type: application/json;charset=UTF-8
content-length: 167
strict-transport-security: max-age=31536000 ; includeSubDomains
www-authenticate: Basic realm="Spring"

{"timestamp":1589900540080,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/<script>alert(1)</script>"}%

I think that this is a false positive, as the API response is JSON encoded, as per this question: Reflected XSS via JSON executed with Burp, but how to do it in realistic conditions?

Am I correct?

Josh P
  • 213
  • 2
  • 7
  • that's *almost* valid JSON, but that isn't valid json, did your output get truncated or something? missing a few characters at the end... – user1067003 May 21 '20 at 13:16
  • 1
    Just to note, your URI isn't valid, `<` and `>` are not valid in path segments [rfc3986](https://tools.ietf.org/html/rfc3986#section-3.3). In chrome, `encodeURI` gives `"https://myservice.example.com/%3Cscript%3Ealert(1)%3C/script%3E". The `/` creates a new path segment with `script>` – Jason Goemaat May 22 '20 at 15:50

4 Answers4

11

This isn't vulnerable to XSS since the Content-Type is set to application/json and thus no Javascript will be executed by all major modern browsers. If you do some fancy Javascript stuff with the JSON response, it could become a DOM XSS (but this doesn't seem to be the case here from what you shared).

A more detailed answer can be found here.

layton
  • 126
  • 1
  • 5
  • 2
    For auditors it's typically easier to complain about such a reflection without finding a real attack vector, and IMHO it is lowest risk and least work of convincing as a developer to avoid it completely. So either don't reflect it or to sanitize the string somewhat. In addition this is a reason why x-nosniff should be used as a header. – eckes May 21 '20 at 01:12
11

An XSS vulnerability usually consists of two components: A backend which reflects user-provided strings without filtering them and a frontend which puts that input into a HTML document without filtering it.

So you don't just need to look at what the server does, you also need to look at the application which consumes that JSON response.

Is there a possibility that it will inject the path into a HTML document as-is without escaping any HTML tags in it first? Or that it processes that JSON document not with JSON.parse but with eval which would allow the server to pass exectuable code to it?

If that's the case, then the first priority would be to fix the consuming application. But as part of a defense-in-depth strategy you would also be well-advised from preventing the server from passing strings which contain valid HTML to the client. A good approach is to encode < and > as &lt; and &gt;.

Also make sure that the JSON response doesn't get malformed if the user-provided string contains a ". That might not just be annoying, it could also be abused in some scenarios to confuse the tools which consume the response.

Philipp
  • 48,867
  • 8
  • 127
  • 157
  • Support this comment, because reflection happens in fact, and therefore the following processing at the front end may lead to exploitation, with some chance though. Therefore the finding is a good signal to look at the following execution chain. – Alexander Fadeev May 20 '20 at 10:55
  • 1
    Can the "path" be escaped using a URL encoding? – bhorkarg May 20 '20 at 11:36
  • 27
    "A good approach is to encode < and > as < and >." - Sounds like a bad idea. It makes the software unnecessary complicated, which can introduce bugs. In fact, you already made a bug by letting out `&` to `&`. This way `https://myservice.example.com/<` and `https://myservice.example.com/<` result in the same value of path in the JSON, which is not desired. The same flawed logic was used in the now removed [magic quotes](https://www.php.net/manual/en/security.magicquotes.php) feature of PHP. – Paul May 20 '20 at 22:58
  • 2
    -1 for recommending escape of arrow brackets. I've seen way too many misformated messages (ie. some characters replaced by `&something;`) in apps that use the JSON API. The correct way is to put the text into HTML by using `new Text(url)`. There is no need for any escaping as long as you use the text like that. – Tomáš Zato - Reinstate Monica May 22 '20 at 13:16
  • "A good approach is to encode..." <- **NO!** The only approach is to to match the interface contract. You do not HTML encode content unless it's HTML. – R.. GitHub STOP HELPING ICE May 22 '20 at 17:15
5

This looks like a finding that many automated scanners will find: they put something in the URL and see it reflected back in the response body.

In your case, this is reported back in valid JSON and therefor this is not an issue. While not clean as a whistle, you'll be hard pressed to find a browser or API consumer that fails on the text in there. For all you know, that dangerous looking HTML could be valid and it's up to the client to deal with that well.

Not so much in a text/html context, there's a case for this in that scenario.

JK.
  • 51
  • 1
3

As it is right now, no. All that has happened is a client placed a bespoke string in a JSON response. For XSS to occur, a bespoke string must be written to an unsafe resource (an HTML file).

If you have a webpage which for whatever reason prints the path of this request (maybe a request logging page), you could have something nasty. For example, this code is vulnerable:

.
.
.
output.Append($"<tr><td>{request.path}</td></tr>\n");
.
.
return Ok(output.ToString());

Now this is an unrealistic example, because typically if you're using C# like in this example, you do not generate markup like this, you would use Razor, or generate it on the front end with AJAX. I chose this example because it's concise and more likely to be understood if you yourself are not a .NET programmer.

XSS and most injections can be prevented in two ways. You can escape (aka sanitize) every string. For you that means replacing < and > with &lt; and &gt;. Or you can make sure that unsafe strings are never treated as code (or in this case, markup). Many front ends, such as React go for the second option.

If you use React and have JSX like this:

.
.
.
return <h1>{foo}</h1>

No matter the contents of the variable foo, it will never be treated as markup/code unless you do this:

.
.
.
return <h1 dangerouslySetInnerHTML={{_html : foo}}/>

You'll have to choose one option, it's probably easier to sanitize the string unless you're already using React or something similar. It is easier to sanitize the string in the API because that way you know everything that uses it is safe, however, that may not necessarily be the most appropriate stylistically: you may feel that the sanitization of strings is not an API issue but an issue for whatever uses the API to solve.

Ben
  • 131
  • 3
  • 1
    Second option is the way to go, really. If you pass data outside your system, you're NOT guaranteed that your < won't get decoded back to < again somewhere and inserted in HTML that way. Actually some exploit RELY on this to pass through dumb filters. And if you don't pass data out and it is always inside your system, better do the right thing and always treat it as text. It is simplier, more straightforward and thus less error-prone. – Oleg V. Volkov May 22 '20 at 08:35