3

Suggestion from OWASP Cross Site Scripting Prevention Cheat Sheet:

RULE #5 - URL Escape Before Inserting Untrusted Data into HTML URL Parameter Values

Rule #5 is for when you want to put untrusted data into HTTP GET parameter value.

<a href="http://www.somesite.com?test=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">link</a > 

Once a victim clicks on the encoded url, he will hit that link. But when the server reads (e.g. $_GET['name']; in PHP) the get parameter value, it will read in plain text e.g. if url?name=alert%28123%29, $_GET['name'] will return alert(123). If the server reflects the string in a browser, the XSS will still be executed.

So, what is the point of the encoding suggested in the above quote?

0

3 Answers 3

4

This isn't to prevent possible future attacks after clicking on the link. Instead, it prevents attacks on this page, where the link is inserted (at the same time, it preserves the correctness of the link, because - as you noted - the server will properly URL-decode the values).

If you would not encode the data here, an attacker could perform XSS, eg via ">[XSS].

If - after you click the link - the parameter is again inserted into the page, you need to again take preventative matters matching the context it is inserted into.

2
  • Thanks Tim! Can you elaborate a little bit? I am not sure of ">[XSS]", that you mentioned.
    – Arka
    Commented Mar 23, 2017 at 10:42
  • 1
    @Arka The injection is here: <a href="http://example.com?test=[INJECTION]">. So to perform XSS, you want to break out of the string context via ". You could then add additional attributes which you can use for XSS (onmouseover, etc), or you can simply close the tag context via > and inject your payload (eg <script>alert(1)</script>). Of course, if you URL-encode, neither is possible.
    – tim
    Commented Mar 23, 2017 at 11:54
2

I think, unlike how others have suggested this does not mean prevention against HTML type XSS injection. I will demonstrate by generalised example from an XSS bug bounty I got paid out from a top 1000 website.

Let's say you have a JSONP API (which, from the get-go is a terrible idea).

A Weather API

This JSONP API returns the weather, across origin without using CORS that we query against with the place we want the weather for. JSONP means that you load it as a script, like this:

* http://mywebsite.com/use-weather-api.php *
<script>
function weatherCallback(data) { alert(data.weather) }
</script>
<script src='http://example.com/weather-api?place=$_GET["place"]&callback=weatherCallback'></script>

The contents of this script will be generated based on the inputs, using the callback to pass the response to the parent context, for example if $_GET["place"] is "london":

weatherCallback({"weather": "rainy"})

The function weatherCallback is then called with the JSON payload from the API, saying the weather is rainy.

Counterpoint: HTML Injection

Let's assume we do no sanitisation or contextual sanitisation on our inputs. Now, the simplest and most obvious XSS we can do is send a place that uses HTML injection to get XSS.

* http://mywebsite.com/use-weather-api.php?place='><script>alert('xss!')</script> *
<script>
function weatherCallback(data) { alert(data.weather) }
</script>
<script src='http://example.com/weather-api?place='><script>alert('xss!')</script>&callback=weatherCallback'></script>

See here that by naïvely adding the parameter into our HTML, on the last line we allow the attacker to add extra HTML that generates the extra script tags which, in turn allow them to add extra code to control our webpage, aka XSS. This is an extended version of what others have provided as an example above.

URL Encoding Doesn't Fix This

However, in this example case we should solve this not by using URL Encoding, but by using HTML encoding. If we replace $_GET["place"] with urlencode($_GET["place"]) then some characters may not be escaped, most notably ' (because it's not a URL entity), meaning we can still get XSS:

* http://mywebsite.com/use-weather-api.php?place='onload='alert(1) *
<script>
function weatherCallback(data) { alert(data.weather) }
</script>
<script src='http://example.com/weather-api?place='onload='alert(1)'callback=weatherCallback'></script>

In this case, we reach XSS by injecting a separate parameter to our <script> tag that sets a malicious onLoad event. We can do this because key HTML special characters are not escaped by URL encoding.

Query Parameter Injection

Let's assume we have perfect HTML sanitisation now. The guarantee the HTML sanitiser will give us is that it prevents things we add escaping the HTML entities they're placed in, causing our previous attacks to fail. However we have one last trick up our sleeve, injecting into the URL context:

* http://mywebsite.com/use-weather-api.php?place=london%26callback=alert# *
<script>
function weatherCallback(data) { alert(data.weather) }
</script>
<script src='http://example.com/weather-api?place=london&amp;callback=alert#callback=weatherCallback'></script>

In this case, in spite of our perfect HTML sanitisation and due to the lack of contextual URL sanitisation, we've allowed extra query parameters to be injected into our request URI. Note that the & is encoded to &amp, but will be decoded to & by the time it reaches the browser's URL parser.

Because the 'correct' callback parameter has been 'edited out' with #, and our malicious one injected, the generated script will be:

alert({"weather": "rainy"})

Allowing us to call any function as the webpage (i.e. XSS). We can go further and, in many cases write any javascript we want here, not just function calls, e.g.:

 alert('hello!');({"weather": "rainy"})

Where our query parameters are place=london&amp;callback=alert('hello!')#callback=weatherCallback.

This is a case where it is important to urlencode and then HTML encode.

So, what can I do?

Don't do this manually. It's not the MySpace days anymore. Use a templating system that provides contextual sanitization. This means it understands where you're putting your inputs in the HTML, and for the most part automatically uses the right sequence of escapes to ensure that malicious code injection doesn't happen -- today we talked about URL syntax in HTML syntax, but sometimes you have a URL inside a <script> tag inside HTML, in which case you need URL escaping, then javascript escaping, then HTML escaping. It's maddening!

Most good templating systems do it these days. PHP's default one doesn't, but there are other options you can use. Do make sure the option you pick is XSS safe.

1
  • 1
    Please watch the language and keep it SFW
    – schroeder
    Commented Mar 13, 2018 at 14:22
0

The point of encoding is not security related; it's just to make sure the link and html is valid and well-formed. Certain chars cannot be used in queryStrings, including the chars typically needed to form malicious JS commands.

Browsers attempt to interpret some mal-formed (under-escaped; like gets inlined with %22 etc) urls, so that example might work w/o escaping, but more complex scripts would fall flat without the "percent encoding" we use for URI search params.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .