-1

An attacker used the HTTP_REFERER variable to inject Javascript by sending the following in the Header:

Referer:
javascript:alert(document.cookie)//a

This was present in $_SERVER['HTTP_REFERER'] and was evaluated in a "Back" button.

Besides the sanitation that I'll do in the back button, I want to sanitize $_SERVER variables in PHP in order to prevent future attacks.

Because I'm expecting a URL value for 'HTTP_REFERER' I tried this:

$_SERVER['HTTP_REFERER'] = filter_var($_SERVER['HTTP_REFERER'], FILTER_SANITIZE_STRING);

But the value wasn't changed. Same thing for this:

 $_SERVER['HTTP_REFERER'] = filter_var($_SERVER['HTTP_REFERER'], FILTER_SANITIZE_URL, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);

And this:

$_SERVER['HTTP_REFERER'] = htmlspecialchars($_SERVER['HTTP_REFERER'])

What's the best way to sanitize the following: 'HTTP_REFERER', 'PHP_SELF', 'REQUEST_URI' ?

1
  • What output were you expecting that you didn't get?
    – schroeder
    Apr 8 at 14:09

1 Answer 1

3

This approach doesn't make sense. Cross-site scripting is an output problem, not an input problem. The vulnerability consists of inserting a string (which currently happens to be the referrer header) into an HTML or JavaScript context without validation or escaping. This part of the code is what you need to locate and fix.

Changing the referrer header with all kinds of “sanitizers” is conceptually wrong for multiple reasons.

  • It doesn't fix the actual XSS vulnerability. For example, if at some point in the future the URL is pulled not from $_SERVER['HTTP_REFERER'] but any other attacker-controlled source or even just a different variable, the vulnerability immediately gets exploitable again.
  • Which validation or escaping method is suitable depends on the exact output context. Is the header inserted into an HTML attribute? The content of an HTML element? Directly into JavaScript code? Those are all fundamentally different contexts which require different approaches. There is no magical one-size-fits-all “sanitizer”.
  • In addition, the same input may be used in different contexts. For example, user input might be both displayed on the page and stored in an SQL database. HTML-escaping SQL input obviously makes no sense whatsoever and can result in yet another vulnerability (namely SQL injection).
  • When you change the value of the referrer header, you no longer have the original data -- depending on the “sanitzer”, you'll either lose the original header permanently, or you have to go through the awkward procedure of un-escaping the header value whenever you want to know what it actually says.

The fix consists of looking at the exact location where $_SERVER['HTTP_REFERER'] is inserted into the page.

  • If it's inserted into an HTML attribute (like href), then you need to change the PHP/HTML to something like href="<?= htmlspecialchars($_SERVER['HTTP_REFERER'], ENT_QUOTES | ENT_HTML5, $the_document_character_encoding) ?>". This will HTML-escape and then output the referrer header. ENT_QUOTES ensures that both single and double quotes are escaped (though this is currently the default), ENT_HTML5 means that the HTML5 standard will be assumed, and $the_document_character_encoding is the character encoding which you use for your HTML document (like UTF-8). The character encoding is important, because the same characters are represented with different byte sequences in different encodings.
  • If the header is inserted into the content of an element, then you can also use htmlspecialchars, but you don't necessarily need to escape quotes.
  • If you insert directly into JavaScript code -- don't. JavaScript is a very complex language compared to HTML, and it's extremely difficult to prepare data for secure insertion. Instead, HTML-escape the data, insert it into a hidden HTML element (like a div) and then extract the data with JavaScript.

In any case, I would strongly recommend you take a systematic approach instead of going through each individual input and output. There are many template engines for PHP (like Twig) which automatically HTML-escape data before inserting it into an HTML context. This isn't always perfect, but it's a good starting point. Additionally, you should consider using Content Security Policy (CSP). This allows you to tell the browser that, for example, all inline JavaScript code should be blocked and reported. Note that CSP isn't a replacement for correct handling of data. But it's extremely valuable to act as a last line of defense, to notify you of vulnerabilities, and to enforce a secure programming style which separates HTML markup from JavaScript code.

You must log in to answer this question.

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