Secure cookie of you web application with PHP or Symfony

Most website use cookies. Whether for tracking or persisting data, unfortunately good security practices are not always apply. Yes it's often only an option to add to ensure security.

Create a cookie in php is easy with function setcookie().

Option http_only

By default a cookie can be read with Javascript. This snippet show how simple it is.

<?php
setcookie('user_id', 10, 0, '/', 'orion.dev');
?>
 
Hello!
 
<script>
    alert(document.cookie);
</script>
 

Once the page is loaded, an alert will display the content of the cookie. If we can read cookie with Javascript, it's easy to write a snippet to send cookie's content to another URI using ajax. Let inject this snippet on any website and you have a simple but working XSS attack.

As we can read cookie, we can also write cookie with Javascript.

<?php
setcookie('user_id', 10, 0, '/', 'orion.dev');
 
var_dump($_COOKIE);
?>
 
Hello!
 
<script>
    document.cookie = "username=ulrich; expires=Thu, 17 Dec 2020 12:00:00 UTC; path=/";
    alert(document.cookie);
</script>

Once the page is loaded, an alert will display user_id and username. You have to refresh the page and altered cookie will be sent to server, then we will find username in $_COOKIE. Fortunately modern browsers do not allow to override, in javascript, a value of the cookie that was sent by the web server. Nevertheless, being able to read the cookie in javascript is still a problem.

The last option of the setcookie() command is http_only and is false by default. By passing it to true, the cookie sent by the web server will no longer be available in javascript. This will not prevent the creation of javascript cookies and they will be available on the server side, but sensitive information (such as session ID) will no longer be available and could not be steal anymore.

<?php
setcookie('user_id', 10, 0, '/', 'orion.dev', false, true);
 
var_dump($_COOKIE);
?>
 
Hello!
 
<script>
    document.cookie = "username=ulrich; expires=Thu, 17 Dec 2020 12:00:00 UTC; path=/";
    alert(document.cookie);
</script>

In Symfony, we find this option in sessions' configuration in framework section of the config.y(a)ml file. This option is true by default in version 3.x and 4.x but I am not sure this was the case for every versions of symfony 2.

framework:
    session:
        cookie_httponly: true

This option can also be set in php.ini configuration file.

Option secure

If your site is in HTTPS and no longer offers HTTP, this option is for you. It guarantees that the cookie will only be sent if your connection is secure. Hum, this rule only applies in the browser to web server direction and not the other way around. It is up to the developer to verify that connection is secured before sending a cookie to the  browser.

But what's the point? This avoids cookie theft via a Man In The Middle attack, since the cookie will be protected during the transfer. This is very important if your application allows users to stay connected, this operation goes through a cookie and from the first request to the server, the browser sends this cookie.

This is the 6th option of the setcookie() function which defaults is false.

setcookie('user_id', 10, 0, '/', 'orion.dev', true, true);

In Symfony, you find this option with http_only but it's false by default on all symfony's version.

framework:
    session:
        cookie_httponly: true
        cookie_secure: true

If you use RememberMe feature in symfony's security, you have to set secure option in security.y(a)ml file.

security:
    firewalls:
        main:
            remember_me:
                secure: true

Prefixes

They are not well known, but supported by all browsers except those of Microsoft. With prefixes, it's possible to force the browser to not accept a cookie if it's misconfigured. There are two prefixes "__Secure-" and "__Host-".
__Secure- forces the developer to add the secure flag to his cookie, otherwise it will be ignored by the browser.

setcookie('__Secure-user_id', 10, 0, '/', 'orion.dev', true);

__Host- is more restrictive, cookie must have the secure flag but also path to root and blank domain.

setcookie('__Host-user_id', 10, 0, '/', '', true);

You do not see what these prefixes can be used for? Consider them as extra security that will allow you to be alerted (a bug can be an alert!) If you make a mistake in configuring your cookies.

In Symfony, you need to change session name in config.y(a)ml file to add the prefix.

framework:
    session:
        cookie_httponly: true
        cookie_secure: true
        name: __Secure-sessid
 

Once again, if you use RememberMe feature, you need to change cookie name in security.y(a)ml file.

security:
    firewalls:
        main:
            remember_me:
                name: __Secure-REMEMBERME
                secure: true
 

Option SameSite

This option is also very interesting, it allows to limit (not prevent) CSRF attacks because it tells browsers not to send the cookie if the source query was not made from the site. To put it simply, if I put a Github link here and you click it, the cookie that allows you to get authenticated on github will not be sent. You think it's not user friendly, but if I replace Github with your bank, what do you think?
There are 2 options: lax and strict. As the name suggests "strict" is the most restrictive option. On the other hand "lax" will leave the cookie if the HTTP request is of type GET.
Browsers have a good support of this feature, only Safari is late, but support is planed for next version (mobil & desktop), But Opera has no plan for this feature yet. About PHP, support will be added in next version PHP 7.3

While waiting to migrate on this new version, to take advantage of the support of SameSite, it is necessary to send the cookie via the headers http.

<?php 
header('Set-Cookie: __Secure-REMEMBERME=5sdf4d65f4; Path=/; Secure; Samesite=lax');

In Symfony, SameSite option will be supported for RememberMe cookie in next version 4.2.

security:
    firewalls:
        main:
            remember_me:
                name: __SECURE-REMEMBERME
                secure: true
                samesite: lax
 


If you want to check your cookie config and security of your website, I recommend you Mozilla observatory.

Add a comment