Imagine if just about any tech-savvy user who visits your website could control the JavaScript of your site. There is a vulnerability that makes that possible. An attacker can inject malicious JavaScript code into a website via an XSS attack. The malicious code can perform actions like defacing the design of a website or granting unauthorized access to sensitive user data.

XSS stands for cross-site scripting. It’s one of the OWASP Top 10 security risks that affect web applications.

In this post, I’ll explain what XSS is. Then I’ll walk you through some examples of XSS in Laravel. I’ll also show you a few ways of preventing an XSS attack for each example.

So, let’s take a looking at the definition of XSS.

What Is XSS?

In an XSS attack, attackers look for vulnerabilities on a website that will let them inject malicious scripts into the website. Once attackers get their scripts injected, they can control the behavior of the victim’s site and steal user data.

A simple XSS attack can occur on a vulnerable website that accepts user input via a GET parameter and displays the data on the webpage. Let’s take a look at the following URL:

https://example.com/profile/?u=joseph

The URL points to a page 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=<script>alert("Hahaha!!! you got hacked!!!")</script>

The injected script will cause the webpage to throw a JavaScript alert dialog with a creepy message that reads “Hahaha!!! You got hacked!!!” as shown in the screenshot below:

xss-js-alert-example

In the code we just looked at, the attacker isn’t really doing anything very harmful — just displaying a creepy message. But if attackers can execute that code, then they can run even more dangerous code. To learn the full details about XSS and see the different types of XSS attacks check out this post.

XSS and the Laravel Framework

Now that we know what XSS is, let’s take a look at XSS in Laravel. Laravel is a very popular framework, written in PHP, for building web apps. While Laravel is popular for backend development, it offers a neat way to render user interface (UI) using the blade engine.

What Is Blade?

Blade is a PHP templating engine built into Laravel. When building a Laravel app, your HTML code goes into the blade file. Blade files are saved with the .blade.php extension.

In XSS, the malicious code runs on the client-side (on the user’s browser). The malicious code runs along-side normal code when users load a webpage. Although Laravel has some mechanisms in place to protect against XSS, Laravel apps are vulnerable to XSS attacks. We’ll look at examples of some vulnerabilities in the next section.

Example 1: Embedded PHP Code

As a developer, you can embed standard  PHP code in a blade file. This example looks at a webpage that displays custom data to users based on their current country. The current country is specified in the URL via a GET parameter.

Using standard PHP inside a blade file, this code will display a user’s country:

<p>Hello user, your current country is [ <?php echo $_GET['country']; ?> ] </p>

The webpage displays the value for the country parameter like this:

Hello user, your current country is [NG]

And the URL for the page becomes

https://example.com/shop/?country=NG

Injecting the following code into the URL enables an XSS attack:

https://example.com/shop/?country=<script>window.location=”https://google.com”</script>

The injected code causes a redirect to google.com as soon as the page loads.

Prevention

Since a blade template renders our webpage, we can rewrite our code by replacing the standard PHP code with a blade function. The new code is shown below:

<p>Hello user, your current country is [ {{ Request::get('country') }} ] </p>

The above code uses the {{ }} echo statement to escape the value of the country parameter. This causes the value to be rendered as plain text all the time. Request::get() is a more Laravel way of doing $_GET[''].

Some other good ways to prevent this kind of XSS attack are sanitizing and validating user inputs. You should avoid processing or displaying user data without checking the nature of the content. In our example, the expected input has a two-character country code.

Example 2: Rendering UI Outside Blade

In Laravel, you can output data or even render HTML directly from a controller class. And in this example, we render a user interface outside of a blade file.

We’ll use the same page and URL from the previous example. However, we change the code to exclude any blade files. The malicious version of the URL is again,

https://example.com/shop/?country=<script>window.location=”https://google.com”</script>

This time the page UI renders using the following code from a Laravel controller class:

public function shop()  {
echo "Hello user, your current country is " . $_GET['country'];
}

Just like in the last example, when a user clicks on the link, the page redirects to google.com.

Prevention

We can prevent this type of XSS attack by passing the user input through PHP’s htmlspecialchars() function. Doing so escapes HTML tags and any scripts, causing the page to render the user input as plain text. Here is the code for this solution:

public function shop() {
echo "Hello user, your current country is" + htmlspecialchars($_GET['country']);
}

Sanitizing user data and removing unwanted characters or codes can also help.

Another solution is validating user input. For our example, this can be done by having a list of countries and verifying that the values passed by users exist in the list. The code for this solution is shown below:

$country = $_GET['country'];
$country_list = array('NG','US','RU', 'GH');

if(in_array($country, $country_list))
{
echo "Hello user, your current country is " . $_GET['country'];

} else {
echo "Invalid input[Country]";
}

Example 3: Stored User-Generated Content

In our two examples so far, we looked at how to prevent XSS attacks for user inputs coming from a URL parameter. In this example, we’ll look at data coming from a MySQL database.

Our example app this time displays a product page where users can drop product reviews. The URL for this page looks like the following:

https://example.com/product/?id=201

The page will show details about a product with an ID 201. The page also includes reviews submitted by users via a form and stored in a database.

To perform an XSS attack, an attacker can enter the following as a review message:

<img src="invalid_url.png" onerror=alert(document.cookie)>

If you save and output review messages without any validation or without escaping the content, the above code could grant an attacker access to the cookies of unsuspecting users.

A database stores the malicious code in this example. The malicious code will affect all users who visit the affected page, leading to information leak for hundreds to thousands of visitors.

Prevention

When building a website that displays user-generated content, make it a habit to validate data before display. If you use blade files to render pages, I recommend you use the {{ }} feature. Alternatively, you can use PHP’s htmlspecialchars() function.

Using Laravel Middleware for XSS Prevention

You can also prevent XSS attacks on a Laravel site using middleware. To create a middleware, open terminal or command prompt and make sure the current directory is set to the root of your Laravel project. Then, enter the following command:

php artisan make:middleware XSS

The command you just entered will create a new XSS.php file under app/Http/Middleware. Now, open XSS.php and add the following coding to the handle() method:

public function handle($request, Closure $next)
  {
$userInput = $request->all();
array_walk_recursive($userInput, function (&$userInput) {
$userInput = strip_tags($userInput);
});
$request->merge($userInput);
return $next($request);
}

Next, you will need to register the new middleware inside Kernel.php which is located at app/Http/Kernel.php. Open the Kernel file and add the following code to the routeMiddleware array:

'XSS' => \App\Http\Middleware\XSS::class

The complete code for the array should look like this after adding the new middleware:

/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
.
.
.
'XSS' => \App\Http\Middleware\XSS::class,
];

One final step, let’s create a route that makes use of the middleware we created. To do that, open web.php or the specific route file for your project and create a route like this:

Route::group(['middleware'=>'XSS', function() {
Route::get('/product', 'ProductController@index');
}]);

And with that, we have our middleware active in example.com/product. We can easily use the middleware in multiple routes by adding each route to the route group.

In Closing

I’ll close by pointing out that this post is not an exhaustive list of XSS vulnerabilities in Laravel. However, patching all three vulnerabilities mentioned in this post can help improve the security of your Laravel applications.

In addition, you learned what XSS is and should now understand how to test your application for possible vulnerabilities. XSS is a serious security issue in general, and it’s serious in Laravel too.

This post was written by Pius Aboyi. Pius is a mobile and web developer with over 4 years of experience building for the Android platform. He writes code in Java, Kotlin, and PHP. He loves writing about tech and creating how-to tutorials for developers.