GDPR in web applications: how we build compliant systems
Author:
Paweł Matusiak
·
GDPR is not just a privacy policy in the footer. It is how the application collects, stores, shares and deletes data. In Laravel this can be designed from day one — cheaper than fixing it after an audit.
Many companies handle GDPR with a PDF in the footer. That is not enough when the app holds customer records, order history, documents, or medical and HR data. The regulation requires protection to be built into the system (privacy by design and by default — Article 25), not bolted on at the end as an I accept everything checkbox. In practice that means decisions at the first database table: which fields are needed, who sees them, how long they live and what happens after an erasure request.
GESOFT builds panels on Laravel partly because roles, logs, encryption and retention can live in code, not in a policy the app does not enforce. Below: what GDPR means for software (Articles 5 and 25), how we map that to consents, permissions, deletion and hosting, and what you cannot cheaply bolt on once the database has grown. We do not replace a law firm — we say what the system must do so a lawyer has something to sign.
What GDPR means in software practice
Regulation 2016/679 is not a list of technologies. It is a list of principles. Article 5 requires lawfulness and transparency, purpose limitation, minimisation, accuracy, storage limitation, integrity and confidentiality, and accountability. Article 25 requires technical and organisational measures already when you determine the means of processing — taking into account the state of the art, the cost of implementation and the risk. Privacy by default: by default you process only what is necessary for each specific purpose, in amount, extent, storage period and accessibility.
- Minimisation — collect only what you really need for a stated purpose. A national ID in a newsletter form is not needed. A date of birth in a B2B shop — usually not either.
- Legal basis — consent, contract, legal obligation or legitimate interest must be clear and demonstrable. Invoicing has one basis, a newsletter another.
- Purpose limitation — contact-form data is not later used for other marketing without consent. The app should not have one I agree to everything bucket.
- Access and erasure (Articles 15 and 17) — a user can get a copy of their data and ask for deletion, with exceptions (for example tax retention).
- Portability (Article 20) — an export in a structured format, not a broken PDF from three tables at once.
- Security (Article 32) — encryption in transit, access control, backups, the ability to restore, breach reporting (Article 33: 72 hours to the authority).
Special categories (Article 9) — health, biometrics, trade-union membership — need an extra basis and tighter access control. That is why panels for clinics, HR and insurance are designed differently from a simple vis-card CRM: narrower roles, separate consents, a shorter path to logs. We do not pretend a cheap shared host closes that topic.
How this looks in a Laravel application
Consents and a processing record
Marketing consents and terms are stored with date, version text and source (form, panel, import, a note a staff member recorded). A single I accept everything checkbox is not enough. Processing under a contract, newsletter and profiling are separate — if profiling is needed at all. Withdrawing consent is as easy as giving it: one click in the customer panel or an email that actually works, not an address nobody reads.
A record of processing activities (Article 30) is the controller duty, not the software house duty. We supply the raw material: which datasets sit in the database, which fields, which retentions, which recipients (courier, payment gateway, SMS). Without that a lawyer draws the record from memory while the app lives a separate life.
Roles instead of one password for everything
Office staff should not see payroll. Accounting does not need the full chat history with a client. Clinic reception does not need test results it does not handle. Laravel roles and policies limit access to specific resources on the API, not only by hiding a menu item. That is one of the most effective ways to reduce the impact of a leak — an attacker with a low-privilege account does not take the whole database. It is also A01 from the OWASP Top 10, not only a GDPR paragraph.
Right to erasure and anonymisation
Delete account must not leave a name, email and phone number in five tables plus logs plus an old export on the accountant disk. We design the path: what is deleted, what is anonymised (to keep invoices and documents required by law — in Poland ledgers and invoices live for years, not weeks), what remains in backups and for how long. Backups have retention too: a backup kept forever with full personal data is a second, forgotten dataset.
That has to be decided before the database grows to hundreds of thousands of rows. After the fact, anonymisation is a script everyone is afraid to run on production and three weeks of checking whether a notes table still holds a national ID. It is cheaper to have columns and cascades thought through at the start.
Logs, but not too many
An audit log (who changed the customer address) is necessary for accountability and for Article 32. Logging full national ID numbers, password contents or a medical message body is not. Passwords never go to logs. Sensitive data is masked or not stored at all. Log retention is a separate decision: too short and you cannot reconstruct an incident, too long and you keep a dataset you cannot justify.
Breaches, 72 hours and what the system must do
Article 33 requires notifying the supervisory authority without undue delay, where feasible within 72 hours of becoming aware. The app will not send that notice for you, but it can make you know: which account, what scope, whether the data was encrypted, whether people can be informed (Article 34). Without logs and without an inventory of datasets, the 72 hours vanish into guessing. That is why panels that hold special-category data get an alert on a mass export, on an admin login from a new IP and on a burst of failed password resets from day one.
Encryption in transit (TLS) is the minimum. Encryption of selected fields at rest — where a leaked database (dump, backup, a stick at the accountant) would hurt most. Laravel has encrypted casts on models; the key must not sit in the same dump as the data. Backups off the production server, with a tested restore — the same points we make in a security audit.
What you cannot cheaply bolt on later
Disk encryption and an SSL certificate can be added at any time. It is much harder to add a coherent consent model, retention, on-demand export, role separation and an audit trail. That is why in new Laravel applications we plan these on analysis — before the first database table exists. An old panel can be improved (HTTPS, backups, login limits), but the right to be forgotten on a database with no foreign keys and no decision about what is an invoice versus marketing is a project on its own.
- A single gdpr_consent = 1 field says nothing about which terms version it covered or whether it included the newsletter.
- A shared office account makes it impossible to show who disclosed data — and breaks accountability.
- An export everything to CSV without 2FA is not a gift to accounting, it is a ready leak vector.
- Logs with passwords or full national IDs are worse than no logs: the dataset itself is a breach waiting for a backup copy.
Privacy by design in analysis, not in an annex
Article 25 does not tell you to buy a certificate. It tells you to think about data when you draw the screen. In the kickoff we ask: why this field, who will see it, how many months it lives, whether we can avoid collecting it. If nobody can defend a national ID on a Facebook lead, the field is not in the MVP. If reception must see a name and a time, not a diagnosis, the API does not return the diagnosis to that role. That is cheaper than masking a column in twenty reports later.
Default settings are a design decision too. A new user does not have the newsletter pre-ticked. A CSV export is not in every salesperson menu. The session expires. Firma2024 is not an accepted password. Backups do not land on the accountant private Drive. Privacy by default means convenient everything on is a conscious exception, not a theme preset. In Laravel those presets live in code and policies, not in a PDF nobody reads when the next feature lands.
We build systems for industries where data is especially sensitive: healthcare, law firms, HR, insurance. Login and 2FA are covered separately in Laravel authentication, because that is the most common way a stranger enters a record. If you need a GDPR-ready app or a review of an existing panel, contact us. We will tell you what is a real risk and what is just cosmetic wording in the policy. The quote follows a description of datasets and roles — no catalogue price for a GDPR pack. Source code and 6 months of warranty — as in every GESOFT project.
Article 6 and 9 bases — not one checkbox
GDPR Article 6 lists bases: consent, contract, legal obligation, vital interests, public task, legitimate interest. An invoice and an order account are usually contract and bookkeeping duty — not a marketing consent the customer failed to tick. A newsletter and profiling are consent or (less often) legitimate interest with a balancing test and easy objection. Combining I accept the terms and I want emails in one field is a classic mistake. In the database we store a basis per purpose, a date, the version text and a source, not a gdpr=1 flag.
Article 9 (special categories: health, biometrics, trade union, sexuality) needs a separate condition and tighter control. A clinic, HR or insurance panel is not a CRM with an extra field. Reception sees a time slot, not a diagnosis. A full-record export is not in every role menu. A DPIA (Article 35) is considered when scale, a new technology or special-category data raise risk — not as a certificate you buy, as a controller document the system feeds with facts: which datasets, which recipients, which retentions.
Cookies, e-Privacy and what GDPR does not settle alone
A cookie banner is not the whole of GDPR. Browser identifiers, tracking and marketing tags also fall under e-Privacy / national e-comms rules. Analytics without identifying a person (anonymised, local, not handed to ad tools) can be justified differently from a remarketing pixel. On panel apps we often set no marketing cookies at all: an HttpOnly session is enough to log in. We do not sell compliance as a banner plugin on a Laravel app that has no AdSense anyway.
Transfers outside the EEA (Chapter V): standard contractual clauses, a country assessment, sometimes extra measures. A cheap bucket in a non-EU region because it is cheaper does not pass if customer records sit inside. Payment and SMS gateways seated outside the EEA must be named in the DPA and in the notice to the person. That is a legal checklist plus DNS/region config, not philosophy. GESOFT panel hosting — EU servers, HTTPS, admin access logged.
DPIA, DPO and where a software house stops
A data protection officer and the record of processing (Article 30) sit with the controller. We supply raw material: a dataset dictionary, fields, retentions, recipients (courier, e-invoicing, SMTP, SMS), logs of who exported. We do not sign as your DPO. We work with your law firm if you have one. A processing agreement (Article 28) is mandatory when we host or have production access — a DPA template, not a footer on the invoice. We do not hide sub-processors.
The 72 hours in Article 33 run from becoming aware of a breach, not from when we find time. The system should be able to say: which account, what scope, whether the data was encrypted, whether people can be informed (Article 34). Without logs and an inventory those hours vanish into guessing. That is why an alert on a mass export, a new admin IP and a burst of password resets is designed into sensitive panels from day one, together with 2FA and access control. Git and 6 months of warranty — as in every project, not as a catalogue GDPR pack.
Frequently asked questions
- Is a privacy policy in the footer enough for GDPR?
- No, if the app holds records, orders or medical data. You need consents in the database, roles, erasure, retention and security — not only a PDF. Article 25 requires privacy by design, not privacy by footer.
- Where do you keep the data?
- On EU servers, HTTPS in transit. If we host, we sign a data processing agreement (DPA, Article 28). We do not hide sub-processors (payments, SMS, mail).
- Can GDPR be bolted onto an old panel?
- Some of it (SSL, backups, login limits). Consents, retention and export get expensive once the database has grown. Cheaper to design on day one. An audit shows what can be patched.
- Does GDPR apply to a tiny firm?
- Anyone who processes data of people in the EU — including a sole trader with a customer list. The scale of duties depends on the type of data and the risk, not on whether you have a legal department. Health and HR data raise the bar.
- Must I delete invoices if a customer asks?
- Not at the expense of tax and accounting duties. Invoices and documents the law tells you to keep are anonymised on the marketing layer and retained on the ledger layer for the required period. We set that path in analysis, not ad hoc on the first request.
- Are the newsletter and the customer account the same consent?
- No. A contract (account, order) is a different basis from marketing. A combined I accept the terms and I want emails box is a classic mistake. In the panel we split them and store the version text.
- What about backups after account deletion?
- A backup must not be a forever second dataset. We set backup retention and the fact that after it expires a restore no longer contains the deleted row. Instant erasure from every tape is often unrealistic — so the retention policy must say so in plain language.
- Does GESOFT replace a Data Protection Officer?
- No. We deliver a system you can defend: consents, roles, logs, a DPA, export. Legal decisions, the processing record and a DPO stay with the controller. We work with your lawyer if you have one.
Related service:
Laravel and Vue.js applications
Describe your project