If you want to develop a secure web application, you need to make sure your cookies are locked down tight.
Because web applications use the client-server model, they are stateless. So we use sessions to persist data from one request to the next. The most common method for identifying a session is to create a unique session ID. Obviously this ID is stored on the server, either directly on the filesystem or in a database. But how will it be stored on the client side? That's where cookies come in.
Clearly, it is of supreme importance that we make our cookies secure. So what defines a "secure" cookie? Like everything else in the world of security, this depends on context. There are no quick fixes or easy answers. To really be confident that your cookies are secure, you need to do your own research and base your approach on the specifics of your situation. That being said, the purpose of this post is to provide a decent starting point. I will focus on the following and describe how each can be implemented in Laravel:
Why should we use prefixes?
Every cookie has a name. We can add prefixes to the names of our cookies to make them more secure. These prefixes tell the browser to reject the cookies if they do not meet certain requirements. The obvious limitation here is that this assumes that the user is using a browser that supports these prefixes.
There are two supported prefixes. Each begins with two underscores and ends with a hyphen:
__Secure- prefix, perhaps counterintuitively, is the less restrictive of the two.
Compliant browsers will reject a cookie with this prefix if it does not meet the following
- It must be marked with the
- It must be sent from a secure origin.
__Host- prefix is more restrictive and is thus preferable. In addition to the
requirements of the
__Secure- prefix, it must also meet these:
- It must not include the
- It must have its
Pathattribute set to
Always opt for the
__Host- prefix unless your application for some reason necessitates
the use of the
Domain attribute or setting the
Path attribute to something
other than the root. Proper use of prefixes helps prevent a man-in-the-middle (MITM) attacker from
overwriting cookie values.
Why should we use the HttpOnly attribute?
In case you're wondering, the name
HttpOnly does not mean "HTTP as opposed to HTTPS." Rather,
This helps prevent cross-site scripting (XSS) attacks.
Why should we use the SameSite attribute?
SameSite attribute controls whether a cookie is sent on cross-origin requests. There are
three possible values:
None(least secure): the cookie is sent on cross-origin requests.
Lax(intermediate security): the cookie is sent on cross-origin requests only if:
- the method is safe (
it is a top-level navigation (meaning the URL in the address bar changes),
as opposed to an AJAX request, a request from a
- the method is safe (
Strict(most secure): the cookie is not sent on any cross-origin requests.
Clearly, you should always use
Strict for your session cookies! Otherwise, you are leaving
yourself open to cross-site request forgery (CSRF).
Why should we use the Secure attribute?
Secure attribute ensures that the cookie is not sent unencrypted. It is only sent
over the HTTPS protocol. This helps to mitigate man-in-the-middle attacks.
Prefixing the session cookie in Laravel
To secure the session cookie, open up
config/session.php. The sections of interest to us
begin on line 118 (as of Laravel 8.57.0).
Let's make sure we understand what this code does:
It checks your
.envfile for a
SESSION_COOKIEvalue. If it finds one, it uses that, and does not proceed with the following steps.
It determines the name of your application by checking your
.envfile for an
APP_NAMEvalue. If no value is found, it defaults to
Using the application name determined in step 2, it constructs the cookie name using the
It may be tempting to modify this file directly. For example, you might want to just hard code the cookie name like this:
But I would recommend against this. If you develop locally using
php artisan serve,
it will work, because Firefox and Chrome make developers' lives easier by ignoring cookie
http://localhost. However, it is not future-proof. If one day you
switch to developing on a server on your network without HTTPS, then suddenly your cookies will
no longer work in development.
A more future-proof solution is to use environment variables as the framework developers intended.
.env files, add a new line under the other session-related variables. That way
you can exclude the prefix in development and include it in local/production.
Adding the Secure attribute to the session cookie in Laravel
Scroll down until you find the following:
This checks your
.env file for an environment variable called
SESSION_SECURE_COOKIE value. By default, it doesn't exist. So add it:
If developing on a server without HTTPS, you can set it to false in your development
Adding the HttpOnly attribute to the session cookie in Laravel
Keep scrolling until you get to this section:
Here we don't need to worry about
to access the session cookie, regardless of environment. So simply hard code it to
Adding the SameSite attribute to the session cookie in Laravel
Finally, scroll down to the bottom of the file.
Again, no need to worry about
.env files here. Hard code it to
And just like that, your session cookie is now secure. Don't forget to run
php artisan config:clear so that your changes take effect. Also double check that for
every change made to your local/development environment file, you also made the corresponding change
to the production file!
Understanding the CSRF cookie in Laravel
The CSRF token cookie is a little different. In my applications, I tend to remove this cookie
altogether. I do this because I like it when vulnerability scans find nothing wrong, but with
this cookie you cannot prefix it or add the
HttpOnly attribute without totally defeating
prefix it, you have changed the name, so the frameworks will probably miss it. If you add the
the potential vulnerabilities or remove it altogether. I opt to remove it.
According to the official Laravel documentation:
Laravel stores the current CSRF token in an encrypted
XSRF-TOKEN cookie that is
included with each response generated by the framework. You can use the cookie value to set the
X-XSRF-TOKEN request header.
libraries, like Angular and Axios, automatically place its value in the
header on same-origin requests.
Removing the CSRF cookie in Laravel
Removing this cookie is incredibly easy. Open up
By default, it looks something like this:
The parent class contains a
$addHttpCookie, which we need
to override and set to
That's all there is to it.