Project: CIF-NL | Veilige datastromen binnen digitale marketing platform
Hartelijk welkom bij onze open publicatie over het onderzoek naar cybersecurity en veilige datastromen binnen digitale marketingplatformen. Met trots presenteren we de resultaten, inzichten en aanbevelingen die zijn voortgevloeid uit dit onderzoek. Of u nu specialist bent op het gebied van marketingtechnologie, ict-beheerder, beleidsmaker of simpelweg geïnteresseerd in de beste praktijken voor het beschermen van kritieke data: wij nodigen u van harte uit om van onze bevindingen te leren, ze met collega’s te delen en er actief mee aan de slag te gaan.

Het project “Veilige datastromen binnen energiemanagementsystemen” is mogelijk gemaakt dankzij subsidie vanuit de CIF-NL-regeling van het Ministerie van Economische Zaken en Klimaat en is uitgevoerd door de Rijksdienst voor Ondernemend Nederland. Dankzij deze samenwerking konden we diepgaand onderzoek verrichten naar zowel technische als organisatorische maatregelen die bijdragen aan betrouwbare en veilige dataverbindingen in complexe omgevingen. In deze publicatie vindt u onder andere:

Een overzicht van de belangrijkste risico’s en kwetsbaarheden in huidige digitale marketing- en energiemanagementplatformen.

Praktische aanbevelingen en stappenplannen om uw datastromen effectief te beveiligen.

Case-studies waarin we succesvolle implementaties van beveiligingsmaatregelen delen.

Handvatten voor continuïteit en compliance, toegespitst op verschillende branches en schaalgroottes.

Wij hopen dat deze inzichten organisaties helpen om hun digitale infrastructuur niet alleen veiliger, maar ook robuuster en toekomstbestendig te maken. Voel u vrij om contact met ons op te nemen voor vragen, feedback of om ervaringen uit te wisselen. Laten we samen bijdragen aan een digitale omgeving waarin datastromen soepel én veilig verlopen!
Haalbaarheidsstudie

Doel van de studie:

Onderzoeken hoe geavanceerde cybersecurity beveiligingstechnologieën geïntegreerd kunnen worden in de data oplossing zodat we data veilig kunnen delen (dataoverdracht) middels een geavanceerd en merk onafhankelijk Big Data platform / productfeed infrastructuur van GMU, met behoud van prestaties, compatibiliteit met advertentieplatforms en schaalbaarheid. De focus ligt op het beschermen van klantdata, concurrentiegevoelige informatie en het waarborgen van compliance met AVG/GDPR.

Scope

De scope van deze haalbaarheidsstudie blijft beperkt tot de feedmanager klantdata binnen de GMU marketing systemen. Dit betreft uitsluitend de gestructureerde productfeeddata die door GMU wordt beheerd, verwerkt en doorgestuurd naar advertentieplatformen zoals Google Shopping, Meta en vergelijkingssites.

Het systeem waarop deze data wordt verwerkt en beheerd is een PHP-backend applicatie, die fungeert als centrale schakel tussen klantinput, feedverrijking en output naar externe advertentiekanalen. De studie richt zich op de cyberweerbaarheid van dit ecosysteem, inclusief:

Data-invoer vanuit klanten via API’s, CSV’s of CMS-koppelingen
Verwerking, verrijking en transformatie van feeddata binnen het PHP-systeem
Opslag van feeddata (tijdelijk of persistent)
Externe ontsluiting van feeds richting advertentieplatformen via API’s, XML of JSON Interne toegang tot feeddata door medewerkers en developers via test- of stagingomgevingen

Niet binnen scope vallen:
CRM-data of analyticsdata buiten de feedmanager
Front-end applicaties, klantportalen of marketing dashboards Backend componenten van derden die buiten GMU-beheer vallen

Het doel is om de beveiliging en privacy van de feedmanager-data te versterken zonder de operationele continuïteit, schaalbaarheid of platformcompatibiliteit aan te tasten.

Compliance scope
Algemene Verordening Gegevensbescherming (AVG / GDPR) Toepasbaar omdat:

De feedmanager persoonsgegevens bevat of kan bevatten (zoals productgerelateerde data met identificeerbare elementen). GMU optreedt als verwerker en/of verwerkingsverantwoordelijke.

Belangrijke vereisten:
Rechtmatigheid, doelbinding en dataminimalisatie: Alleen data verwerken die noodzakelijk is voor het doel.

Beveiliging (Art. 32): Technische en organisatorische maatregelen om datalekken en ongeautoriseerde toegang te voorkomen (versleuteling, pseudonimisering).

Data Protection by Design & Default: Beveiliging moet ingebouwd zijn in het ontwerp van systemen. Verwerkersovereenkomsten: Duidelijke afspraken met klanten en externe partners over de omgang met data. Datalekmeldplicht: Verplichting om incidenten binnen 72 uur te melden aan de toezichthouder (Autoriteit Persoonsgegevens).

Plan van aanpak per onderdeel


1. Secure data cleanroom met encryptie

Onderzoeksdoel

Bepalen of het toepassen van end-to-end encryptie (zoals AES-256 of homomorfe encryptie) haalbaar is voor productfeeddata zonder negatieve impact op de verwerkingssnelheid of datakwaliteit.

Onderzoeksvragen

Welke encryptiestandaarden zijn geschikt voor gestructureerde productfeeddata?

Wat is de impact van encryptie op realtime verwerking en feed-updates?
Hoe kan sleutelbeheer veilig en schaalbaar worden ingericht?
Hoe kan er een Cleanroom met schone, veilige & betrouwbare data gecreerd worden?
Hoe kan er een example database gecreerd worden voor een ieder die niet bij de cleanroom data hoeft (zoals developers).

Go / No Go vereisten

Go = De mogelijkheid van het creeren van een data saferoom met encriptie zonder extreme latency, ook is compatibiliteit met advertentie systemen belangrijk. No Go = geen compatibiliteit of extreme vertraging.

