De ‘privacy by design’ cookiebanner handleiding
"Nog veel cookies zonder toestemming" , aldus de Consumentenbond in maart van dit jaar. Hoog tijd voor een aantal praktische tips en trucs. In dit (longread) blog leg ik uit aan welke juridische vereisten een maatwerk cookiebanner moet voldoen en hoe je de banner met Google Tag Manager (conditioneel) kan inladen.
De reden om voor een maatwerk banner te gaan kan verschillen en is persoonlijk. Je zou kunnen kiezen voor een kant-en-klare consent management oplossing, zoals Cookiebot of CookieFirst. Ik ben liever volledig in-control over de implementatie van het tag management systeem en de (conditionele) cookie-voorkeuren. Zo kan je het ontwerp van de banner beter af stemmen op dat van de website.Voeg als laatste stap de
De minimale eisen...
Een cookiebanner moet voldoen aan een aantal juridische, functionele en technische eisen. De eisen zijn slechts gedeeltelijk een kwestie van smaak vanwege de doorslaggevende invloed van de wet. Er is desondanks nog steeds behoorlijk wat ruimte over voor een eigen (creatief) ontwerp. Mijn doel is om concrete handvatten te geven waarmee je uiteindelijk een eigen banner kan maken en implementeren. Dit zijn naar mijn mening de belangrijkste ontwerpeisen om rekening mee te houden:- Design = gebruikte typen cookies en technieken: Het ontwerpen van een eigen banner stelt je in staat om het design heel specifiek af te stemmen op de gebruikte cookies en andere technieken Lees mijn blog over de 'andere technieken' om te begrijpen welke dit onder meer zijn.. Er moet in de ontwerpfase dan wel rekening worden gehouden met de gebruikte technieken en de regels. Oftewel, ontwikkel je banner privacy by design.
- Broncode los van website te onderhouden: De broncode van de cookiebanner en die van de website moet los van elkaar te onderhouden zijn. Dit wordt ook wel separation of concerns genoemd. Dit design principe zorgt voor meer begripsmatige eenvoud, want hiermee deel je de business logica op in meer behapbare code-brokken.
- Integratie met tag management: De scripts, pixels en andere vergelijkbare technieken moeten via een tag management systeem/plugin worden ingeladen. Het gebruik van tag management is min of meer standaard in de marketingbranche. Het gebruik van tag management stelt de eigenaar van een website bijvoorbeeld in staat om een online marketingbureau meer specifieke toegang te geven. Dus enkel toegang tot de pixels/tags in plaats van de complete backend of het CMS.
Design kaders en het soort banner (informeren of toestemming?)
De belangrijkste eisen aan banners volgen uiteraard uit de wet, namelijk artikel 11.7a Telecommunicatiewet (= ‘cookiebepaling’) en/of de AVG. Er zijn grofweg twee categorieën banners, namelijk een:- informatie banner waarmee een website alleen (gedeeltelijk) voldoet aan haar informatieplicht De hoofdregel bij gebruik van cookies: de website of app moet voorzien in “... duidelijke en volledige informatie overeenkomstig de [AVG], in ieder geval over de doeleinden waarvoor deze informatie wordt gebruikt…”. (artikel 11.7a lid 1 Tw);
- consent banner waarmee naast het informeren ook om toestemming wordt gevraagd.
Design kaders voor toestemming
Onze fictieve website beoogt om in ieder geval een persoonlijke banner te laten zien, welke op het eerdere surfgedrag is gebaseerd. Dit betekent dat toestemming volgens de vereisten van de AVG noodzakelijk is.Om toestemming te vertalen naar design kaders is begrip vereist van de juridische achtergrond. Er is sprake van toestemming bij een vrije, specifieke, geïnformeerde en ondubbelzinnige (actieve) wilsuiting. Daarnaast moet de gebruiker toestemming net zo eenvoudig kunnen intrekken als deze is verstrekt. Ook moet de website of app eigenaar (als verantwoordelijke) kunnen aantonen dat toestemming geldig is verstrekt. Wat betekenen deze zes vereisten in de praktijk voor cookies met toestemming? En wat betekenen de eisen specifiek voor onze dummy website die personalisaties wil laten zien?1. Vrije keuze
De website of app gebruiker moet wel een echte vrije keuze hebben. Dit raakt ook aan de discussie over de zogenaamde ‘cookiewall’. AP: “Een cookiewall houdt in dat mensen die een website willen bezoeken of app willen gebruiken, de vraag krijgen om cookies te accepteren voordat zij toegang krijgen tot de website. Geven zij geen toestemming, dan krijgen zij geen toegang.” Er wordt per land verschillend gedacht over cookiewalls. De Nederlandse AP is bijvoorbeeld van mening dat er niet sprake kan zijn van een vrije keuze, terwijl de Franse rechter dit weer anders ziet Conseil d’Etat N. 434684 19 juni 2020. Gelet op de onduidelijkheid zou ik adviseren om nu geen cookiewall te gebruiken. Mogelijk wordt dit met de komst van de e-privacyverordening weer anders (voor bijv. de journalistieke sector).2. Specifiek
Een website moet per doeleinde (en niet per cookie) toestemming verzoeken. Er kan niet sprake zijn van een algemene geformuleerd doel, zoals ‘het verbeteren van de dienstverlening’. Hoe doe je dit? Verzamel je informatie over het surfgedrag? Zo ja waarvoor en hoe verbeter je welke dienstverlening? Wat ook kan helpen is een voorbeeld in je doelomschrijving.In de praktijk lijkt het niet uitgesloten dat een betrokkene voor meerdere doeleinden op hetzelfde moment toestemming geeft. Bij cookies zou dit kunnen met een ‘alles accepteren’ knop. Het moet dan wel duidelijk zijn voor de gebruiker. De gebruiker moet ook de optie hebben om per doeleinde (granulair) zijn voorkeuren kenbaar te maken. Pas hier wel mee op. Is het te billijken dat een gebruiker met één druk op de knop voor meer dan 10 keuzes (=/= checkboxes) akkoord geeft? Je moet dan wel heel specifiek beschrijven welke doeleinden de gebruiker accepteert. Dit levert begripsmatig een lastige spagaat op, want hoe beknopt kan je deze all-in-one-toestemming nu echt goed uitleggen. Als je dit principe toch wil gebruiken dan zou ik mijzelf afvragen: kan de gebruiker dit redelijkerwijs verwachten en is de tekst dus voldoende duidelijk?3. Geïnformeerd
Op het moment van toestemming moet het al helder zijn voor welke doeleinden de gegevens worden verzameld en verwerkt. Dit kan worden verduidelijkt in het privacybeleid of op een andere pagina, maar de informatie welke essentieel is voor de keuze van de gebruiker moet ook in de banner staan. De Autoriteit Persoonsgegevens geeft aan dat de volgende informatie minimaal noodzakelijk is:- de soorten persoonsgegevens die je via de cookies verzamelt en verwerkt;
- de doeleinden van de gegevensverwerking;
- de categorieën bedrijven waaraan je de gegevens verstrekt;
- de bewaartermijn;
- nadere informatie die nodig is om je bezoekers een eerlijk beeld te geven van de gegevensverwerking.
4. Ondubbelzinnig (actief)
De gebruiker moet met een actieve handeling akkoord geven. Dit is bijvoorbeeld een akkoord-knop met als begeleidende tekst “Door op akkoord te klikken stemt u in met het gebruik van cookies voor doeleinde X”. Het gebruik van vooraf aangevinkte checkboxes of radiobuttons is ieder geval geen optie.5. Aanpasbaar
De gebruiker van je website of app heeft het recht om toestemming op elk moment weer in te trekken. Het intrekken van toestemming moet even eenvoudig zijn als het geven ervan. In de praktijk zie je vaak twee opties, welke allebei correct zijn. Dit kan met een link ergens op de website of een instellingen-knop onderaan de pagina. Na het klikken op de link of knop verschijnt de banner opnieuw. Wat is niet voldoende? Dit is een uitleg in je cookiebeleid hoe je middels de (developer) instellingen van de browser cookies etc. kan verwijderen. Hoe doet dit recht aan het vereiste dat intrekken ‘even eenvoudig’ moet zijn als geven? Of vraag je de gebruiker ook om de cookies zelf aan te maken via de dezelfde instellingen? Dan wel.6. Aantoonbaar proces
De eigenaar van de website (of app) moet kunnen aantonen dat toestemming geldig is gegeven. Er kan worden gedacht aan (extra) logging. In de praktijk is dit geen optie. Je moet dan (bijvoorbeeld) IP-adressen verzamelen enkel om aan te kunnen tonen dat iemand toestemming heeft gegeven. Dit kan niet de bedoeling zijn geweest van de wetgever.Logischer lijkt het om een geautomatiseerd proces in te richten, waarvan de integriteit en compliance met de toestemmingseisen kan worden aangetoond. Dit doe je door de wijzigingen in de programmatuur van de banner bij te houden. In praktijk kan je dit bijvoorbeeld borgen met versie management in Github en Google Tag Management. Hiermee heb je dan bewijs over de geldigheid van het toestemmingsproces, zoals deze in het verleden werd aangeboden.Design kaders voor onze banner
De hiervoor genoemde toestemmingseisen resulteren in een aantal algemene design kaders. Voor een website welke enkel gebruik maakt van technieken voor personalisatie van advertenties en content zijn dit specifiek de belangrijkste kaders:- Geen cookiewall. De gebruiker mag weigeren en nog steeds toegang krijgen tot de website.
- Toelichting per doeleinde en geen checkboxes/opties. In dit geval is er maar één doeleinde, namelijk ‘personalisatie van advertenties en content aan de hand van het surfgedrag’.
- De toelichting bestaat uit de onderwerpen benoemd onder ‘geinformeerd’ (zie hiervoor).
- De gebruiker drukt (actief) op een akkoord-knop, waaruit duidelijk blijkt dat degene toestemming geeft voor het omschreven doeleinde.
- De gebruiker kan toestemming op een later moment weer (eenvoudig) aanpassen.
- Versiebeheer in Github en Google Tag Management
Broncode en werking banner
Voor dit blog heb ik een eenvoudige banner gemaakt, waarover kort het volgende:- De (toestemming) banner voldoet aan de hiervoor uitgewerkte design kaders.
- De broncode is open-source (MIT Licentie) beschikbaar.
- De inhoud van de banner is eenvoudig aan te passen. De broncode bevat een voorbeeld van de wijze waarop de inhoud banner concreet kan worden ingeladen en aangepast. Dit voorbeeld is te vinden in het
index.html
bestand onder'static' -> 'live-test-site'
. - Een live versie van de banner is hier te raadplegen.
Banner in GTM installeren
De laatste stap is het integreren van de banner in Google Tag Management (GTM). Hiermee bepaalt de website eigenaar welke (script)tags en pixels pas na akkoord (en/of een andere voorwaarde) mogen worden ingeladen. In essentie werkt dit stapsgewijs als volgt:- Bij elk bezoek aan de website wordt GTM ingeladen.
- GTM vuurt het ‘All Pages’ trigger af en vervolgens wordt de cookiebanner ingeladen.
- De broncode van de banner heeft twee modi, namelijk het tonen van een cookiebanner of instellingen-knop.
- GTM vuurt een custom marketing trigger af waarmee kan worden vastgesteld of marketing content mag worden ingeladen.
Stap 1: GTM installeren
Om GTM op je website te installeren moet je eerst een account aanmaken en vervolgens een web container. Doe dit eerst en ga dan verder met deze stap. In de container onder‘beheer’ → ‘Google Tag Manager installeren’
zijn de instructies te vinden waarmee GTM aan je website kan toevoegen. Dit betreft het toevoegen van een script-element aan het head-element en een noscript-element gelijk na het opening body-tag.Let op! De installatiemethode kan afhankelijk van je codebase ook anders worden uitgevoerd. Bij Wordpress raad ik aan om GTM (wanneer dit kan) met een plugin te integreren in je website. Een Single Page Application (bijv. React, Angular of Svelte) heeft z’n eigen gebruiken en eigenaardigheden om rekening mee te houden.Stap 2: Cookiebanner tag aanmaken
De cookiebanner kan worden ingeladen via een HTML-fragment. Je moet hiervoor een aantal variabelen en een tag aanmaken. Dit werkt als volgt:1) We voegen eerst twee variabelen toe. Klik op‘Variabelen’
en dan 'Nieuw'
en...- ...noem de eerste variabele
‘CDN-DOMAIN (const var)’
en vul hier het volgende in:
- ...noem de tweede variabele
‘COOKIE-DOMAIN (const var)’
en vul hier het volgende in:
'HTML ID'
en 'Container ID'
. Deze variabelen worden gebruikt Het 'banner script' is met deze variabelen ook geschikt voor 'tag-sequencing'. Hierdoor kan je bijvoorbeeld een cleanup script afvuren nadat de cookiebanner is ingeladen. In dit blog van Simo Ahava meer informatie. in het script waarmee de cookiebanner wordt ingeladen. Je activeert deze onder 'Variabelen' -> 'Configureren'
.3) De volgende stap is het toevoegen van het cookiebanner script als tag.- Dit doe je door in je container onder
‘Tag’ → ‘Nieuw’
te klikken. Geef deze tag de naam‘Consent Banner (add)’
. - Vervolgens klik je op ‘
Tag-Configuratie’
en dan selecteer je de optie‘Aangepaste HTML’
. - Plak in de text-editor de volgende code:
<script id="consent-bundle" async>
(function () {
var config = {
content: {
"nl-nl": {
main: {
contentParagraph: [
"We maken gebruik van cookies en vergelijkbare technieken om onze diensten en advertenties te leveren, te onderhouden en te verbeteren. We verzamelen hiervoor ",
{
type: 'personal-data-popover',
text: 'informatie',
title: 'Welke informatie verzamelen we over je surfgedrag?',
list: [
{
name: 'Identifiers betreffende je apparaat',
contentParagraphs: [
'Dit zijn identifiers welke worden opgeslagen in een cookie.',
]
},
{
name: 'Gebruiksinteracties',
contentParagraphs: [
'Met informatie over interacties wordt bedoeld:',
{
type: 'list',
list: [
'de weergave van specifieke pagina’s;',
'alle scroll-, touch-, klikacties;',
'specifieke gebeurtenissen, zoals het delen of lezen van een artikel; of',
'de doorgebrachte tijd op een pagina of de website als geheel.'
]
},
'Alle interacties worden gekoppeld aan een tijdstip en datum.'
]
},
{
name: 'Browser- en Apparaatgegevens',
contentParagraphs: [
'Bij een bezoek aan de website wordt er informatie over je browser en apparaat verzameld. Het gaat dan in ieder geval om:',
{
type: 'list',
list: [
'het type-/versie browser, apparaat of bestuurssysteem (user-agent);',
'de gebruikte link om op deze website te komen (referrer);',
'de taalinstelling van de browser (accept-language);',
'de scherm- en venstergrootte;',
'de laadtijd van een pagina; en',
'je onnauwkeurige locatiegegevens op het niveau van land, regio en/of woonplaats.'
]
},
]
}
]
},
" over je surfgedrag. Klik op ",
{
type: 'consent-popover',
text: "akkoord ",
title: 'Waarvoor geef je akkoord?',
purposeLists: [
{
type: 'first-party',
purpose: 'Het gebruik van je surfgedrag voor het tonen van meer relevante content en advertenties op onze website. Bijvoorbeeld:',
list: [
'een banner op de homepage welke aansluit op je eerdere surfgedrag',
'...'
]
},
{
type: 'third-party',
purpose: 'Voor het tonen van gepersonaliseerde advertenties delen we gegevens met:',
list: [
{
name: 'Facebook',
privacyNotice: 'https://www.facebook.com/about/privacy'
},
{
name: 'Google Ads',
privacyNotice: 'https://policies.google.com/technologies/ads?hl=nl'
},
{
name: 'Twitter',
privacyNotice: 'https://twitter.com/en/privacy'
}
]
}
]
},
" als je wilt dat we de content en advertenties die je ziet aan de hand van je surfgedrag personaliseren. Meer informatie in het ",
{
type: 'inline-url',
text: 'privacy beleid',
url: "/privacy"
},
"."
],
btnOoptions: "Weigeren",
btnAgree: "Akkoord"
}
}
},
settings: {
domain: '{{COOKIE-DOMAIN (const var)}}',
path: '/',
secure: true,
httpOnly: false,
maxAge: 15552000,
sameSite: 'none'
}
};
window.addEventListener("biscuit-init", function () {
window._biscuit(config)
});
var gtm = window.google_tag_manager[{{Container ID}}];
function addContainer() {
var container = document.createElement('div');
container.id = "consent";
document.body.appendChild(container);
}
if (document.getElementById("consent-style") === null) {
addContainer();
} else {
var container = document.getElementById("consent-style");
container.parentElement.removeChild(container);
}
if (document.getElementById("consent-style") === null) {
var style = document.createElement('link');
style.id = "consent-style";
style.rel = "stylesheet";
style.href = "{{CDN-DOMAIN (const var)}}/bundle.css";
style.media = "print";
style.addEventListener('load', function () {
style.media = 'all'
});
document.body.appendChild(style);
}
if (document.getElementById("gtm-consent-script") !== null) {
var el = document.getElementById("gtm-consent-script");
el.parentElement.removeChild(el);
}
var el = document.createElement('script');
el.id = "gtm-consent-script";
el.async = true;
el.src = '{{CDN-DOMAIN (const var)}}/bundle.js';
el.addEventListener('load', function () {
gtm.onHtmlSuccess({{HTML ID}});
});
document.head.appendChild(el);
})();
</script>
- Klik hierna op
‘Trigger’
en vervolgens‘Alle Pagina’s’
. Sla vervolgens de nieuwe marketing trigger op.
Stap 3: Instellingen of banner tonen
Bij deze stap zijn geen verdere aanpassingen in de instellingen van GTM noodzakelijk. Na weigering of akkoord zet het cookiebanner-script de waarde van het ‘showBanner’ cookie op 0 (false). In dat geval wordt er niet een banner maar een instellingen-knop links onderin het scherm getoond.Stap 4: Marketing tags laden
Alleen na akkoord is de waarde van het ‘pref’-cookie ‘marketing=1’, waarbij de '1' gelijk staat aan true ('akkoord'). Deze waarde willen we uitlezen in GTM, zodat de deze kan worden gebruikt voor het conditioneel inladen van bijvoorbeeld marketing gerelateerd tags. De beoogde cookies, tags en vergelijkbare technieken moeten uiteraard wel voldoen aan het doeleinde waarvoor toestemming is gegeven. Dit werkt als volgt.1) Maak een nieuwe variabele aan en noem deze‘MARKETING-PREFERENCE (cookie var)’
. Als type variabele selecteer je ‘Direct Cookie’
. Vul hier vervolgens het volgende in:2) Om een marketing tag conditioneel af te vuren is het noodzakelijk om een trigger aan te maken. Dit hebben we nog niet eerder gedaan. Ga naar ‘Triggers’ → ‘Nieuw’
. Noem de trigger ‘Marketing (Page View)’
en stel de volgende instellingen in:De volgende stap is het toevoegen van de 'marketing tag' Dit tag is vervangen door die van Facebook, Google Ads en andere tags waarvoor je toestemming moet hebben.. Maak hiervoor een tag aan van het type Aangepaste HTML en kopieer de volgende code in het veld:<div id="personal" class="wordart rainbow">
</div>
<style>
.wordart {
top: 400px;
font-family: Arial, sans-serif;
font-size: 4em;
font-weight: bold;
position: relative;
z-index: 1;
display: inline-block;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.wordart.rainbow {
transform: scale(1, 1.5);
-webkit-transform: scale(1, 1.5);
-moz-transform: scale(1, 1.5);
-o-transform: scale(1, 1.5);
-ms-transform: scale(1, 1.5);
}
.wordart.rainbow .text {
background: red;
background: -webkit-linear-gradient(left, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af);
background: -o-linear-gradient(left, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af);
background: -moz-linear-gradient(left, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af);
background: linear-gradient(to right, #b306a9, #ef2667, #f42e2c, #ffa509, #fdfc00, #55ac2f, #0b13fd, #a804af);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>
<script type="text/javascript">
var txt = "<p class=\"text\">Your device info:</p>"
txt+= "<p class=\"text\">Browser CodeName: " + navigator.appCodeName + "</p>";
txt+= "<p class=\"text\">Browser Name: " + navigator.appName + "</p>";
txt+= "<p class=\"text\">Browser Version: " + navigator.appVersion + "</p>";
txt+= "<p class=\"text\">Cookies Enabled: " + navigator.cookieEnabled + "</p>";
txt+= "<p class=\"text\">Platform: " + navigator.platform + "</p>";
txt+= "<p class=\"text\">User-agent header: " + navigator.userAgent + "</p>";
document.getElementById("personal").innerHTML=txt;
</script>
'Marketing (Page View)'
als trigger toe en sla je tag op. Test vervolgens de werking van je banner. Alleen bij akkoord krijgt de gebruiker een (niet erg...) ‘persoonlijke’ marketing banner te zien.