Introduction
In a day and age where hacking and data theft are common, understanding and implementing strong security measures is more important than ever!
In the first part of our web security series, we’ll take a closer look at three of the most common vulnerabilities found in applications: SQL injection, Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF).
We’ll explore how these vulnerabilities work, demonstrate why they can be dangerous, and look at how developers can protect their applications against them.
SQL Injection
SQL injection is a technique in which an attacker injects malicious code into an input field, and can cause the database to execute unwanted queries. These vulnerabilities are particularly dangerous because databases often contain valuable information, such as user accounts, personal data, and other sensitive information. Fortunately, there are well-established techniques developers can use to protect their applications against SQL injection.
An Example
To demonstrate the concept we will be using a GET parameter.
Suppose we have the following query:
A normal request like ?id=1 would return all data for the movie with the id of 1. However, if we change this to ?id=1 or true our query becomes:
This would return the data of all movies in the table.
An attacker may try to take this even further by attempting to inject additional SQL statements, for example:
If no proper security measures are in place this query could also return all the user data. An attacker could even remove the content from your application’s database.
Best Practices
Fortunately, protecting against SQL injection is relatively straightforward using the following rules. And Laravel provides many built-in protections when used correctly. By combining the following techniques, developers can significantly reduce the risk of SQL injection and limit the potential impact if a vulnerability is ever introduced.
Never directly insert user input into a query string
Use parameter binding for the value, example:
This keeps the input value separate from the SQL statement, preventing it from being interpreted as SQL code.
Always validate user input
Validation helps ensure that the application receives the expected input. For example, an id should be validated as an integer and, where appropriate, checked to ensure that the corresponding record exists.
Be careful with dynamic SQL generation
In this case $sort should be limited to the possible sorting columns.
Use the principle of least privilege
Restrict the database account to only have the permissions it actually needs. This limits what an attacker can do if a SQL injection vulnerability is exploited.
Cross-Site Scripting (XSS)
This is a vulnerability that occurs when an application includes untrusted user input in a web page without properly escaping or sanitizing it. This can allow an attacker to inject JavaScript that is then executed in another user’s browser. For example, an attacker could inject malicious links or other content that tricks users into interacting with a dubious website, perform actions as the victim, or access sensitive information that is accessible to scripts running in the browser.
There are 3 common types of this attack:
- Stored XSS – malicious content is saved, for example in a database, and later displayed to users.
- Reflected XSS – malicious input is included in a request and immediately returned in the application’s response.
- DOM-based XSS – client-side JavaScript takes untrusted data and inserts it into the page unsafely.
How to implement safety nets?
In vanilla PHP, you can use htmlspecialchars() to prevent user-controlled output from being interpreted as HTML when displaying a web page. However, when using templating engines like Twig or Laravel’s Blade, output is generally made safe for display automatically when using their standard output syntax.
Let’s look at an example. Let’s say we have a simple movie title that is being displayed and an attacker managed to add a link to it.
When using the default output syntax in Laravel:
Laravel will display this as plain text. So if the movie title contains:
This will be displayed as text rather than as a clickable link.
However, when using Blade’s raw output syntax:
This would show our Movie title that is now a clickable link.
Therefore, raw output should only be used when the content is trusted or has been appropriately sanitized. Otherwise, it can introduce an XSS vulnerability.
Cross-Site Request Forgery (CSRF)
This is a vulnerability that occurs when an attacker tricks a user’s browser into sending a state-changing request to a web application where the user is already authenticated. The browser may automatically include the user’s authentication information, such as a session cookie, allowing the attacker to perform actions on behalf of the user, such as changing their email address or making a money transfer.
What are state-changing requests?
Simply put, a state-changing request is a request that changes something on the server rather than just retrieving data. It is good practice to use GET methods only for retrieving data, while methods such as POST, PUT, PATCH and DELETE are generally used for state-changing requests.
How to implement safety nets?
The most common way to protect against these kinds of attacks is to use a CSRF token. Every time a state-changing request is made a token is included and validated by the server when processing the request.
In the example above, the generate() method creates a secure token and stores it in the user’s session. The insert() method adds the token to the HTML form as a hidden field. When the form is submitted, the server must then retrieve the token and validate it using the validate() method. If the tokens do not match, the server should reject the request and not perform the requested action.
In Laravel, a similar CSRF protection is built in. The @csrf directive can be added to forms to include a hidden CSRF token, which Laravel's CSRF middleware will then validate when the form is submitted.
Conclusion
Today, we discussed some common web security vulnerabilities that can cause serious damage. Fortunately, they can largely be prevented by following established security practices that are well implemented in the Laravel framework. By understanding how these vulnerabilities work and applying the appropriate protections, developers can build applications that are safer and more resilient to common attacks.