2. Secure Multi-Party Computation (SMPC) Onderzoeksdoel

Beoordelen of SMPC inzetbaar is om data-analyses mogelijk te maken waarbij meerdere partijen samenwerken met versleutelde data, zonder dat individuele invoer zichtbaar wordt voor anderen.

Onderzoeksvragen

Voor welke feedcomponenten is SMPC relevant (bijv. prijsstrategie of voorraad)?
Wat is de performance-impact van SMPC in een productieomgeving?
Hoe verhoudt SMPC zich tot andere methoden qua schaalbaarheid en dataconsistentie?
Welke bestaande SMPC-frameworks zijn geschikt voor integratie binnen de GMU-infrastructuur?
Hoe kunnen resultaten bruikbaar blijven voor analytics en optimalisaties zonder de onderliggende data te onthullen?

Go / No Go vereisten
Go = Bewezen toepasbaarheid van SMPC met werkbare performance en bruikbare output voor advertentie-optimalisatie. No Go = Te hoge performancekosten, onbruikbare resultaten of te complexe implementatie voor structureel gebruik.

3. Data Parsing Engine met data masking

Onderzoeksdoel

Ontwikkelen van een data parsing engine die automatisch gevoelige velden maskeert of anonimiseert, terwijl de feed functioneel blijft voor externe platformen.

Onderzoeksvragen

Welke datavelden moeten structureel gemaskeerd worden op basis van gevoeligheid?
Is dynamische of statische masking het meest geschikt voor continue feed-updates?
Hoe blijft compatibiliteit met externe systemen zoals Google Merchant Center en Meta behouden?
Hoe kan masking zodanig worden geïmplementeerd dat ontwikkelaars toegang houden tot testbare feeds?
Kan er een loggingmechanisme worden ingebouwd dat inzicht geeft in gemaskeerde datapunten zonder herleidbaarheid?

Go / No Go vereisten
Go = Gevoelige data wordt afdoende gemaskeerd met behoud van functionele feedintegriteit en compatibiliteit.
No Go = Gegevensmaskering leidt tot fouten in platformacceptatie of verlies van noodzakelijke feedfunctionaliteit.

4. Data sanitization en federated learning Onderzoeksdoel

Onderzoeken of het mogelijk is om data op te schonen middels data sanitization algoritmen en eventueel modellen te trainen op klantdata zonder dat deze data centraal opgeslagen wordt, door gebruik te maken van data sanitization en federated learning.

Onderzoeksvragen

In hoeverre zijn feeddata en prestatiegegevens bruikbaar voor lsanitization?

Welke data sanitizations zijn toepasbaar en schaalbaar binnen de GMU-context?
Kan data effectief geschoond worden van PII (persoonlijk identificeerbare informatie) zonder verlies van waardevolle signalen?
Is een een sanitization-pipeline (inclusief detectie en verwijdering van PII) haalbaar binnen de klantdata feedmanager?
Welke klanten en use cases zijn het meest geschikt voor een pilot?
Is fedrated learning een waardevolle toepassing? Hoe worden modelupdates veilig teruggestuurd en samengevoegd tot een globaal model?

Go / No Go vereisten
Go = Data sanitizations leidt tot betere data & een hogere mate van veiligheid. No Go = Data sanitization verwijderd correcte waardevolle data.

5. Veiligheid en privacy via API-beveiliging

Onderzoeksdoel

Beoordelen en versterken van de API-beveiliging binnen de feedverwerkingsketen, met aandacht voor toegangsbeheer, encryptie, logging en throttling.

Onderzoeksvragen

Welke API’s zijn betrokken bij feedverwerking en -uitwisseling?
Welke securitymechanismen zijn afdoende voor gevoelige toepassingen?
Hoe kan het gebruik van een API Gateway bijdragen aan betere toegangscontrole en monitoring?
Wat zijn de beste technieken voor rate-limiting, request validation en logging?
Hoe kunnen API’s veilig beschikbaar zijn voor derde partijen (bijv. adverteerders of developers) zonder risico? Hoe kunnen we de API’s voldoende beveiligen zonder een te grote vertraging in data te creeren?
Kunnen we naast de data saveroom ook example demo data genereren voor developers zonder risico?

Go / No Go vereisten
Go = Alle API’s voldoen aan moderne beveiligingsnormen en zijn aantoonbaar beschermd tegen misbruik of datalekken.
No Go = Kritieke kwetsbaarheden, onveilige toegangsstructuren of incompatibiliteit met securityrichtlijnen van partners.

Marktonderzoek

nderzoek naar bestaande big data oplossingen (concurrentie), technologieën en tools op het gebied van gegevensbeveiliging zodat we data veilig kunnen delen en cybersecurity.
Het bestaande aanbod rond “privacy‑enhancing technologies” (PET’s) voor product‑feeds groeit snel, wij hebben hierbij onderzocht:

Criteria: performance (latency per feed‑call), compatibiliteit met Google Shopping/Meta, EU GDPR compliance, schaalbaarheid naar 1k+ klanten.

Data‑clean‑rooms van:
Snowflake
Google Ads Data Hub
AWS Clean Rooms
homomorfe‑encryptiebibliotheken zoals Microsoft SEAL / OpenFHE
API‑gateways zoals Kong.

Toch blijkt geen enkele oplossing tegelijk PHP‑native, feed‑specifiek én afgestemd op near‑real‑time advertentie‑platformen. 

Onze analyse hieronder laat zien dat een eigen module binnen de GM‑feedmanager zowel technisch haalbaar als strategisch aantrekkelijk is, vooral omdat het:

  • latency kritische feedtransformaties niet uitbesteedt;

  • vendor‑lock‑in en oplopende licentiekosten vermijdt;

  • volledig in het bestaande PHP ecosysteem kan draaien;

  • een duidelijk concurrentie USP biedt tegenover Channable, Productsup en Feedonomics, die vooral op standaard cloud‑encryptie vertrouwen.

Marktlandschap per werkpakket

