What is an XSS
Cross-site scripting (also known as XSS) is a web security vulnerability that allows an attacker to run malicious code in the victim's browser. This is usually due to the way the web application reuses data manipulated by a user in an insecure manner.
An XSS can be reflected (the target has to click on a link containing the malicious code that will execute on his browser) or stored (the attacker places the malicious code on the web application, for example in a comment field, and the code will execute each time the page is displayed.
Here is an example of vulnerable PHP code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Reflected XSS Example</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/light.css">
<style>
.disclaimer {
color: red;
}
</style>
</head>
<body>
<h1>Reflected XSS Example</h1>
<p class="disclaimer">
THIS IS AN EXAMPLE OF A PAGE VULNERABLE TO REFLECTED XSS.
THE TEXT IS NOT SANITIZED AND CAN BE USED TO INJECT MALICIOUS CODE.
DO NOT USE THIS CODE IN REAL PROJECTS.
</p>
<p>Use the GET parameter text (example: http://localhost/reflected_xss.php?text=test) to insert text in the page. The text will be displayed below.</p>
<div>
<?= $_GET['text']?? "" ?>
</div>
</body>
</html>
The following link can for example display a popup thanks to JavaScript code, proving that the vulnerability is present:
- http://localhost/reflected_xss.php?text=%3Cimg%20src=1%20onerror=alert(1)%3E
Of course, in real life conditions, an attacker will instead try to steal cookies or session tokens or display a fake login form to steal credentials.
In order to fix this vulnerability, the most common and effective solution is to securely encode the data before it is sent to the victim's browser. For example, under PHP, the vulnerable code below can be fixed in the following way:
<?= htmlentities($_GET['text']?? "", ENT_QUOTES) ?>
The formatted text problem
The problem becomes more complicated when you want users to be able to format text, by adding HTML tags, for example, with a visual editor like CKEDITOR.
The first idea is often to create whitelists or blacklists of allowed or forbidden tags, but this requires relying on REGEX or filters that will not always include all cases, as hackers are often very imaginative when it comes to bypassing such filters.
Here is an example of a code containing an editable text field that is included in the page without being properly secured. This example takes the data directly into the page (so it is a reflected XSS) but the principle will be the same if the data is stored and reused later.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Insecure formatted textarea</title>
<script src="https://cdn.ckeditor.com/4.20.2/standard/ckeditor.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/light.css">
<style>
.disclaimer {
color: red;
}
</style>
</head>
<body>
<h1>Exemple of insecure formatted text</h1>
<p class="disclaimer">
THIS IS AN EXAMPLE OF A TEXTAREA WITH INSECURE FORMATTING TEXT LEADING TO XSS.
THE TEXT IS NOT SANITIZED AND CAN BE USED TO INJECT MALICIOUS CODE.
DO NOT USE THIS CODE IN REAL PROJECTS.
</p>
<p>Here is a text area with formatting text. You can write some text and submit it. The text will be displayed below the form.</p>
<form action="textarea_insecure.php" method="post">
<label for="text">Insecure formatted text</label>
<textarea id="text" name="text" rows="10" cols="50"></textarea>
<p>
<input type="submit" value="Submit">
</p>
</form>
<script>
CKEDITOR.replace( 'text' );
</script>
<div>
<?= $_POST['text']?? "" ?>
</div>
</body>
</html>
On this application, it is possible to intercept traffic to manipulate data and add malicious code. With CKEDITOR, it is also possible to simply click on the "source" button to edit the HTML directly. By simply entering JavaScript code, it will be interpreted when it is reused.
The iframe sandbox
To fix this problem, it is not possible to just encode the data, as this would remove all possibility of text formatting. This is where the "sandbox" parameter of the HTML "iframe" tags will make life easier.
Indeed, this parameter is designed to automatically clean what is in the iframe from anything that could be dangerous, while keeping the HTML tags harmless.
Here's how you can use this iframe using the example above:
<iframe src="data:text/html;base64,<?= base64_encode($_POST['text']?? "") ?>" sandbox=""></iframe>
If the data is reused in a raw way as above, it is important to use a BASE64 encoding to ensure that it will not be possible to inject code in the src parameter of the tag. Indeed, without this encoding, it would have been possible to add a double quote (") and then close the tag with (>) to then inject directly here.
It is however possible to pass a simple URL without the "src" parameter.
The data entered by the user is then displayed in a secure way while keeping the formatting.
By default, the sandbox setting is very strict, but it can take some settings to change its behavior (source):
- allow-downloads: allows downloads to take place after a user action.
- allow-forms: the built-in navigation context can send forms. If this keyword is not used, this operation is not allowed.
- allow-modals: the navigation context can open modal windows.
- allow-orientation-lock: the navigation context can disable the screen orientation lock.
- allow-pointer-lock: the navigation context can use the Pointer Lock API.
- allow-popups: the navigation context can trigger popups (for example with window.open, target="_blank", showModalDialog). If this keyword is not used, the functionality will fail without error message.
- allow-popups-to-escape-sandbox: This keyword allows a sandboxed document to open new windows without having to force sandboxing for those windows. This will allow, for example, a third party ad to be sandboxed correctly without applying the same restrictions to the original page.
- allow-presentation: this keyword allows an iframe to start a presentation session (en-US).
- allow-same-origin: this keyword allows the isolated document to support same-origin policy tests by disabling the replacement of the iframe origin with a single origin.
- allow-scripts: the browser context can execute scripts (but cannot create popups). If this keyword is not used, this operation is not allowed.
- allow-storage-access-by-user-activation Experimental: this keyword allows the embedded browsing context to request access to the storage features of the parent document (e.g. its cookies, web storage) through the Storage Access API (en-US).
- allow-top-navigation: the navigation context can load content from the top-level navigation context. If this keyword is missing, this operation is not allowed.
- allow-top-navigation-by-user-activation: the navigation context can load content from the top-level context only if the action comes from the user. If this keyword is missing, this operation is not allowed.
The source code of the different pages used in this article can be found here : https://github.com/Secureaks/xss-fix-iframe-sandbox