XSS in Modern Web Apps

Recently, as I was scrolling Twitter, I came across this tweet:

The page he links to demonstrates a very simple vulnerability that is frequently overlooked. This is the source code from his sample modified for brevity:

<a href="<?php
    echo htmlspecialchars($_POST['link'], ENT_QUOTES);

At first glance, most will dismiss this as secure due to the escaping performed. However, HTML links are able to execute JavaScript with the javascript: prefix. This is perfectly fine text and will not get escaped by htmlspecialchars. An example of attack input in the above code will be when link is set to javascript:alert(1).

This is frequently overlooked because it is uncommon for links to include user input without any preceeding path like /files/ which prevent the javascript: prefix from working. If your user input is a relative link, you could prevent such XSS by prefixing with ./. Otherwise for absolute links, it should have been normalised beforehand, enforcing a http(s) prefix.

Modern templating engines perform some form of XSS escaping. Some (like Go's html/template) rely on context to encode user input which prevent this attack, while other engines (like Handlebars and Vue) trust that the developer is aware of all the possible XSS vectors. It's always important to understand what extent your tools go to help you in security, even if it's a widely used library, some edge cases may not be covered by the library. I think it's also good if every framework and library have a document describing potential security issues like this pending pull request on the Vue docs.