In this article, I’ll delve into the art of recognizing and defending against XSS attacks, drawing from my own experiences.
At present, a plethora of payloads exist for launching XSS attacks, and various frameworks implement countermeasures. Nonetheless, let’s focus on fundamental concepts surrounding XSS attack methods.
Recognizing XSS Vulnerabilities in Returned User Input
When user input is accurately returned to the client and displayed, it signals that the server is not pre-processing data before storage or presentation. Consequently, user input can disrupt HTML structures and insert malicious code.
Consider a common scenario in search functionality where developers aim to retain the user’s search string for convenience:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<h1>Blog</h1>
<p>Search for content</p>
<?php
$search = "";
if (isset($_GET['search'])) {
$search = $_GET['search'];
}
?>
<form action="/blog/index.php" method="get">
<input type="text" name='search' value="<?php echo $search; ?>" />
<input type="submit" value="submit">
</form>
</body>
Inspecting the web or reviewing responses from a proxy might reveal vulnerabilities if special characters aren’t converted to HTML entities.
Chỉ cần dùng ">
để thoát khỏi tag input
và chèn script tuỳ ý. Using “> to break out of the input tag and inject arbitrary scripts
1
a"><script>alert(document.cookie)</script><
Resulting in the user’s HTML display being:
Leveraging JavaScript and Data Protocol
Attributes such as href, src, srcdoc, and events (onclick, onerror, etc.) can execute data from external sources or inline scripts using the javascript: and data: protocols. For instance:
1
<source onerror="javascript:alert(1)">
1
2
3
data:text/html,<script>alert(0)</script>
data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>
Proactive Programming Measures
Best practices for preventing XSS attacks by encoding user input, avoiding pattern replacement pitfalls, and implementing effective whitelist and blacklist strategies.
During numerous tests, developers often attempt to replace XSS attack patterns like <script>
, <.*>
, alert
, eval
, etc. However, there are numerous ways to bypass these replace mechanisms. Upgrading the code might look like this:
1
2
3
4
5
if (isset($_GET['search'])) {
$search = strtolower($_GET['search']);
$search = str_replace('<script>', '', $search);
$search = str_replace('</script>', '', $search);
}
However, using the payload <scr<script>ipt>
can bypass this defense. To prevent XSS, leverage the following methods:
Encoding Data: This is the most effective way for user-input data. Characters like <>"=
should be encoded when processing with HTML or JavaScript. For instance, in HTML, characters can be replaced with HTML entities:
<
can be replaced with<
>
can be replaced with>
On the server-side, depending on the backend language or framework in use, functions such as htmlspecialchars
and htmlentities
may be used.
In JavaScript, there isn’t a built-in API for this, but here’s an example function:
1
2
3
4
5
function htmlEncode(str){
return String(str).replace(/[^\w. ]/gi, function(c){
return '&#'+c.charCodeAt(0)+';';
});
}
Frameworks like Vue.js, React.js, and Angular.js have built-in mechanisms to prevent XSS, except when using functions that allow writing HTML through strings.
In the example above, just adjust it as follows:
1
2
3
if (isset($_GET['search'])) {
$search = htmlentities($_GET['search']);
}
Hackers cannot escape the value attribute because the “ character has been encoded.
Whitelist and blacklist: Ngăn chặn các từ khoá trùng với các protocol dễ bị tấn công như javascript:
và data:
Deploying CSP for Enhanced Protection
Content Security Policy (CSP) stands as an additional security layer to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS). CSP is inserted into the response header:
1
2
<!-- Example of CSP Header -->
Content-Security-Policy: <policy-directive>; <policy-directive>
To prevent XSS, set the script-src
policy, for example:
1
2
<!-- Example of CSP Script Policy -->
script-src 'self' https://lithonn.local;
With this policy, the website is allowed to load scripts from itself and from https://lithonn.local. Hackers cannot inject scripts from other domains.
1
2
3
4
5
<!-- allowed by 'self' -->
<script src="/js/main.js"></script>
<!-- allowed by https://lithonn.local -->
<script src="https://lithonn.local/main.js"></script>
The following JS won’t load due to CSP since it is not within the allowed domain:
1
2
<!-- Block -->
<script src="https://attacker.com/evil.js"></script>
The following inline JS is also blocked by default:
1
2
3
4
<!-- Block -->
<script>
runInlineScript();
</script>
In addition, there are CSP directives like script-src-elem and script-src-attr tailored to apply exclusively to script blocks or target specific events such as onclick and onerror. However, there are still ways to bypass CSP. Refer to this resource. Therefore, validating user content is still crucial.
Conclusion
In the ever-evolving landscape of web security, safeguarding against Cross-Site Scripting (XSS) attacks is paramount. By understanding the intricacies of user input handling, recognizing vulnerabilities, and implementing robust defensive strategies, we fortify our digital spaces against malicious exploits.
From meticulous encoding of user input to the judicious application of Content Security Policy (CSP), the journey to a secure web environment demands continuous vigilance. The insights shared in this exploration serve as a foundation for developers, administrators, and security enthusiasts alike.
As we navigate the dynamic realm of cybersecurity, remember that proactive measures, diligent coding practices, and the adoption of security best practices contribute to a resilient defense against XSS threats.
Stay informed, stay vigilant, and let’s collectively strive to build a more secure digital future. Thank you for accompanying us on this journey into the realm of XSS defense.