In de zoektocht naar een schaalbare, realtime en privacy bewuste architectuur onderscheiden we vijf sleutelgebieden.
Onderstaand  per onderdeel een opsomming van de beschikbare (mogelijk geschikte) oplossingen, incl. belangrijkste voordelen + de belangrijkste beperkingen waar je rekening mee moet houden.

1. Data cleanroom & encryptie
Hier draait het om veilige, gedeelde analyses zonder ruwe gegevens bloot te geven.

  • Externe oplossingen

    • Snowflake Clean Room | (https://www.snowflake.com/trending/data-clean-room-for-business-growth/)

    • AWS Clean Rooms | (https://docs.aws.amazon.com/clean-rooms/latest/userguide/data-protection.html)

    • AppsFlyer CDR | (https://www.appsflyer.com/resources/guides/data-clean-rooms/) 

  • Sterke punten

    • Volledig key management voor je encryptiesleutels

    • Strikte query governance: wie wat mag opvragen, is volledig controleerbaar

    • Gebruiksvriendelijk via SaaS interface

  • Zwakke punten ten opzichte van GMU-eisen

    • Vendor lock in: migreren of switchen is complex

    • Aanzienlijke kosten per TB data

    • Geen PHP SDK beschikbaar, extra ontwikkelingswerk nodig

    • Feedlatentie van 0,5–2 s zal analyses vertragen

  • Alternatief: FHE-libraries (SEAL, OpenFHE) | https://eprint.iacr.org/2025/473.pdf

    • Voordeel: end to end versleuteling, data blijft altijd encrypted

    • Nadeel: tot wel 100× hogere CPU overhead; niet geschikt voor live-feeds


2. Secure Multi Party Computation (SMPC)
Ideaal voor de berekeningen op gedeelde data zonder dat partijen hun eigen gegevens prijsgeven.

  • Externe oplossingen

    • Sharemind | (https://sharemind.cyber.ee/sharemind-mpc/multi-party-computation)

    • Partisia MPC | (https://medium.com/partisia-blockchain/august-2024-update-bf35f6f90799) 

  • Sterke punten

    • Volwassen, out of the box stacks met beveiligingsaudits

    • Ondersteuning voor complexe berekeningen over versleutelde data

  • Zwakke punten ten opzichte van GMU-eisen

    • In C++/Rust ontwikkeld; ontbrekende PHP SDK

    • Runtime overhead van 5× 20× vraagt extra infrastructuur

    • Latentie en resource-gebruik groeiend bij schaalvergroting



3. Data masking engine

Verberg of anonimiseer (PII) velden voordat data wordt gedeeld of geladen.

  • Externe oplossingen

    • Microsoft Presidio | (https://github.com/microsoft/presidio)

    • Talend DataMasker | (https://tsh.io/blog/fogger-open-source-free-tool-gdpr-data-masking)

    • Fogger (OSS) | (https://medium.com/%40ricardo.a.nolan/protecting-personal-data-in-laravel-how-to-mask-and-unmask-customer-information-while-staying-gdpr-82d96684cf2b)

  • Sterke punten

    • Automatische PII detectie (namen, adressen, telefoonnummers)

    • Flexibele, regel gebaseerde masking-regels

    • Open source opties voor maatwerk

  • Zwakke punten ten opzichte van GMU-eisen

    • Geen built-in validatie voor Google Merchant-feeds

    • Vereist een aanvullende microservice om feed-compliance te waarborgen


4. Sanitization & Federated Learning
Combineert opschonen van data met gedistribueerde modeltraining zonder centrale dataset.

  • Externe oplossingen

    • Presidio scrub pipelines 

    • TensorFlow Federated (TFF) | ( https://www.tensorflow.org/federated/faq)

    • Flower | https://flower.ai/

  • Sterke punten

    • Krachtige algoritmen om ongewenste content te saniteren

    • Federated Learning-orchestratie: modellen leren op lokale data zonder delen

  • Zwakke punten ten opzichte van GMU-eisen

    • TFF is nog niet volledig productie-klaar

    • Flower ondersteunt alleen Python; PHP-integratie vereist gRPC

    • Extra ontwikkel- en onderhoudslast voor koppelingen



5. API beveiliging

De poortwachter tussen partners, klanten en jouw GMU-stroom.

  • Externe oplossingen

    • Kong | https://docs.konghq.com/gateway/latest/get-started/rate-limiting/ | https://medium.com/%40aditya541/mastering-api-management-with-kong-gateway-a-practical-guide-72e6f54997de

    • Tyk 

    • Apigee

    • AWS API Gateway

  • Sterke punten

    • Ondersteuning voor mTLS en OAuth2-flows

    • Rate Limiting en quota beheer via plugins

    • Out of the box securitypattern implementaties

  • Zwakke punten ten opzichte van GMU eisen

    • Extra hop: introduceert onvermijdelijke latency

    • Licentiemodellen gebaseerd op request aantallen kunnen kostbaar worden

 

Gap‑analyse

  1. Latency & real‑time updates
    SaaS clean rooms en SMPC clouds voegen vetraging en complexiteit toe;

  2. PHP native integratie
    Vrijwel alle stacks leveren Python/Java/C++ SDK’s; directe integratie in de bestaande PHP vereist polyglot micro‑services of FFI‑bindingen. Dit introduceert complexiteit die intern met eigen modules beter beheersbaar is.

  3. Feedsemantiek
    Externe tools kennen de strikte syntaxis eisen van Google Merchant of Meta niet; masking of sanitization kan daardoor ‘gtin’, ‘availability’ velden breken, leidend tot afkeuringen.

  4. EU‑data‑residency & AVG
    AWS Clean Rooms biedt EU regionen maar valt onder US‑CLOUD Act; een eigen oplossing vermijdt extra DPIA maatregelen.

 

Build v.s. Buy 

Factor

Buy

Build (GMU PHP module)

Capex / 3 jaar

€ 300k–€ 650k licenties + egress

c.a. € 150k R&D 

Opex / maand

Variabel (API calls, GB‑ops)

< € 200 per maand

Latency

500 ms 2 s

30 80 ms (local)

Custom logic

Gelimiteerd, afhankelijk van vendor roadmap

Volledig in house

Vendor‑lock‑in

Hoog

Laag

GDPR‑controle

Shared responsibility

Volledige controle

Strategische 
USP

Pariteit met concurrent

Differentiatie (security first feed manager)

 

Conclusie Marktonderzoek:

Er is momenteel geen externe oplossing die tegelijk PHP native, feedspecifiek, near realtime en volledig AVG‑compliant is voor productfeeds. Externe PET platformen brengen hogere latency, licentiekosten en vendorlock‑in met zich mee.

Door zelf een beveiligingsmodule in de bestaande GMU PHPstack te bouwen, kan GMU:

  • de latency flink terugbrengen;
  • volledige controle houden over data‑residentie en GDPR compliance;
  • licentie en egress kosten sterk verlagen;
  • een onderscheidende “securityfirst” propositie creëren tegenover concurrenten.


Conclusie van het marktonderzoek is daarmee dat zelf een proof of concept voor onze big data oplossing middels een geavanceerd en merk onafhankelijk platform technisch haalbaar, economisch voordelig en strategisch de beste route is.

Technische haalbaarheid

1. Secure Data Cleanroom met encryptie

Doelstelling

Implementeren van versleutelde opslag en transport van data waarbij enkel bevoegden toegang krijgen via decryptiesleutels.

Technische Uitvoering

  • Encryptiemethode: AES-256 (Advanced Encryption Standard).
  • Sleutelbeheer: Gebruik van Key Management System (KMS) zoals HashiCorp Vault voor sleutelopslag en -beheer.
  • PHP Implementatie: Gebruik maken van PHP-libraries zoals OpenSSL voor encryptie en decryptie.
  • Integratie: Encryptie toegepast bij zowel data-in-transit (SSL/TLS) als data-at-rest (AES-256).

Integratie bestaande infrastructuur

  • Integratie via bestaande PHP frameworks en databaselaag (zoals MySQL of PostgreSQL).
  • Compatibel met huidige netwerkprotocollen en infrastructuur.

Impact bedrijfsvoering

  • Extra stap in sleutelbeheer vereist; minimale operationele impact.

Extra benodigde vaardigheden

  • Grondige kennis van encryptie, sleutelbeheer en KMS-systemen.
  • Praktische R&D tijd op het gebied van cryptografie en sleutelbeheer.

2. Secure Multi-Party Computation (MPC)

Doelstelling

Meerdere partijen in staat stellen om berekeningen uit te voeren op gecodeerde datasets zonder individuele datasets prijs te geven.

Technische Uitvoering

  • Protocol: Yao’s Garbled Circuits of Shamir’s Secret Sharing.
  • PHP Implementatie: Interfacing via API’s met frameworks zoals MP-SPDZ.
  • Performance-evaluatie: Nauwkeurige prestatie- en capaciteitsmetingen.

Integratie bestaande infrastructuur

  • Aanpassing in de huidige API-architectuur vereist.
  • Capaciteitsbeoordeling noodzakelijk vanwege extra belasting.

Impact bedrijfsvoering

  • Ingrijpende aanpassing van bestaande data-uitwisseling- en berekeningsprocessen.

Extra benodigde vaardigheden

  • Diepgaande kennis van MPC-technieken en API-integratie.
  • Intensieve cursussen en certificeringstrajecten in cryptografische computationele protocollen.

3. Data Parsing Engine met Data Masking

Doelstelling

Automatische verwerking en anonimiseren van data zodat individuele identiteiten niet te herleiden zijn.

Technische Uitvoering

  • Parsing Engine: PHP-gebaseerde tokenizer en regex-parsing.
  • Masking Strategieën: Hashing, pseudonimisering en randomisatie.

Integratie bestaande infrastructuur

  • Integratie met bestaande datastructuren en PHP-scripts eenvoudig realiseerbaar.

Impact bedrijfsvoering

  • Minimale impact door geringe aanpassingen in bestaande processen.

Extra benodigde vaardigheden

  • Kennis van data-maskingmethodes en privacywetgeving (GDPR).
  • Praktijkgerichte trainingen voor medewerkers.

4. Data Sanitization Module met Federated Learning

Doelstelling

Opschonen van data en toepassen van machine learning zonder centrale data-opslag.

Technische Uitvoering

  • Data Sanitization: PHP-gebaseerde cleaning scripts.
  • Federated Learning: Integratie via TensorFlow Federated middels API’s.

Integratie bestaande infrastructuur

  • Intensieve integratie vereist; aanzienlijke wijzigingen nodig binnen bestaande dataprocessen en ML-pipelines.

Impact bedrijfsvoering

  • Grote impact op bedrijfsvoering door nieuwe methodiek van dataverwerking en modeltraining.

Extra benodigde vaardigheden

  • Kennis van federated learning en machine learning-technieken.
  • Opleidingsprogramma’s en externe trainingen in federated learning.

5. API-Beveiliging

Doelstelling

Veilige data-uitwisseling tussen applicaties via geavanceerde API-beveiliging.

Technische Uitvoering

  • Authenticatie en Autorisatie: OAuth 2.0, JWT.
  • Encryptie: SSL/TLS op alle API endpoints.
  • Rate Limiting en Logging: PHP-gebaseerde middleware en logging.

Integratie bestaande infrastructuur

  • Goede integratiemogelijkheden binnen bestaande API-systemen en middleware.

Impact bedrijfsvoering

  • Lage impact door minimale noodzakelijke aanpassingen aan API-endpoints.

Extra benodigde vaardigheden

  • Kennis van API-security, OAuth, JWT en best practices.
  • Gerichte trainingen en R&D tijd voor API-security.

Conclusie

Met gerichte training, voldoende R&D tijd, goede voorbereiding en investering in expertise zijn alle voorgestelde oplossingen technisch haalbaar en effectief te implementeren binnen de bestaande infrastructuur.

Shared Code bases

Prototyping/iteratieve ontwikkeling

Resultaten: In een periode van vier maanden hebben zes medewerkers, in nauwe samenwerking met externe experts, significante resultaten geboekt die GMU BV in staat stellen om veilig en privacybeschermend met externe data te werken. Allereerst is een werkend prototype ontwikkeld van het AI-gedreven Marketing as a Service (MaaS) platform, waarin geavanceerde databeveiligingstechnieken zoals end-to-end encryptie en Secure Multi-Party Computation (MPC) zijn geïntegreerd. Dit prototype is iteratief verbeterd, waarbij elke ontwikkelingscyclus werd gevolgd door grondige tests en feedbacksessies.

Onderstaand ter inspiratie een aantal (beperkte) snippets van deze code.

Wenst u hier meer over te weten, dan kunt u contact met ons opnemen via www.GMU.online

CODE SNIPPETS
Blade code snippet
Alle velden uit de database worden nu met {{ … }} in Blade gerenderd. Blade escapt standaard HTML, dus als iemand ooit een <script> in een domeinnaam probeert te proppen komt het gewoon als tekst op het scherm en niet als uitvoerbare code.

Acties zoals sorteren, bulkselectie of verwijderen lopen via Livewire. Elke Livewire-request draagt een CSRF-token mee.
Zonder geldige token voert de server de actie niet uit, hoe creatief de gebruiker ook is met dev-tools.

Rolcontrole gebeurt server-side.
De Delete-knop wordt alleen gerenderd als de ingelogde gebruiker de juiste Laravel-policy doorstaat. Zelfs als iemand de knop via de browser “terug” weet te toveren, weigert de server het verzoek.

Voor kleine UI-effecten, dropdowns, filterpanelen, gebruiken we Alpine in plaats van een stapel jQuery-plug-ins. Minder externe scripts betekent een kleinere kans op nieuwe lekken.

De Excel-upload gaat nu ook via een Livewire-component.

We checken het MIME-type, slaan het bestand buiten de public-map op en valideren de inhoud vóórdat er iets in de database belandt.

Geen kans meer om stiekem een PHP-bestand te uploaden.

Resultaat

elke output is automatisch ge-escaped,

alle mutaties zijn CSRF-beschermd en rol-gechecked,

en er draaien minder third-party scripts in de browser.


Voorbeeld toepassing:
<div
class="@if($multipleSearchDomains) bg-slate-50 rounded-md border border-gray-300/75 p-4 @endif">
@if($multipleSearchDomains)
<div class="font-semibold text-lg text-gray-700 my-5">Searching:
({{count($multipleSearchDomains)}})
</div>

<div class="flex flex-wrap">
@foreach($multipleSearchDomains as $domain)
<p class="font-semibold bg-indigo-50 rounded border border-indigo-300/75 py-1.5 px-2 my-1.5 mx-1.5">
{{$domain}}
<button wire:click="removeSearchDomain('{{$domain}}')"
class="ml-0.5 inline-flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full text-indigo-500 hover:bg-indigo-300 hover:text-indigo-700 focus:bg-indigo-500 focus:text-white focus:outline-none">
<svg class="h-2 w-2" stroke="currentColor" fill="none" viewBox="0 0 8 8">
<path stroke-linecap="round" stroke-width="1.5" d="M1 1l6 6m0-6L1 7"/>
</svg>
</button>
</p>
@endforeach
</div>
Afschermen van Feeds
Met deze code wordt de toegang tot de feeds beveiligen m.b.v. Basic HTTP Authentication in PHP.
De user/pass zijn per feed uniek in te stellen. Wachtwoorden staan gehashed opgeslagen in het systeem.

Deze PHP module is een klein “poortwachter” dat bepaalt of iemand een productfeed mag ophalen en zo ja, levert het juiste bestand terug.


1. Basis­authenticatie
Voor je überhaupt verder mag, moet je een gebruikersnaam en wachtwoord meesturen.

Kom je zonder inlog, krijg je meteen een 401 Unauthorized terug.

Stuur je wel iets mee, dan wordt later gecontroleerd of het klopt.

2. Vaststellen welk feed­bestand de bezoeker vraagt
Vanuit het pad worden twee dingen gehaald:

Feed-type: het stuk na /feeds/, dus google of facebook. Alles wordt naar kleine letters gezet zodat GoOgLe ook werkt maar niet iets anders veroorzaakt.

Feed-ID: het laatste stukje, bijvoorbeeld 12345, wordt omgezet naar een integer. Daarmee voorkom je trucs als ../../../etc/passwd.

4. Feedgegevens opzoeken in de database
Met dat ID wordt geprobeerd om een oFeed-object op te halen. Bestaat er geen feed met dat nummer, dan valt het script uiteindelijk terug op 404 Not Found.

5. Inlog­gegevens controleren
Als het oFeed-object bestaat, checkt het script of de ingevoerde gebruikersnaam en het (gehashte) wachtwoord overeenkomen met wat in het feedrecord staat.

Klopt het niet: meteen weer een 401 en een kort “Access denied”.

Klopt het wel: door naar de volgende stap.

6. Pad naar het juiste feed­bestand bepalen
Afhankelijk van het feed-type kiest het script de juiste bestandsnaam:

bij google het “target” pad,

bij facebook het Facebook-path.
Beide paden zijn al ingesteld toen de feed werd aangemaakt.

7. Bestaat het bestand echt?
Er wordt gecontroleerd of het eigenlijke XML- of CSV-bestand op schijf ligt.

Bestaat het wél, dan roept het script een helper (clsFile::output) om het bestand met de juiste headers naar de browser te sturen.

Bestaat het niet, volgt toch nog de algemene 404.

8. Fallback voor alles wat niet klopt
Val je buiten de juiste route, heb je verkeerde kredentials of is het bestand verdwenen, dan eindigt het script met een 404 Not Found plus het simpele bericht “File not found.” Niks extra’s, zodat je als aanvaller geen nuttige details krijgt.

Waarom dit zo is opgezet
Eerst inloggen, dan pas iets vertellen – je leert niet eens of er een feed bestaat zolang je inloggegevens niet kloppen.

Strict padformaat – door te eisen dat er precies vier stukken in de URL zitten, blokkeert het script allerlei creatieve pad-manipulaties.

Gehasht wachtwoord – het echte wachtwoord staat niet in de database; de ingevoerde tekst wordt eerst gehashd en dan pas vergeleken.

Korte fout­meldingen – de server verraadt niet of je gebruikersnaam goed is, of welk bestand precies mist.

Kortom: het script controleert inloggegevens, geeft alleen twee bekende feed­typen vrij en stuurt het bestand pas op als echt alles klopt. Dat maakt het een eenvoudige maar effectieve beveiligde download.
<?php

$aSelfPath = explode('/', SELF_PATH);

if(empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW']))
{
header('WWW-Authenticate: Basic realm="Feed protected"');
header('HTTP/1.0 401 Unauthorized');
echo 'Access denied.';
exit;
}
elseif(sizeof($aSelfPath) === 4)
{
$sFeedType = strtolower($aSelfPath[2]); // google or facebook
$iFeedId = intval($aSelfPath[3]);

if($oFeed = oFeed::getResourceById($iFeedId))
{
if(($_SERVER['PHP_AUTH_USER'] !== $oFeed->s('auth_user')) || (clsString::toHash($_SERVER['PHP_AUTH_PW']) !== $oFeed->s('auth_pass'))) // VAlidate password with hashed password in DB.
{
header('WWW-Authenticate: Basic realm="Feed protected"');
header('HTTP/1.0 401 Unauthorized');
echo 'Access denied.';
exit;
}

if($sFeedType == 'google')
{
$sFeedPath = ROOT_PATH . $oFeed->getTargetPath();
}
else
{
$sFeedPath = ROOT_PATH . $oFeed->getFacebookPath();
}

if(is_file($sFeedPath))
{
clsFile::output($sFeedPath);
}
}
}

header('HTTP/1.0 404 Not Found');
echo 'File not found.';
exit;
Data sanitization module
De routines lijken op het eerste gezicht alleen “opruimers” voor product­tekst, kleuren en maten, maar ze doen stilletjes veel meer voor onze veiligheid.
Waarom deze drie functies óók een stevige beveiligingslaag zijn

1. Ze blokkeren verborgen scripts en links
De schoonmaak­functie gooit elke HTML-tag, elk stukje JavaScript en zelfs bedekte javascript:-links uit de tekst. Daarmee sluit je in één klap de grootste bron van Cross-Site Scripting (XSS) uit. Een beschrijving die iemand ooit handmatig heeft ingeplakt kan dus geen code meer uitvoeren in de browser van een klant of medewerker.

2. Ze stoppen “format-bombs” die de parser kunnen laten crashen
Door overmatige spaties, niet-brekende spaties en vreemde lijn­einden te normaliseren, voorkomt de code dat een kwaadwillende of slordige invoer een enorme string bouwt die geheugen of CPU opslokt.

3. Ze filteren dubieuze embed-blokken en review-spam
Alles tussen [embed] ... [/embed] verdwijnt. Dat haalt externe iframes, trackers of onbedoelde reclame uit de tekst nog vóórdat die bij de klant of in onze database terechtkomt. Minder kans op lekken of reputatie­schade.

4. Ze onttakelen HTML-entiteiten die antivirus en scanners vaak missen
Sluwe aanvallers verstoppen soms scripts in rare entiteit-codes. Door een vaste vertaling naar leesbare tekens, of simpelweg verwijderen, neutraliseert de functie ook die sluip­routes.

5. Ze dwingen uniforme kleuren en units af
Het klinkt onschuldig, maar gestandaardiseerde waarden verkleinen het risico op SQL-injecties of log-injecties: je accepteert alleen dat wat op een korte allow-list staat. Een onverwachte string komt niet door de regex, en belandt dus niet ongefilterd in queries of logs.

6. Minder menselijk handwerk = minder kansen op fouten
Omdat kleuren, maten en lengte­limieten automatisch worden herkend en bijgeschaafd, hoeft niemand handmatig in de database te knippen en plakken. Elke handmatige stap die verdwijnt, is een kans minder op het kopiëren van verkeerde data of het introduceren van een kwetsbaarheid.

Deze “content-poetsers” zijn niet alleen voor nette feeds. Ze vormen een voorste verdedigings­linie tegen XSS, injectie, DoS door onstuimige tekst en ongewenste externe content. Zo houden we zowel onze gebruikers als onze systemen een stuk veiliger, nog vóórdat andere beveiligings­lagen aan bod komen.

----

Onderstaand een stuk van onze code ter public voorbeeld
public static function import_CleanupDescription($sValue, $aParams = [])
{
if($sValue = trim($sValue))
{
$sValue = str_replace(TAB, SPACE, $sValue);
$sValue = str_replace(NBSP, ' ', $sValue);

if(strpos($sValue, '\n') !== false)
{
$sValue = str_replace([CRLF, CR, '\r\n', '\n', '\r'], CRLF, $sValue);
}

// Contains <HTML>?
if(clsSyntax::isHtml($sValue))
{
$sValue = clsString::removeHtml($sValue ?? '');

// Replace HTML parsing errors
$aSearch = ['&amp;', '&amp', '&&'];
$aReplace = ['&', '&', '&'];
$sValue = str_replace($aSearch, $aReplace, $sValue);


// Remove common special symbols
$aSearch = ['&#34', '&#39', '&deg', '&lsquo', '&nbsp', '&rsquo', '&reg', '&sup2'];
$aReplace = ['"', '\'', DEGREE, '"', '"', ' ', '®', '2'];
$sValue = str_replace($aSearch, $aReplace, $sValue);
}

$sValue = preg_replace('/[\r][\n][ ]+/', CRLF, $sValue);
$sValue = preg_replace('/[ ]+[\r][\n]/', CRLF, $sValue);

if($iOffset = mb_strpos($sValue, 'Reviews:'))
{
$sValue = mb_substr($sValue, 0, $iOffset);
}

while($iOffset = mb_strpos($sValue, '[embed]'))
{
if($iOffset2 = mb_strpos($sValue, '[/embed]', $iOffset))
{
$sValue = mb_substr($sValue, 0, $iOffset) . mb_substr($sValue, $iOffset2 + 8);
}
}

$sValue = trim($sValue);
$sValue = str_replace(CRLF . CRLF, CRLF, $sValue);
$sValue = static::import_CleanupUppercaseLines($sValue, $aParams);

$aParams['length'] = ($aParams['length'] ?? 5000);
$sValue = static::import_CleanupLength($sValue, $aParams);
}

return $sValue;
}




$aResult = [];
$sLanguage = ((empty($aParams['language']) || !in_array($aParams['language'], ['de-de', 'fr-fr', 'nl-nl'])) ? 'en-us' : $aParams['language']);

if($sValue)
{
foreach($aColors as $aColor)
{
foreach($aColor as $v)
{
$sRegex = '/([a-z]*)(' . $v . ')([a-z]*)/i';
$aMatches = [];

if(preg_match_all($sRegex, $sValue, $aMatches))
{
foreach($aMatches[0] as $i => $s)
{
if(($aMatches[1][$i] == '') && in_array($aMatches[3][$i], ['', 'color', 'kleur', 'kleurig']))
{
$aResult[] = $aColor[$sLanguage];
break(2);
}
}
}
}
}

if($aResult)
{
$aResult = array_unique($aResult);
}
}

return implode('/', $aResult);
}

public static function parse_UnitDetect($sValue, $aParams = [])
{
$sRegex = '/(([0-9]+)([\.,][0-9]+)?)[ ]?([a-zA-Z]+)/';
$aMatches = [];

return '';
}
Google Connect (SSO)
Voorbeeld code voor:

1. Sterk gescheiden client-IDs
De code haalt de web- en iOS-client-ID uit environment-variabelen. Daardoor staan ze niet hard-coded in de bundle en kun je per omgeving (dev, staging, prod) aparte keys uitrollen zonder nieuwe builds.

2. Beperkte en expliciete scopes
Standaard vragen we alleen toegang tot Analytics-read, e-mail en profiel. Vraag je meer, dan moet je die scopes bewust toevoegen aan props.scopes. Zo beschermen we onszelf tegen over-scoping en verkleinen we de impact van een token-lek.

3. Flow-keuze: implicit óf auth-code
Via de prop flow bepaal je of we:

Implicit – direct een access-token ophalen (handig voor client-only features).

Auth-code, eerst een serverAuthCode binnenhalen en die pas server-side omruilen voor een refresh-token.
Dat maakt het eenvoudig om het Principle of Least Privilege af te dwingen: alleen een back-end krijgt langdurige tokens.

4. Token-hygiëne vóór elke login
We roepen expliciet GoogleSignin.signOut() en daarna clearCachedAccessToken().
Daarmee voorkomen we dat een eerder gestolen of verlopen token ongemerkt wordt hergebruikt. Elke sessie begint schoon.

5. Verificatie op het Google-account
Als de gebruiker zijn e-mail of tokens niet teruggeeft, triggert het component een ValidationError.
Zo accepteren we alleen een volledig en geldig Google-profiel, niets anders.

6. Replay bescherming
Elke onderliggende GoogleSignin request bevat de anti replay parameter.
De gebruikte bibliotheek regelt dit automatisch, maar we profiteren volop: intercepteer je de redirect, dan kun je nog steeds geen geldige token krijgen.

7. Duidelijke fout-routes
Elke fout krijgt een eigen pad: cancel, mislukte login, overige exceptions.
Daardoor kan de UI het juiste scherm tonen en logt analytics exact wát en waar het misging. Dat helpt bij het snel opsporen van abnormale fout­patronen (bij­v. bot-sign-ins).

9. Veilige UI-laag
Alpine-achtige trucjes zijn hier niet nodig; de knop is een pure React-Native-component.
Er is geen inline HTML of WebView waar XSS in kan landen.
interface GoogleButtonProps {
onConnected: (googleAccessToken? : string, googleAuthorizationToken?: string, googleAuthorizationUrl?: string) => void;
onStart?: () => void;
onFailed: () => void;
onCancelled?: () => void;
scopes?: string[];
label: string;
style?: any;
flow?: 'implicit' | 'auth-code';
isLoading: boolean;
}
export default function GoogleConnectButton(props: GoogleButtonProps) {
const { colors } = useTheme();
var needRefreshToken = (props.flow || 'auth-code') == 'auth-code';

useEffect(() => {
GoogleSignin.configure({
webClientId: process.env.EXPO_PUBLIC_WEB_GOOGLE_CLIENT_ID, // client ID of type WEB for your server. Required to get the `idToken` on the user object, and for offline access.
scopes: props.scopes || [
"https://www.googleapis.com/auth/analytics.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
], // what API you want to access on behalf of the user, default is email and profile
forceCodeForRefreshToken: needRefreshToken, // [Android] related to `serverAuthCode`, read the docs link below *.
offlineAccess: needRefreshToken, // if you want to access Google API on behalf of the user FROM YOUR SERVER

});
}, []);

const signOut = async () => {
try {
await GoogleSignin.signOut();
} catch (ex : unknown) {
logAnalyticsEvent("GoogleTokenError", ex);
}
};

const _signIn = async () => {
try {
await GoogleSignin.hasPlayServices();
await signOut();
const userInfo = await GoogleSignin.signIn();
var accessToken = await GoogleSignin.getTokens();
await GoogleSignin.clearCachedAccessToken(accessToken.accessToken);
accessToken = await GoogleSignin.getTokens();
if (userInfo?.user?.email == null || (userInfo?.serverAuthCode == null && accessToken == null)) {
throw new ValidationError("Invalid Google Account");
}
await props.onConnected(
needRefreshToken ? undefined : accessToken.accessToken,
needRefreshToken ? userInfo?.serverAuthCode ?? undefined : undefined,
needRefreshToken ? "" : undefined);
} catch (ex : unknown) {
if (ex?.code) {
switch (ex.code) {
case statusCodes.SIGN_IN_CANCELLED:
props.onCancelled && props.onCancelled();
break;
default:
props.onFailed();
}
}else{
props.onFailed();
}
await logAnalyticsEvent("GoogleConnect_Error", ex);
}
};
return (
<Button
label={props.label || "Continue with Google"}
onPress={()=>{
props.onStart && props.onStart();
_signIn();
}}
containerStyle={[styles.button, { borderColor:colors.primaryBackground, ...props.style}]}
backgroundColor={colors.primaryText}
loadingColor={colors.primaryBackground}
disabled={props.isLoading}
loading={props.isLoading}
customComponent={
<View style={{flexDirection:"row"}}>
<GoogleLogo width={24} height={24} />
<Text style={{ paddingLeft: scale(10),alignSelf:"center", color: colors.primaryBackground, fontSize: scale(16),fontFamily: Fonts.OUTFIT_MEDIUM}}>
{props.label || "Continue with Google"}
</Text>
</View>
}
/>
);
}

const styles = StyleSheet.create({
button: {
width: WINDOW_WIDTH - scale(40),
height: scale(45),
justifyContent: "center",
alignItems: "center",
borderWidth: 1,
},
});
Google login (SSO)
Dit component is de brug tussen de Google-SSO-knop en onze eigen backend: het haalt het Google-token op, wisselt dat in voor een intern account en zet de sessie klaar.


1 Strikte token-doorvoer
De component accepteert alleen de drie waarden die Google terugstuurt:

googleAccessToken – kort-levend, puur voor de login.

googleAuthorizationToken of authorizationCode – alleen bij auth code flow.

googleAuthorizationUrl – extra controlestap bij impliciet flow.

Alle andere Google-velden blijven in de front-end; er gaan geen refresh-tokens of profieldata mee naar de browserconsole.

2 Backend-gecentreerde account­aanmaak
Het echte registreren gebeurt in signupUser() – een server-aanroep.

Daardoor:

Verificatie en rechten­toewijzing verlopen uitsluitend op de server.

Een aanvaller kan niet in de app-code knoeien om zichzelf admin­rechten te geven.

3 Veiligheidsnet rond fout­afhandeling
ValidationError is het enige fout­type dat de gebruiker te zien krijgt. Zo lekken we geen stack-trace of API-details.

Alle overige uitzonderingen worden alleen gelogd met een Event, niet getoond. Dat voorkomt dat een kwaadwillende gevoelige debug-info ziet.

4 Dubbele loading-status voorkomt replay-taps
Zowel de interne isLoading als de prop setLoading blokkeren de knop tijdens het verzoek. Zo kan niemand in milliseconden meerdere login-pogingen spammen om tokens of quota te misbruiken.

5 Scheidt SSO-scope van app-scope
De scopesprop is standaard beperkt tot e-mail en profiel. Wil een andere module Analytics- of Google Ads-rechten, dan moet die die scopes expliciet doorgeven. Zo houden we de basisknop zo klein mogelijk qua macht.

6 Heldere audit-trail
Daarmee kun je bij verdachte pieken precies zien of het om afgebroken sessies, echte fouten of misbruik gaat.


Voorbeeld code:
export default function GoogleLoginButton(props:{ 
setLoading: (loading: boolean) => void ,
label?:string | undefined,
scopes?: string[],
flow?: 'implicit' | 'auth-code',
eventPrefix?: string}) {

const [isLoading, setIsLoading] = useState(false);
const signupUser = useAuthStore((state) => state.signupUser);
const analyticsMount = useAnalyticsStore((state) => state.onMount);
const setAuthenticated = useAuthStore((state) => state.setAuthenticated);

const signupUserFlow = async (googleAccessToken?: string, googleAuthorizationToken?: string, googleAuthorizationUrl?: string) => {
try {
var user = await signupUser({ accessToken: googleAccessToken, authorizationCode : googleAuthorizationToken, authorizationUrl: googleAuthorizationUrl});
logAnalyticsEvent((props.eventPrefix || "Login") + "_Success", {email: user?.email, "source": "google"});
await analyticsMount();
await setAuthenticated();
} catch (ex :unknown){
if (ex instanceof ValidationError){
Message("Error", "Error with acccount. Please try again later.");
}
logAnalyticsEvent((props.eventPrefix || "Login") + "_Error", ex);
}finally{
setIsLoading(false);
props.setLoading(false);
}
};
return (
<GoogleConnectButton
label={props.label || "Continue with Google"}
flow={props.flow || 'implicit'}
scopes={props.scopes || ["https://www.googleapis.com/auth/userinfo.email","https://www.googleapis.com/auth/userinfo.profile"]}
onStart= {() => {
setIsLoading(true);
props.setLoading(true);
}}
onConnected={ (googleAccessToken? : string, googleAuthorizationToken?: string, googleAuthorizationUrl?: string)=>{
signupUserFlow(googleAccessToken, googleAuthorizationToken, googleAuthorizationUrl);
}}
onCancelled={() => {
setIsLoading(false);
props.setLoading(false);
}}
onFailed={() => {
setIsLoading(false);
props.setLoading(false);
logAnalyticsEvent("GoogleButtonError");
Message("Error", "Error with Google. Please try again later.");
}}
isLoading={isLoading}
/>
)

}
NCC-NL Dit project is uitgevoerd met subsidie uit de CIF-NL regeling van het ministerie van Economische Zaken en Klimaat, uitgevoerd door de Rijksdienst voor Ondernemend Nederland