What is Cross-Site Scripting?
Cross-Site Scripting (XSS) attacks are a form of injection attack, where malicious scripts are injected into trusted web applications.
XSS is usually inserted through a website using a hyperlink or a web form. The inserted code can be used via any client-side language such as JavaScript, PHP, HTML.
A simple XSS attack will occur on a vulnerable website that accepts user input via a GET parameter and displays the data on the website.
Letâs take a look at the URL example below:
https://example.com/profile/?u=john
This URL points to a website that reads the value for username from the u
parameter in the URL and displays it on the webpage.Â
An attacker may inject malicious code into the website by setting the value for u to the following:
https://example.com/profile/?u=alert("Your website is compromised!")
The injected script will produce a JavaScript dialog pop-up alert stating âYour website is compromised!â
Whilst this may appear trivial, attackers can use this process to send the unassuming user to a site they control. To learn about different types of XSS attacks check out this blog.
Understanding how XSS attacks work in PHP applications is important so they can be prevented as part of the development pipeline before they hit production and be secure by design.
In this article:
How does XSS in PHP work?
As a developer with access to the source code, you can embed standard PHP code in a blade file for example, if you are coding in Laravel Framework.
XSS PHP Example
This example looks at a website that displays specific data to users based on the group they belong to. The current group is specified via GET parameter in the URL.
Using standard PHP inside a blade file, this code will display a userâs group:
PHP
<p>Hello user, your current group is beginner [ <?php echo $_GET['group']; ?> ] </p>
The website displays the value for the group parameter like this:
Hello user, your current group is [beginner]
And the URL for the page becomes
https://example.com/school/?group=beginner
Injecting the following code into the URL enables an XSS attack:
https://example.com/school/?group=window.location=âhttps://maliciouswebsite.comâ
The injected code will cause a redirect to maliciouswebsite.com as soon as the site loads.
How to fix this XSS vulnerability
Since a blade template renders the website, we can rewrite the code by replacing the standard PHP code with a blade function. The new code is shown below:
PHP
<p>Hello user, your current group is [ {{ $_GET['group'] }} ] </p>
The code above uses the {{ }} echo statement to escape the value of the group parameter. This causes the value to be rendered as plain text all the time.
Some other good ways to prevent this kind of XSS attack are validating and sanitizing inputs from the user. At all times you should avoid displaying and processing the user data without checking the content source and credibility.
Preventing XSS in PHP
Preventing XSS in PHP has two main considerations:
- Type of sanitization performed on the input
- The location where input gets inserted
There is no single sanitization method that can prevent all the XSS attempts no matter how well we filter the input. Required filtering is mostly dependent on the context in which data gets inserted. Preventing XSS with data inserted between HTML elements is the most efficient. On the other hand, preventing XSS with data inserted in Javascript code is much more complicated.
Input Sanitization in PHP
For most PHP websites, htmlspecialchars()
are your way to go!
htmlspecialchars()
will be able to convert special characters to HTML entities as shown below:
‘&
‘ (ampersand) becomes ‘&
‘
‘"
‘ (double quote) becomes ‘"
‘
‘<
‘ (less than) becomes ‘<
‘
‘>
‘ (greater than) becomes ‘>
‘
You will notice that these do not include single quotes. htmlspecialchars()
is best used with the âENT_QUOTES
â to assure that single quotes are encoded as well. Single quote entity conversion is shown below:
“'
” (single quote) becomes ‘'
‘ (or '
)
htmlentities() vs htmlspecialchars()
htmlentities()
is a similar function to htmlspecialchars()
that performs the same functional sanitization on the potentially dangerous characters. The difference is it will encode all the character entities when one is available. This should be avoided because it can lead to excessive encoding practices and cause some content to incorrectly display if character sets change at any time.
strip_tags()
We do not recommend using strip_tags()
for sanitizing the data, as they will remove the content between the HTML tags and will not prevent XSS instances that already exist within the HTML entity attributes. strip_tags()
will neither filter nor encode the angle brackets that are not paired properly. This will give an attacker a chance to combine this weakness with others on the website to inject Javascript, which we are trying to prevent.
addslashes()
Function addslashes()
is mostly used to escape input when it’s inserted into JavaScript variables.
<script>
var = "te"st "; // addslashes()
displayname(var);
</script>
As you can see, addslashes()
(as the name implies) adds a slash in an attempt to prevent the attacker from terminating the variable assignment and appending a malicious code. Although effective in many cases, this process has a critical flaw.
Most engines running JavaScript will construct code segments from open and closed <script?
tags before the code isparsed to them. The browser cares about the data that resides between the two quotes, so to exploit this we don’t really need to bypassâ addslashes()
, but rather terminate the script tag.
<script>
var = "test1</script><script>alert(document.cookie);</script>";
displayname(var);
</script>
The browser will process this as the injected code is an entirely new code segment that contains valid JavaScript.
When will entity encoding fail?
As we described previously the location where the data gets inserted must be taken into consideration when preventing XSS in PHP.
Let’s go over some examples where entity encoding with htmlspecialchars()
is just not enough!
A simple and common example is when data gets inserted within the actual attribute or element tag. HTML Event Attributes allow for JavaScript to be called upon a particular event. onload
attribute, for example, can execute JavaScript once the HTML object loads.
<body onload=alert(document.cookie);>
This may be a rare situation when extremely defined filtering is required, but nevertheless, the location where input gets inserted should always be taken into consideration when preventing XSS in PHP applications.
Preventing XSS in PHP with Bright
While traditional Dynamic Application Security Testing (DAST) tools can test for some XSS vulnerabilities, they are often limited and produce a large number of false-positive results.
Bright can automatically crawl your applications regardless of the programming language used, to test for a wide range of vulnerabilities including XSS, giving you maximum coverage, seamlessly integrated across development pipelines.
Engineering and security teams can trust Brightâs results, with automatic validation of every XSS finding carried out, with NO/false positives. Bright even generates a screenshot proof of concept along with comprehensive developer-friendly remediation advice to fix the issue quickly and early.
Start testing with Bright scanner today – get a free account
