Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

aayala91's avatar

GDPR - For those of you building apps with Laravel

I've been doing some reading into GDPR and as an aspiring developer, I feel a little worried.

Part of GDPR (assuming I've understood it correctly) requires companies to encrypt any information about a user.

Many apps I've built with Laravel store information about users in a database.

So, I thought okay. To ensure my app is GDPR compliant, I can just attach a trait to User models which Encrypt & Decrypt attributes - that's easy.

But how would we go about querying data with Eloquent? Would this still be possible?

I'm fairly confused.

0 likes
30 replies
KNietzsche's avatar

where did you read the companies are required to encrypt any information about a user ???? I followed a training session and have read a lot of things about that, as we want to be compliant... and a lot of things are about processes, security...I agree, but not encryption...( that will be a nighmare, even for people who will check ) ! But if you have information and links about what you say, let me know !

KNietzsche's avatar

basic questions are... what kind of data do you want to collect from a user, are they sensitive (medical, religion, ...) why you need these data, how long you store the data..where you store the data...the hosting server could be responsible as well if it did not its job to protect the server....how can a people acces his data and change them or delete them....how long the data are saved...what to do if there is an issue (lost phone with mails, contacts...) what to do if there is an attack (server) how to solve... So basically...think before to do, and use only data you need for your business. And a personal data from People A concerns only People A, just have procedures to not allow people B to see people A's data.... Be fair with the customer (cookies rules) no hidden script to catch the user actions (heatmap, etc) and always accept user consentment to add a cookie Nothing special...just need to write the processes concerned by personal data. Ok, I agree there is a lot of marketing and wrong ideas at this time on the social medias...even some companies trying to sell softwares to be compliant...once you understand such a software does not exist as each company is different and has different processes...it's easy to put in place ! Even an excel sheet could be ok, just need each department of the company to describe the processes...it's not a specific IT topic !

Snapey's avatar

Legislation does not normally dictate any technology.

It is up to the organisation to decide what it feels is appropriate measures for the safe storage of personal data.

This is probably the best website to understand and navigate the legislation https://gdpr-info.eu/

Recital 83:

In order to maintain security and to prevent processing in infringement of this Regulation, the controller or processor should evaluate the risks inherent in the processing and implement measures to mitigate those risks, such as encryption. Those measures should ensure an appropriate level of security, including confidentiality, taking into account the state of the art and the costs of implementation in relation to the risks and the nature of the personal data to be protected. In assessing data security risk, consideration should be given to the risks that are presented by personal data processing, such as accidental or unlawful destruction, loss, alteration, unauthorised disclosure of, or access to, personal data transmitted, stored or otherwise processed which may in particular lead to physical, material or non-material damage.

2 likes
Snapey's avatar

To answer your technical question, you can use a trait to encrypt data being saved by eloquent, and decrypted when it is retrieved.

Very quickly though you will come across problems like;

  • logging in the user when all the email fields are encrypted (how do you find the right row)
  • ensuring a user's email is unique in the database
  • loading records sorted in a particular way (eg list of customers sorted by surname)

Its easy to think, just encrypt the input data and compare it to the encrypted database data - but with strong encryption, the same input should never produce the same output.

All these issues are solvable, but the scale of the issue goes up exponentially with the number of records

2 likes
ymslavov's avatar

Or just encrypt the database at rest? Most production-ready dbs have such an option and most of the time it's super simple to implement.

Snapey's avatar

@ymslavov yes, but this does not protect against someone getting the db credentials or access to a backup

1 like
EmilMoe's avatar

To clarify: Not everything needs to be encrypted, only high risk data such as social security numbers and bank information.

2 likes
Vilfago's avatar

I take the opportunity of this topic for a small question regarding cookies.

In a freah laravel application, with auth route and controllers, is it correct that there is a cookie only for the remember me option at login ?

So if a user never click this box, he has no cookie ?

Thank you for your help.

Snapey's avatar

@Vilfago no, there is always a session cookie unless you use the stateless API routes.

Vilfago's avatar

Ok, so we all need a warning message that we're using cookies...

Good to know >.<'

Thanks for the answer @Snapey

Snapey's avatar

@Vilfago if its a pure static website you could have routes that don't use the web middleware but you would also need to exclude csrf protection from any contact or login form

damianrocks's avatar

I'm reopening this debate with one question about consentment. How do we store the consent of the user ? We have full control over data after all.. And its' very easy to cheat.

Is there a requirement about signing data ?

Thanks

pluma's avatar

Sorry to gravedig but this post is the first result I came across when googling for GDPR and Laravel.

Let's correct some of the misconceptions about compliance: you do not have to encrypt your database or your data but you do have to implement "state of the art" best practices. What that means depends on your use case, your technology stack, your infrastructure and so on. This may include encryption at rest. And yes, the GDPR (and the ePrivacy Directive which for all intents and purposes should be implicitly included whenever GDPR compliance is mentioned) does say some things about technology despite not explicitly telling you what to do.

My biggest gripe with Laravel is that when using session cookies there's no way to restrict session cookies to logged in users. This makes it hard to present a GDPR consent dialog before issuing the cookies. Technically this may be considered compliant as the legal basis for the session cookie does not require consent but it still feels iffy to throw cookies at an anonymous user before they are actually useful for something, especially when most of the interactions happen behind a login.

It looks like the solution to this problem is to override the StartSession and ValidateCsrfToken middleware, as suggested in this Stack Overflow answer, but I would have hoped there'd be an easy setting for this the way there is for disabling lazy loading and such: https://stackoverflow.com/questions/78352481/prevent-laravel-11-from-setting-any-cookies-including-session-xsrf-cookies

As a rule of thumb, it's best to think of all user data as radioactive. You only want to handle radioactive material when it can't be avoided, you only want to store it when absolutely necessary and you want to audit anything that happens to it so you can trace any possible contamination and know whom to notify when there is a spill. Web 2.0 era privacy practices by comparison are like children playing in radioactive sludge.

The most important principles are legal basis, which in practice usually means obtaining consent, and the basic rights: right to delete, right to information (and portability) and right to correct. You want to make sure a user's data can be deleted in such a way to not leave any trace beyond what is necessary for legal compliance (which usually has a shelf-life, so you still want to delete once that need has expired). You also want to make sure there's a way to obtain all the data specific to one user in a machine-readable way (i.e. JSON, CSV or XML) without leaking any private data of other users and without leaking any confidential information or secret implementation details. You also want to make sure that any personal information you collect about a user can be corrected by the user (with your help if necessary), so don't use legal names (e.g. usernames which may be based on the legal name) as primary keys for example.

Luckily Laravel gets most of these things right by default. Depending on your use case, data portability may not be an early requirement (either because there is no data to port or it's easier to do it manually than implement a way for users to do this) either. So the only big one is making sure user deletions cascade correctly (keep in mind user generated content may still be subject to the GDPR itself so it's safer to delete that too unless you can guarantee it's impossible to de-anonymize) and that you obtain consent for any data retention and processing that isn't strictly necessary for what your app/site needs to do to provide the user with a minimal version of its functionality (e.g. sessions and CSRF tokens).

1 like
martinbean's avatar

My biggest gripe with Laravel is that when using session cookies there's no way to restrict session cookies to logged in users. This makes it hard to present a GDPR consent dialog before issuing the cookies. Technically this may be considered compliant as the legal basis for the session cookie does not require consent but it still feels iffy to throw cookies at an anonymous user before they are actually useful for something, especially when most of the interactions happen behind a login.

@pluma I don‘t really know what you’re talking about here? What do you mean session cookies are restricted to logged in users? I’ve built cookie notice modals with cookies for non-authenticated users just fine.

I check if there’s a preferences cookie. If not, I display the notice. If the user accepts or rejects analytics cookies, then I store a cookie with their preference. You’re still allowed to use cookies for essential functionality, which this would class as, otherwise a user would be receiving the same annoying pop-up on every page they visit.

Snapey's avatar

@martinbean @pluma it is of course possible to create a route group that is for guests, does not send any cookies and does not show a cookie notice. If there are routes that require sessions then the cookie notice can be shown at that point.

Just bear in mind that the user might be referred directly to a page that requires sessions by a search link or user shared content.

martinbean's avatar

@Snapey I think, again, this is a misconception of the GDPR. The GDPR doesn’t say all cookies are bad and must require consent; you’re still allowed to use cookies for absolutely essential operation of the website or service.

Explicit consent is needed for cookies that process personal data. So it’s nefarious cookies like analytics and advertising cookies that trigger the need for consent, since they literally exist for the sole purpose of, “Who is this person? Because we want to know as much about them as possible” and not for utilitarian reasons like, “Does the current user of the computer want to view the site in light or dark mode“ or, “Is user with the ID of 1234 logged in?” There’s no personal information being transmitted in those.

pluma's avatar

@Snapey Yeah, that's the problem with "consent" though. You need to show the dialog before sending the cookie. Sure, the session cookie is technically exempt from this but it still feels rude given that the user did not have a chance to review what the cookie will store. I'm more okay with the separate CSRF cookie given that it doesn't serve to identify the user/session but merely authorize a form request itself. You also don't need to track whether the consent dialog has been shown with a cookie, you can do that with localStorage- unless they opt in, in which case you need to audit log their consent to allow them to opt out later.

I don't provide any functionality to logged out users besides reading plain HTML like the privacy policy so I don't want to send them session cookies. In the SPA world this is straightforward as session cookies (if any) are usually created by the login, in the SSR/backend world of course session cookies are widely seen as the default.

I'm not saying this is a GDPR compliance problem, I'm saying it feels iffy to me because the need to always send a session cookie is an implementation quirk, not an actual technical necessity (again, I'm not storing any data in it for logged-out users). However I've found a StackOverflow answer which seems promising, the answer seems to be to inherit and override the session middleware: https://stackoverflow.com/questions/78352481/prevent-laravel-11-from-setting-any-cookies-including-session-xsrf-cookies

pluma's avatar

@martinbean That's not entirely correct. "Is user with the ID of 1234 logged in" is transmitting personal information because the user ID acts as an alias or pseudonym. Even the mere association of the IP address itself with the request is personal information as far as the GDPR is concerned. But what I'm talking about is not the GDPR but the E-Privacy Directive (or rather its current precursor, the E-Privacy Guideline) and the German (could you tell I'm German?) TTDDG (formerly TTDSG, formerly TMG) which implements the various EU privacy laws. The GDPR concerns privacy and data owners' rights, E-Privacy concerns technological requirements.

You could make a case that session cookies are "absolutely necessary" (which is the approximate English translation for the German legal text) for the functioning of the website but my point is that I know that is not necessarily the case (i.e. the CSRF cookie suffices to verify a login request and I otherwise have no need to track a user across requests until they log in) and while it's a minor transgression that won't get anyone sued as long as they don't do anything nefarious with it, it's also something I should be able to easily avoid.

Snapey's avatar

@pluma Not sure what your issue is? I explicitly said that its quite possible to serve content to the user without requiring any cookies. But at some point you are going to need to ask them the question about accepting cookies. If they say no, where to store that preference?

I think stuffing it into local storage is just sidestepping semantics and probably worse than using a cookie.

Snapey's avatar

@pluma as for your reply to @martinbean you just dilute your arguments with every statement since most are incorrect.

pluma's avatar

@Snapey Stuffing it into localStorage means the information never reaches you (i.e. the server) which makes all the difference. My goal is data minimization, which btw is a core concept of the GDPR. I'm talking about following the spirit of the law, not just the letter. Privacy by default and all that. At some point I will need a session for some users, yes, but that doesn't justify doing it for all users.

I'm not looking to argue. This is a matter of personal opinion and attitude and Laravel's default is fully compliant (if not in a strict reading of the law then at least in practice when it comes to enforcement). I think the use of a "GDPR-compliant" US-based CDN in starters is a bigger issue given that Schrems has repeatedly proven that any data sharing agreements between the EU and US (e.g. Privacy Shield) are inherently impossible due to current US law.

I think you perceive me as attacking you or @martinbean. I'm not. Data privacy is a special interest of mine and I've read through both the laws themselves (again, German laws tend to be more narrow) and opinions from legal experts as well as talked to people who specialize in data protection and privacy laws. There is a lot of wiggle room in practice beyond what a strict reading of the law would mean in theory. As my concern is to follow the spirit of these laws (i.e. data minimization, avoid PII where possible, don't store PII longer than necessary, don't track users across requests unless identifying them is strictly necessary) I already acknowledged I'm holding my code to a higher standard than what may or may not get me dinged for non-compliance.

If you think "most" of my statements are incorrect, I'd welcome you to elaborate on that. I seem to be coming off as argumentative but I'm just trying to explain my reasoning and why you might want to do what I'm trying to do. I'm not extrapolating to a general rule of what you or anyone else should or shouldn't do, especially if you're not yourself in the EU. I think merely trying to be technically compliant or "compliant enough" is likely good enough for most purposes. But as I'm in the process of migrating an existing application to Laravel, I don't want to "do less" if the only reason is a technical implementation detail of the framework. And as I said, StackOverflow has pointed me in the right way to address that.

1 like
pluma's avatar

@Snapey FTR I still stand by everything I said in my first reply so I'm not sure how you think I'm "dilluting" that and I don't know what of it you think is "mostly incorrect".

In fact, @martinbean's first reply to me seems to be misunderstanding what I said. I said "when using session cookies there's no way to restrict session cookies to logged in users" (i.e. only create a session when a user logs in). Martin said "what do you mean session cookies are restricted to logged in users". That's the opposite of what I said and actually what I wanted to achieve, not what I described as being my gripe.

He also said "you're still allowed to use cookies for essential functionality" which I never disagreed with. What I said is that I want to avoid using cookies when I think they can be avoided. That's why I said "the session cookie is technically exempt but it still feels rude" (later emphasis added).

You mentioned

it is of course possible to create a route group that [..] does not send any cookies

... can you elaborate on that? Do you just remove the StartSession middleware for that route group? Or what else did you mean in concrete technical terms?

1 like
Snapey's avatar

@pluma You clearly understand the objectives of the regulations, but I'm not sure you understand the Laravel framework as much.

I suggested an option, you could create routes that do not issue any cookies. How this is achieved varies by version, but with the latest Laravel, probably easiest to follow instructions for adding a new routes file, eg guest.php and creating a middleware group that omits non-required features.

I don't recognise

I think the use of a "GDPR-compliant" US-based CDN in starters is a bigger issue

as being applicable to an out of box laravel implementation?

pluma's avatar

@Snapey

I'm not sure you understand the Laravel framework as much.

Obviously. I never claimed otherwise.

creating a middleware group that omits non-required features.

So in other words there's no built-in way to have "lazy sessions", i.e. not create a session (and clutter up the db) until we actually store something in it - and that's okay. I already qualified my initial statement that "there's no way" with a reference to a potential solution I found on StackOverflow - in the very same reply - so I guess a better way to express my gripe would have been "there's no obvious and well-documented way to do this": there's not a config option you can set or a side-note in the documentation. I don't want to "disable sessions" for some routes, I want to avoid creating sessions (and setting session cookies) when I don't have anything to store in them, which in my case means once a user logs in.

This is not a novel or outrageous concept. What Laravel is doing isn't wrong, either. There are different ways to handle this. Laravel does it one way, I want to do it another way and that requires a non-trivial amount of effort.

I don't recognise

I think the use of a "GDPR-compliant" US-based CDN in starters is a bigger issue as being applicable to an out of box laravel implementation?

Nevermind that, btw. I was thinking of Bunny CDN, which upon closer inspection seems to be based out of Slovenia. The address is listed at the very end of the privacy policy.

Snapey's avatar

@pluma As suggested, you create a guest.php routes file. In there, put your homepage and other routes that are accessible without needing sessions.

Then all the routes that need sessions go in the existing web.php file as normal

pluma's avatar

@Snapey I tried out the StackOverflow answer and it doesn't work. Unlike what the SO answer claimed, the StartSession middleware can't be replaced because it seems to be loaded by the session service provider.

I tried your solution (creating a separate middleware group) and while it does work in the sense that it does what you describe, it only allows me to completely opt out of the middleware stack and build my own from scratch. It doesn't, for example, let me make sessions non-persistent (opting out of cookies and session persistence) depending on the request cookies the way the StackOverflow question described it.

There are posts and questions about this with half-baked solutions going back to at least Laravel 4 and it looks like the Laravel session service has only become more integral to Laravel over time, making it harder and harder to modify. Given the overall flexibility of Laravel, I stand by what I said initially: I dislike that there is no easy way to prevent Laravel from storing sessions and sending cookies until I want it to (e.g. after login).

EDIT: I've found a solution on my own which works well enough for my purposes. This middleware, prepended to the web group, looks for the presence of a consent cookie and disables session persistence and the session & csrf cookies otherwise. It also allows for exclusions. YMMV:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Foundation\Http\Middleware\Concerns\ExcludesPaths;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Symfony\Component\HttpFoundation\Response;

class ConsensualCookies
{
    use ExcludesPaths;

    protected static $except = [];

    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $sessionCookie = Config::get('session.cookie');
        if (
            $request->cookies->has('consent') ||
            $request->cookies->has($sessionCookie) ||
            $this->inExceptArray($request)
        ) {
            return $next($request);
        }
        Config::set('session.driver', 'array');
        $response = $next($request);
        $response->headers->removeCookie('XSRF-TOKEN');
        $response->headers->removeCookie($sessionCookie);
        return $response;
    }

    /**
     * Get the URIs that should be excluded.
     *
     * @return array
     */
    public function getExcludedPaths()
    {
        return $this::$except ?? [];
    }

    /**
     * Indicate that the given URIs should be excluded.
     *
     * @param  array|string  $uris
     * @return void
     */
    public static function except(string|array $path): void
    {
        if (is_array($path)) {
            static::$except = array_merge(static::$except, $path);
            return;
        }
        static::$except[] = $path;
    }
}
1 like
Snapey's avatar

@pluma have you tried using the existing API.php routes file. This could prove the concept, but of course all routes will be prefixed /api/, but as a test, its a quick one.

pluma's avatar

@Snapey I've made some modifications to the middleware I posted but it's given me what I need. Only limitation is that it doesn't play nice with livewire because of the CSRF limitation. As the consent modal blocks interaction until dismissed or accepted, this is good enough for me as I'll just reload the page after submission, enabling CSRF tokens and sessions.

Please or to participate in this conversation.