Security Tip: Why Parameterised Queries Are Important!
[Tip #4] We're following the theme of reminders for simple features that are easy to overlook with a reminder to use Parameterised Queries!
Laravel provides an expressive fluent interface for building database queries, either as raw queries through the query builder or as part of Eloquent (Laravel’s Object-Relational Mapper, ORM). The query builder allows you to write queries regardless of the database backend, and makes it easy to write secure queries. This is important to protect us against SQL Injection (SQLi) attacks.
Check out my SQL Injection In Depth article for more details, but in a nutshell, an SQL Injection attack is where the attacker can modify the query being executed on the database to perform some other (usually malicious) behaviour. SQLi is often used to bypass authorization checks or download copies of the database, and in some circumstances, it can even be used for more destructively to drop (delete) an entire database.
This is why we need to use parameterised queries.
Consider this PHP SQL query string:
SELECT * FROM users WHERE name = '{$name}'
In a typical scenario, the $name
variable could be populated from user input. Maybe it’s a search box, or the user has specified their name on a profile form. Either way, it’s probably user data, and we simply cannot trust it.
We can’t trust it because an attacker could pass in a string with multiple single quotes (‘
) in an attempt to re-write part of the query by closing the string early and adding alternate conditions.
Such as:
' OR '' = '
Which can transform the query into:
SELECT * FROM users WHERE name = '' OR '' = ''
The result is a very different conditional.
How does Laravel Help Us?
Laravel’s query builder helps us out by providing a way to safely provide variables (i.e. user data) to the database in the form of a parameter. The database itself will treat the whole parameter as a single value and use it when it performs the condition, without the value being injected into a string. The end result being, no combination of characters within the parameter can modify the query itself.`
Laravel does this with the where()
method on the query builder:
$user = DB::table('users')->where('name', $name)->get();
The $name
parameter, the conditional value, is now completely safe, and you can pass whatever your users provide directly in there.
Note, only the value is safe. The column name (‘name’
) is used in the query, and allowing users to provide that directly is asking for trouble!
You can also define the conditional operator as the second parameter, and bump the value to the third parameter:
$users = DB::table('users')
->where('name', 'like', 'T%')
->get();
This will still ensure the value is handled safely by the database, but allow you to perform more complex queries.
Finally, if you need to use more complex logic or database functions that aren’t handled by the query builder, you can pass parameters into whereRaw()
too.
$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->get();
The database will see any ?
and automatically replace them safely with the values in the parameter array sequentially.
So you’ve got no excuses not to write secure parameterised queries.
Just remember: always use a method like where()
when building queries.
Found this security tip helpful? Don't forget to subscribe to receive new Security Tips each week, and upgrade to a premium subscription to receive monthly In Depth articles, or toss a coin in the tip jar.
Reach out if you're looking for a Laravel Security Audit and Penetration Test or a budget-friendly Security Review, and find me on the various socials through Pinkary. Finally, don't forget to check out Practical Laravel Security, my interactive security course.