Personvernlovgivning, samtykke og databehandling.
Som utvikler har du et stort ansvar når du bygger systemer som håndterer personopplysninger. GDPR (General Data Protection Regulation) er EUs personvernforordning som gir strenge regler for hvordan persondata skal behandles.
I Norge heter loven Personopplysningsloven, som implementerer GDPR. Brudd på loven kan føre til bøter på opptil 4% av global omsetning eller 20 millioner euro - det som er høyest.
Men personvern handler ikke bare om å følge loven. Det handler om å respektere brukernes rettigheter og bygge tillit.
Personopplysninger er all informasjon som kan knyttes til en identifiserbar person:
Direkte identifikasjon:
- Navn, personnummer, fødselsdato
- E-postadresse, telefonnummer
- Foto, fingeravtrykk, stemmeopptak
Indirekte identifikasjon:
- IP-adresse
- Cookie-ID
- Stedsinformasjon
- Kombinasjon av data (alder + bosted + yrke)
Særlige kategorier (ekstra beskyttelse):
- Helse
- Politisk oppfatning
- Religion
- Fagforeningsmedlemskap
- Seksuell orientering
- Genetiske/biometriske data
Grunnprinsipper i GDPR:
1. Lovlighet, rettferdighet og åpenhet
Brukere skal vite hva data brukes til.
2. Formålsbegrensning
Innsamle data kun for spesifikke formål.
3. Dataminimering
Samle inn kun det som er nødvendig.
4. Riktighet
Hold data oppdatert og korrekt.
5. Lagringsbegrensning
Behold data kun så lenge det er nødvendig.
6. Integritet og konfidensialitet
Beskytt data mot uautorisert tilgang.
7. Ansvarplikt
Kunne dokumentere at du følger reglene.
Lovlig behandlingsgrunnlag:
For å behandle personopplysninger må du ha ett av disse grunnlagene:
1. Samtykke: Bruker har gitt eksplisitt tillatelse
2. Avtale: Nødvendig for å oppfylle en avtale
3. Rettslig forpliktelse: Lovpålagt
4. Vitale interesser: Nødvendig for å beskytte liv
5. Offentlig interesse: Utføre offentlig oppgave
6. Legitime interesser: Avveining mot brukers rettigheter
7 prinsipper:
1. Proaktiv, ikke reaktiv
Forebygg problemer før de oppstår.
2. Personvern som standardinnstilling
Brukere skal være beskyttet automatisk, uten å måtte konfigurere noe.
# Eksempel: Standard-settings
default_settings = {
'profile_visibility': 'friends', # IKKE 'public'
'data_sharing': False, # IKKE True
'notifications': 'minimal' # IKKE 'all'
}3. Personvern bygget inn i design
Ikke et tillegg, men en integrert del.
4. Full funksjonalitet
Ikke kompromiss mellom personvern og funksjonalitet - du kan ha begge.
5. Sikkerhet gjennom hele livssyklusen
Fra innsamling til sletting.
6. Synlighet og gjennomsiktighet
Brukere skal kunne se hva som skjer med dataene deres.
7. Respekt for brukerens personvern
Brukervennlig og brukerorientert.
Praktiske tiltak:
Dataminimering i praksis:
# DÅRLIG: Samler unødvendig data
class BrukerRegistrering:
def __init__(self):
self.fornavn = ""
self.etternavn = ""
self.fodselsdato = ""
self.personnummer = ""
self.adresse = ""
self.postnummer = ""
self.telefon = ""
self.epost = ""
# BEDRE: Kun det nødvendige
class BrukerRegistrering:
def __init__(self):
self.brukernavn = ""
self.epost = ""
# Resten er valgfritt eller hentes når nødvendigAnonymisering og pseudonymisering:
import hashlib
# Pseudonymisering: Kan reverseres med nøkkel
def pseudonymiser(bruker_id, secret_key):
return hashlib.sha256(
f"{bruker_id}{secret_key}".encode()
).hexdigest()
# Anonymisering: Kan ikke reverseres
def anonymiser_data(bruker_data):
return {
'alder': bruker_data['alder'] // 10 * 10, # Aldersgruppe
'postnummer': bruker_data['postnummer'][:2] + '00', # Område
'kjonn': bruker_data['kjonn']
# Fjern navn, personnummer, etc.
}1. Frivillig: Ikke tvunget eller vilkår for tjeneste (hvis ikke nødvendig)
2. Spesifikt: For hvert formål
3. Informert: Bruker vet hva de samtykker til
4. Utvetydig: Aktiv handling, ikke forhåndsavkrysset
5. Tilbaketrekkbart: Lett å trekke tilbake
Dårlige eksempler:
<!-- IKKE GYLDIG: Forhåndsavkrysset -->
<input type="checkbox" name="samtykke" checked>
Jeg godtar vilkårene
<!-- IKKE GYLDIG: Uklart -->
<input type="checkbox" name="samtykke">
Jeg godtar at mine data kan brukesGode eksempler:
<!-- GYLDIG: Spesifikt og utvetydig -->
<input type="checkbox" name="nyhetsbrev">
Jeg vil motta nyhetsbrev med produktoppdateringer (valgfritt)
<input type="checkbox" name="markedsforing" required>
Jeg godtar at mine kontaktopplysninger lagres for å kunne kontakte meg om min ordre (nødvendig)Implementere samtykke:
from datetime import datetime
class Samtykke:
def __init__(self, bruker_id):
self.bruker_id = bruker_id
self.samtykker = {}
def gi_samtykke(self, formal, beskrivelse):
self.samtykker[formal] = {
'gitt': datetime.now(),
'beskrivelse': beskrivelse,
'tilbakekalt': None
}
# Logg for å dokumentere
logg_samtykke(self.bruker_id, formal, 'GITT')
def trekk_tilbake_samtykke(self, formal):
if formal in self.samtykker:
self.samtykker[formal]['tilbakekalt'] = datetime.now()
logg_samtykke(self.bruker_id, formal, 'TILBAKEKALT')
# Slett data knyttet til dette formålet
slett_data_for_formal(self.bruker_id, formal)
def har_samtykke(self, formal):
if formal not in self.samtykker:
return False
samtykke = self.samtykker[formal]
return samtykke['tilbakekalt'] is NoneInformasjonsplikt (personvernerklæring):
Du MÅ informere brukere om:
1. Hvem er behandlingsansvarlig (din bedrift)
2. Hva samler du inn (hvilke data)
3. Hvorfor (formål med behandlingen)
4. Hvordan (behandlingsgrunnlag - samtykke, avtale, etc.)
5. Hvor lenge (lagringstid)
6. Hvem andre får tilgang (databehandlere, tredjeparter)
7. Rettigheter (innsyn, sletting, etc.)
Cookie-samtykke:
<div id="cookie-banner">
<p>Vi bruker cookies for å forbedre din opplevelse.</p>
<button onclick="acceptEssential()">Kun nødvendige</button>
<button onclick="acceptAll()">Aksepter alle</button>
<button onclick="showSettings()">Innstillinger</button>
<a href="/personvern">Les mer</a>
</div>
<script>
function acceptEssential() {
// Lagre kun nødvendige cookies
setCookie('consent', 'essential', 365);
hideBanner();
}
function acceptAll() {
// Lagre alle cookies
setCookie('consent', 'all', 365);
// Aktiver sporingsscripts
enableAnalytics();
hideBanner();
}
</script>GDPR gir brukere en rekke rettigheter som du må støtte i systemet ditt:
1. Rett til innsyn
Brukere kan be om kopi av alle data du har om dem.
@app.route('/api/min-data', methods=['GET'])
@krever_autentisering
def eksporter_mine_data():
bruker_id = request.bruker['id']
# Samle alle data om bruker
data = {
'profil': hent_profil(bruker_id),
'innlegg': hent_innlegg(bruker_id),
'kommentarer': hent_kommentarer(bruker_id),
'aktivitetslogg': hent_aktivitet(bruker_id),
'samtykker': hent_samtykker(bruker_id)
}
# Returner som JSON
return jsonify(data)2. Rett til retting
Brukere kan kreve at feil data rettes.
@app.route('/api/profil', methods=['PUT'])
@krever_autentisering
def oppdater_profil():
bruker_id = request.bruker['id']
nye_data = request.get_json()
# Valider og oppdater
oppdater_bruker(bruker_id, nye_data)
return jsonify({'message': 'Profil oppdatert'})3. Rett til sletting ("rett til å bli glemt")
Brukere kan be om at data slettes.
@app.route('/api/slett-konto', methods=['DELETE'])
@krever_autentisering
def slett_konto():
bruker_id = request.bruker['id']
# Slett alle data
slett_profil(bruker_id)
slett_innlegg(bruker_id)
slett_kommentarer(bruker_id)
slett_tokens(bruker_id)
# Anonymiser data som må beholdes (f.eks. fakturahistorikk)
anonymiser_fakturaer(bruker_id)
# Logg sletting
logg_sletting(bruker_id)
return jsonify({'message': 'Konto slettet'})4. Rett til dataportabilitet
Brukere kan få data i maskinlesbart format for å flytte til annen tjeneste.
@app.route('/api/eksporter', methods=['GET'])
@krever_autentisering
def eksporter_data():
bruker_id = request.bruker['id']
# Eksporter i standard format (JSON)
data = hent_alle_data(bruker_id)
# Returner som nedlastbar fil
response = make_response(jsonify(data))
response.headers['Content-Disposition'] = \
'attachment; filename=mine-data.json'
return response5. Rett til innsigelse
Brukere kan protestere mot visse typer behandling.
6. Rett til begrensning
Brukere kan kreve at behandling begrenses mens en sak undersøkes.
Her er et komplett eksempel på hvordan du implementerer brukersletting i henhold til GDPR:
from datetime import datetime
from flask import Flask, request, jsonify
import sqlite3
app = Flask(__name__)
def logg_hendelse(hendelse_type, bruker_id, detaljer):
"""Logger GDPR-relevante hendelser for dokumentasjon"""
db = sqlite3.connect('gdpr_logg.db')
db.execute(
'''
INSERT INTO gdpr_logg (tidspunkt, type, bruker_id, detaljer)
VALUES (?, ?, ?, ?)
''',
(datetime.now(), hendelse_type, bruker_id, detaljer)
)
db.commit()
db.close()
@app.route('/api/slett-konto', methods=['DELETE'])
@krever_autentisering
def slett_konto():
bruker_id = request.bruker['id']
# Be om bekreftelse
bekreftelse = request.json.get('bekreftelse')
if bekreftelse != 'JEG VIL SLETTE MIN KONTO':
return jsonify({
'error': 'Må bekrefte sletting'
}), 400
db = sqlite3.connect('app.db')
cursor = db.cursor()
try:
# 1. Slett profil og kontaktinfo
cursor.execute('DELETE FROM profiler WHERE bruker_id = ?', (bruker_id,))
logg_hendelse('SLETT', bruker_id, 'Profil slettet')
# 2. Slett innhold opprettet av bruker
cursor.execute('DELETE FROM innlegg WHERE bruker_id = ?', (bruker_id,))
cursor.execute('DELETE FROM kommentarer WHERE bruker_id = ?', (bruker_id,))
logg_hendelse('SLETT', bruker_id, 'Innhold slettet')
# 3. Slett filer (profilbilde, opplastinger)
filer = cursor.execute(
'SELECT filsti FROM filer WHERE bruker_id = ?',
(bruker_id,)
).fetchall()
for fil in filer:
slett_fil_fra_disk(fil[0])
cursor.execute('DELETE FROM filer WHERE bruker_id = ?', (bruker_id,))
logg_hendelse('SLETT', bruker_id, f'{len(filer)} filer slettet')
# 4. Slett autentisering
cursor.execute('DELETE FROM tokens WHERE bruker_id = ?', (bruker_id,))
cursor.execute('DELETE FROM sesjoner WHERE bruker_id = ?', (bruker_id,))
# 5. Trekk tilbake alle samtykker
cursor.execute(
'''
UPDATE samtykker
SET tilbakekalt = ?
WHERE bruker_id = ? AND tilbakekalt IS NULL
''',
(datetime.now(), bruker_id)
)
logg_hendelse('SAMTYKKE_TRUKKET', bruker_id, 'Alle samtykker trukket')
# 6. Anonymiser data som må beholdes
# (f.eks. bestillinger for regnskapsloven - 5 års oppbevaringsplikt)
cursor.execute(
'''
UPDATE bestillinger
SET navn = 'SLETTET BRUKER',
epost = 'slettet@example.com',
adresse = NULL,
telefon = NULL,
ip_adresse = NULL
WHERE bruker_id = ?
''',
(bruker_id,)
)
logg_hendelse('ANONYMISER', bruker_id, 'Bestillinger anonymisert')
# 7. Slett selve brukerkontoen (sist, pga foreign keys)
cursor.execute('DELETE FROM brukere WHERE id = ?', (bruker_id,))
logg_hendelse('SLETT', bruker_id, 'Brukerkonto slettet')
db.commit()
# 8. Varsle andre systemer (f.eks. nyhetsbrev-tjeneste)
fjern_fra_nyhetsbrevliste(request.bruker['epost'])
# 9. Send bekreftelse på e-post (før data slettes!)
send_sletting_bekreftelse(request.bruker['epost'])
return jsonify({
'message': 'Konto og data slettet',
'tidspunkt': datetime.now().isoformat()
})
except Exception as e:
db.rollback()
app.logger.error(f"Feil ved sletting av bruker {bruker_id}: {e}")
return jsonify({'error': 'Kunne ikke slette konto'}), 500
finally:
db.close()
@app.route('/api/data-eksport', methods=['GET'])
@krever_autentisering
def data_eksport():
"""Implementerer rett til dataportabilitet"""
bruker_id = request.bruker['id']
db = sqlite3.connect('app.db')
db.row_factory = sqlite3.Row
# Samle all data
profil = db.execute(
'SELECT * FROM profiler WHERE bruker_id = ?',
(bruker_id,)
).fetchone()
innlegg = db.execute(
'SELECT * FROM innlegg WHERE bruker_id = ?',
(bruker_id,)
).fetchall()
kommentarer = db.execute(
'SELECT * FROM kommentarer WHERE bruker_id = ?',
(bruker_id,)
).fetchall()
samtykker = db.execute(
'SELECT * FROM samtykker WHERE bruker_id = ?',
(bruker_id,)
).fetchall()
db.close()
# Konverter til JSON-vennlig format
data = {
'eksportert': datetime.now().isoformat(),
'profil': dict(profil) if profil else None,
'innlegg': [dict(i) for i in innlegg],
'kommentarer': [dict(k) for k in kommentarer],
'samtykker': [dict(s) for s in samtykker]
}
# Logg eksport
logg_hendelse('EKSPORT', bruker_id, 'Data eksportert')
response = make_response(jsonify(data))
response.headers['Content-Disposition'] = \
'attachment; filename=mine-data.json'
return responseOppsummering
I dette kapittelet har du lært:
- Dataminimering: samle bare det nødvendige.
- Pseudonymisering vs. anonymisering: reverserbar mot ureverserbar.
- GDPR i praksis: standardinnstillinger og brukersletting.
- Dataetikk: ansvarlig bruk av persondata.
- Innebygd personvern: privacy by design.
Noekkelbegreper
| Begrep | Forklaring |
|---|---|
| Dataminimering | Samle bare nødvendige data |
| Pseudonymisering | Kan reverseres med nøkkel |
| Anonymisering | Kan ikke reverseres |
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.