Personvernlovgivning, samtykke og databehandling.
Som utviklar har du eit stort ansvar når du byggjer system som handterer personopplysningar. GDPR (General Data Protection Regulation) er EU si personvernforordning som gir strenge reglar for korleis persondata skal handsamast.
I Noreg heiter lova Personopplysningslova, som implementerer GDPR. Brot på lova kan føre til bøter på opptil 4% av global omsetning eller 20 millionar euro - det som er høgast.
Men personvern handlar ikkje berre om å følgje lova. Det handlar om å respektere rettane til brukarane og byggje tillit.
Personopplysningar er all informasjon som kan knytast til ein identifiserbar person:
Direkte identifikasjon:
- Namn, personnummer, fødselsdato
- E-postadresse, telefonnummer
- Foto, fingeravtrykk, stemmeopptak
Indirekte identifikasjon:
- IP-adresse
- Cookie-ID
- Stadinformasjon
- Kombinasjon av data (alder + bustad + yrke)
Særlege kategoriar (ekstra vern):
- Helse
- Politisk oppfatning
- Religion
- Fagforeiningsmedlemskap
- Seksuell orientering
- Genetiske/biometriske data
Grunnprinsipp i GDPR:
1. Lovlegheit, rettferd og openheit
Brukarar skal vite kva data blir brukt til.
2. Formålsavgrensing
Samle inn data kun for spesifikke formål.
3. Dataminimering
Samle inn kun det som er nødvendig.
4. Rettleik
Hald data oppdatert og korrekt.
5. Lagringsavgrensing
Behald data kun så lenge det er nødvendig.
6. Integritet og konfidensialitet
Vern data mot uautorisert tilgang.
7. Ansvarsplikt
Kunne dokumentere at du følgjer reglane.
Lovleg handsamingsgrunnlag:
For å handsame personopplysningar må du ha eitt av desse grunnlaga:
1. Samtykke: Brukar har gitt eksplisitt løyve
2. Avtale: Nødvendig for å oppfylle ei avtale
3. Rettsleg plikt: Lovpålagt
4. Vitale interesser: Nødvendig for å verne liv
5. Offentleg interesse: Utføre offentleg oppgåve
6. Legitime interesser: Avveging mot rettane til brukaren
7 prinsipp:
1. Proaktiv, ikkje reaktiv
Førebygg problem før dei oppstår.
2. Personvern som standardinnstilling
Brukarar skal vere verna automatisk, utan å måtte konfigurere noko.
# Eksempel: Standard-settings
default_settings = {
'profile_visibility': 'friends', # IKKE 'public'
'data_sharing': False, # IKKE True
'notifications': 'minimal' # IKKE 'all'
}3. Personvern bygd inn i design
Ikkje eit tillegg, men ein integrert del.
4. Full funksjonalitet
Ikkje kompromiss mellom personvern og funksjonalitet - du kan ha begge.
5. Tryggleik gjennom heile livssyklusen
Frå innsamling til sletting.
6. Synlegheit og gjennomsikt
Brukarar skal kunne sjå kva som skjer med dataa deira.
7. Respekt for personvernet til brukaren
Brukarvennleg og brukarorientert.
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: Ikkje tvinga eller vilkår for teneste (hvis ikkje nødvendig)
2. Spesifikt: For kvart formål
3. Informert: Brukar veit kva dei samtykkjer til
4. Utvetydig: Aktiv handling, ikkje førehandsavkryssa
5. Tilbaketrekkbart: Lett å trekkje tilbake
Dårlege eksempel:
<!-- 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 eksempel:
<!-- 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 brukarar om:
1. Kven er handsamingsansvarleg (di bedrift)
2. Kva samlar du inn (kva for data)
3. Kvifor (formål med handsaminga)
4. Korleis (handsamingsgrunnlag - samtykke, avtale, etc.)
5. Kor lenge (lagringstid)
6. Kven andre får tilgang (databehandlarar, tredjepartar)
7. Rettar (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 brukarar ei rekkje rettar som du må støtte i systemet ditt:
1. Rett til innsyn
Brukarar kan be om kopi av alle data du har om dei.
@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
Brukarar kan krevje at feil data blir retta.
@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 gløymd")
Brukarar kan be om at data blir sletta.
@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
Brukarar kan få data i maskinlesbart format for å flytte til ei anna teneste.
@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
Brukarar kan protestere mot visse typar handsaming.
6. Rett til avgrensing
Brukarar kan krevje at handsaming blir avgrensa medan ei sak blir undersøkt.
Her er eit komplett eksempel på korleis du implementerer brukarsletting i samsvar med 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 berre det nødvendige.
- Pseudonymisering vs. anonymisering: reverserbar mot ureverserbar.
- GDPR i praksis: standardinnstillingar og brukarsletting.
- Dataetikk: ansvarleg bruk av persondata.
- Innebygd personvern: privacy by design.
Nøkkelomgrep
| Omgrep | Forklaring |
|---|---|
| Dataminimering | Samle berre nødvendige data |
| Pseudonymisering | Kan reverserast med nøkkel |
| Anonymisering | Kan ikkje reverserast |
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.