Introduction
In our previous article, we explored three common web vulnerabilities: SQL injection, XSS, and CSRF. In this second part of our web security series, we’ll take a closer look at three other important aspects of keeping an application secure and free from common vulnerabilities.
Session Security
A session manages a user’s authenticated state after they log in and is critical to keep secure against hijacking, theft, and unauthorized access.
When a session involves transmitting sensitive user data, it’s important to encrypt all communication between the server and the client using HTTPS. However, encryption alone isn’t enough to guarantee session security. Some of the most common session-related attacks are:
- Session fixation – The attacker gets the user to use a session ID known to the attacker.
- Session hijacking – The attacker gets access to the user’s session ID.
Let’s take a closer look at important practices to help keep sessions secure and how we can implement them in Laravel.
An Example
Take this basic login method, for example. Since we didn’t regenerate the session ID after successful authentication, the application may be vulnerable to session fixation. If an attacker managed to force the victim to use a session ID known to him before logging in. To limit this risk, Laravel recommends regenerating the session ID after successful authentication by calling $request->session()->regenerate();. This generates a new session ID while preserving the session data.
Best Practices
As seen above it’s important to regenerate your session ID after a successful login or when a privilege change is made, however there are many more things we can do to improve our session security.
Cookie configuration
Proper session cookie configuration is essential. Laravel allows us to configure this through config/session.php.
- secure – If enabled ensures the browser sends the session cookie only over HTTPS connections.
- http_only – Prevents client-side JavaScript from reading the cookie. Reducing the risk of session theft through XSS attacks.
- same_site – Controls when the browser sends the cookie with cross-site requests. Laravel default setting, lax helps mitigate CSRF attacks while allowing cookies in many normal navigation scenarios.
But most importantly, always enable HTTPS in a production environment. These settings complement HTTPS, but none of them alone guarantee protection against session attacks.
Session Expiration
Sessions shouldn’t remain valid forever. If a user leaves a shared or unattended device, someone else could be able to access their account.
Through config/session.php Laravel also allows us to configure SESSION_LIFETIME. This sets the time a session can be inactive before it expires.
At Logout
When logging out it’s advised to clear the authentication state, invalidate the session, and regenerate the CSRF token.
Session Storage
Session data can be stored in different places, depending on the configuration. Client side storage is the first you learn as a starting developer. However, for managing authenticated sessions, server-side storage is the preferred solution. This method gives the application more control over the session data.
Laravel supports several session drivers, including files, databases, and Redis. Using a server-side driver also makes it easier to invalidate sessions when necessary.
Password Hashing
Hashing transforms a user’s password into a one-way representation that is designed to be computationally expensive to reverse. Unlike encryption, hashing is not meant to be reversed to recover the original password.
When hashing a password, you should never use general-purpose algorithms like MD5 or SHA-1. Instead, use password hashing algorithms like bcrypt or Argon2id, which are specifically designed to make password guessing more expensive.
Laravel provides the Hash facade to make password hashing and verification straightforward. It provides methods like Hash::make() to hash passwords and Hash::check() to verify them. By default, bcrypt is being used for hashing, but other algorithms are supported and can be configured through the configuration file.
It’s also possible to make the hashing more resistant. For bcrypt, this is done by increasing the hashing rounds. Of course, this comes with a performance penalty, so when changing it, choose the work factor carefully.
For example:
Rehasing Passwords
This configuration and algorithms can evolve, depending on the applications needs and general recommandations. When this happens, existing password hashes may need to be updated. For this specific requirement Laravel provides Hash::needsRehash() to check if a hashed password needs updating without the need for a user to update his password.
For example after successfully authenticating a user, we could check whether their password needs updating:
Rate Limiting
We’ve all heard of DDoS, DoS, and brute force attacks. These attacks can overwhelm an application with requests or repeatedly attempt to guess a user’s credentials.
Restricting how many requests a client can make within a specific time frame can drastically reduce the risk of these attacks. This is where rate limiting comes in.
Laravel provides built-in rate limiting functionality that allows us to define request limits for specific actions, such as logging in or downloading a file.
A basic example in Laravel would be to use the throttle middleware on routes to enforce rate limits:
This example limits the login attempts to 5 per minute per rate-limiting key. By default the rate limiting key is the client’s IP address.
Conclusion
Today, we saw that proper session management helps protect authenticated users, secure hashing keeps credentials safer, and rate limiting helps reduce abuse. These security measures can go a long way toward keeping our users data safe.