Laravel 5's Key Security Features

  • July 22, 2015

Welcome to the incredibly popular Easy Laravel 5 companion blog. To celebrate the new edition's release (updated for Laravel 5.5!) use the discount code easteregg to receive 20% off the book or book/video package! » Buy the book

A reader recently e-mailed me and asked about Laravel 5's native security features. While I talk about various security-related matters throughout the book, this information isn't consolidated into any particular chapter and so I thought it would be useful to do so in a single blog post.

Laravel helps to secure your web application by protecting against three serious security risks: SQL injection, cross-site request forgery, and cross-site scripting.

SQL Injection

Laravel's Eloquent ORM uses PDO parameter binding to avoid SQL injection. Parameter binding ensures that malicious users can't pass in query data which could modify the query's intent. Consider for instance a form field used to supply an e-mail address which might be used for searching a user table. But instead of supplying an e-mail address the user searches for 'jason@example.com' or 1=1. Left unsecured, the resulting query might look like this:

SELECT * FROM users WHERE email = 'jason@example.com' or 1=1

If you're not familiar with the 1=1 syntax, it is a simple logic expression that always evaluates to true, meaning when coupled with or, all records will be returned from the users table!

Consider a particularly malicious user who instead passed 'jason@example.com'; drop table users; into the search field, meaning an improperly secured query would look like this:

SELECT * FROM users WHERE email = 'jason@example.com'; drop table users;

Your eyes are not deceiving you! If the MySQL account responsible for executing the application queries happened to have the DROP privilege, the users table and all of the data found inside it would be destroyed.

However, when PDO parameter binding is used, the supplied input will be quoted, meaning the former resulting query will look like this:

SELECT * FROM users WHERE email = 'jason@example.com or 1=1'

Because no e-mail address matches jason@example.com or 1=1, the query will safely return no results.

You might be aware that Laravel provides alternative solutions for talking to the database, such as using raw SQL queries. I realize that the SQL gurus out there might wish to just drop down into SQL rather than spend the time learning Eloquent syntax, however the additional effort will be well worth it in terms of avoiding the potential for SQL injection due to a carelessly constructed query.

Cross-Site Request Forgery

Imagine a situation in which a malicious third-party crafts a special link (or a form masquerading as a link) which when clicked initiates a request to another site where you are registered and happen to be authenticated into (by way of a session cookie). Suppose this link endpoint performed a sensitive task such as updating your profile to include a spam message. Because you are authenticated, the site will presume the request is indeed coming from you, and update the profile accordingly.

CSRF (cross-site request forgery) tokens are used to ensure that third-parties cannot initiate such a request. This is done by generating a token that must be passed along with the form contents. This token will then be compared with a value additionally saved to the user session. If it matches, the request is deemed valid, otherwise it is deemed invalid.

If you are constructing forms using standard HTML (which I do not recommend), then you will need to supply the token to your form like so:

<form ...>
{!! csrf_field() !!}
</form>

If you're using the (recommended) LaravelCollective/html package to construct your forms, then the CSRF token will be automatically added for you.

Cross-Site Scripting

Laravel's {{}} syntax will automatically escape any HTML entities passed along via a view variable. This is a very big deal, considering that a malicious user might pass the following string into a comment or user profile:

My list <script>alert("spam spam spam!")</script>

If this string were allowed to be saved to the database without filtering, and then subsequently displayed in a web page without escaping, it would in fact display an annoying alert window. This is an example of an attack known as cross-site scripting. In the grand scheme of things this is but a minor annoyance compared to more sophisticated attacks which might prompt the user to supply some sensitive information via a JavaScript modal which was subsequently sent to a third-party website.

Fortunately, when a variable is rendered within the @{{}} escape tags, Laravel would instead render the string like so, thus preventing the possibility of cross-site scripting:

My list &lt;script&gt;alert("spam spam spam!")&lt;/script&gt;

Conclusion

Of course, there are plenty of other things you should do to further secure your Laravel application, such as ensuring browser-based error reporting is disabled so as to ensure sensitive application details aren't exposed to a potentially malicious party. However Laravel really does ensure a much more secure application by eliminating these three very common attack vectors.