dapla-toolbelt-pseudo

Dapla logo

dapla-toolbelt-pseudo er en python-pakke som har som sitt hovedformål å gi Dapla-brukere muligheten til å pseudonymisere, de-pseudonymisere og re-pseudonymisere data. Det skal sikre at brukerne av Dapla har verktøyene de trenger for å jobbe med direkte identifiserende opplysninger i henhold til lovverk og SSBs tolkninger av disse.

Siden tilgang til direkte identifiserende opplysninger er underlagt strenge regler, så krever bruken av dapla-pseudo-toolbelt at man forholder seg til vedtatte standarder som datatilstander og systemer som Kildomaten. I tillegg er det en streng tilgangsstyring til hvor man kan kalle funksjonaliteten fra. Tjenestene er satt opp på en slik måte at Dapla-team skal være selvbetjent i bruken av funksjonaliteten, samtidig som regler, prosesser og standarder etterleves på enklest mulig måte.

Standardisert klassifisering av datatilstander

I SSB er det bestemt at all data skal klassifiseres på en standardisert måte basert på datatilstander for å avgjøre om de er sensitive, skjermet eller åpen. Den eneste datatilstanden som klassifiseres som sensitiv er kildedata. Det er derfor bestemt at pseudonymisering er en av prosesseringene som skal skje mellom datatilstandene kildedata og inndata.

Forberedelser

Før man tar i bruk funksjonaliteten er det viktig at man kjenner godt til tilgangstyring i Dapla-team og Kildomaten, og har diskutert med seksjonen hvordan man skal behandle direkte identifiserende opplysninger i de aktuelle dataene.

For at et Dapla-team skal kunne bruke dapla-toolbelt-pseudo må Kildomaten være skrudd på for miljøet1 man ønsker å jobbe fra. Som standard får alle statistikkteam skrudd på Kildomaten i prod-miljøet og ikke i test-miljøet. Ønsker du å aktivere Kildomaten i test-miljøet kan dette gjøres selvbetjent som en feature.

Tilgangsstyring

Tilgang til å funksjonalitet i dapla-toolbelt-pseudo kan regnes som sensitivt i seg selv, og derfor er det en streng tilgangsstyring for bruk av tjenesten. I prod-miljøet kan man kun ta i bruk funksjonaliteten ved å prosessere dataene i Kildomaten, og det er bare tilgangsgruppen data-admins som har tilgang til å godkjenne slike automatiske prosesseringer. I test-miljøet derimot kan alle på teamet benytte seg av all funksjonalitet, siden det aldri skal forekomme ekte data her.

Tabell 1: Tilgangsstyring til dapla-pseudo-toolbelt
(a) Test-miljø
Aktør Validator Pseudonymize() Depseudonymize() Repseudonymize()
Kildomaten
data-admins (interaktivt)
developers (interaktivt)
(b) Prod-miljø
Aktør Validator Pseudonymize() Depseudonymize() Repseudonymize()
Kildomaten 🚫 🚫
data-admins (interaktivt) 🚫 🚫 🚫 🚫
developers (interaktivt) 🚫 🚫 🚫 🚫

I Tabell 1 ser vi fra Tabell 1 (a) at man i test-miljøet har full tilgang til funksjonaliteten i dapla-toolbelt-pseudo, både fra Kildomaten og når man jobber interaktivt2 i Jupyterlab. Tabell 1 (b) viser at det kun er tilgang til pseudonymize() og validator() fra Kildomaten i prod-miljøet, og man kan aldri interaktivt kan kalle på funksjoner som potensielt avslører et pseudonym. Av den grunn er det alltid anbefalt å teste ut koden sin i test-miljøet før den produksjonssettes i i prod-miljøet med Kildomaten.

Funksjonalitet

I denne delen viser vi hvilken funksjonalitet som tilbys gjennom dapla-toolbelt-pseudo. Eksempelkoden under viser hvordan man ville kjørt det fra en notebook i test-miljøet til teamet, og ikke hvordan koden må skrives når det skal kjøres i Kildomaten3.

Installering

dapla-toolbelt-pseudo er ferdig installert i Kildomaten. Men ønsker du å bruke den i test-miljøet til teamet så kan du installere det i et ssb-project fra PyPI med denne kommandoen:

Terminal
poetry add dapla-toolbelt-pseudo

Dataformater

dapla-toolbelt-pseudo støtter innlesing av følgende dataformater:

Eksemplene under viser hovedskelig innlesing av dataframes fra minnet4, men du kan lese mer om filbasert prosessering lenger ned i kapitlet.

Pseudonymisering

Pseudonymisering tilbys via Pseudonymize-metoden i dapla-toolbelt-pseudo. Den følger et builder-pattern der man spesifiserer hva og i hvilken rekkefølge operasjonene skal gjøres. Anta at det finnes en Polars dataframe i minnet som heter df hvor kolonnen fnr skal pseudonymiseres. Da vil koden se slik ut:

Notebook
from dapla_pseudo import Pseudonymize

result_df = (
    Pseudonymize.from_polars(df) 
    .on_fields("fnr")
    .with_default_encryption()                     
    .run()                                
    .to_polars()                                   
)

I koden over så angir from_polars(df) at kolonnen vi ønsker å pseudonymisere ligger i en Polars dataframe i minnet og heter df. Deretter spesifiserer vi at kolonnen fnr er den som skal behandles med funksjonen on_fields("fnr"). Videre angir with_default_encryption() at fnr skal pseudonymiseres med dapla-toolbelt-speudo sin standard-algoritme5. Til slutt ber vi om at det ovennevnte blir kjørt med funksjonen run(), og at dataene skal returneres som en Polars dataframe med funksjonen to_polars().

Se flere eksempler i dokumentasjonen.

De-pseudonymisering

De-pseudonymisering tilbys via Depseudonymize-metoden i dapla-toolbelt-pseudo. Den følger et builder-pattern der man spesifiserer hva og i hvilken rekkefølge operasjonene skal gjøres. Anta at det finnes i en Polars dataframe i minnet som heter df hvor kolonnen fnr skal de-pseudonymiseres. Da vil koden se slik ut:

Notebook
result_df = (
    Depseudonymize.from_polars(df)            
    .on_fields("fnr")                          
    .with_default_encryption()                     
    .run()                                         
    .to_polars()                                   
)

Oppbygningen av koden med Depseudonymize() er helt lik som for Pseudonomize(). Les beskrivelsen der for å se hva de ulike funksjonskallene gjør. Se flere eksempler i dokumentasjonen.

De-pseudonymisering er også støttet for informasjon som først er transformert til stabil ID og deretter pseudonymisert med Papis-nøkkelen. I disse tilfellene kan det også være behov for å spesifisere hvilken versjon av snr-katalogen man ønsker å benytte for å erstatte snr med fødselsnummer:

Notebook
from dapla_pseudo import Depseudonymize

result_df = (
    Depseudonymize.from_pandas(df)            
    .on_fields("fnr")                          
    .with_stable_id(
      sid_snapshot_date="2023-05-29")                    
    .run()                                         
    .to_pandas()                                   
)

I eksempelet over spesifiserer vi datoen 2023-05-29 og da benyttes snr-katalogen som ligger nærmest i tid til denne datoen. Hvis sid_snapshot_date ikke oppgis benyttes siste tilgjengelige versjon av katalogen.

De-pseudonymisering ikke tilgjengelig i prod-miljø

Foreløpig er det kun tilgang til å pseudonymisere i test-miljøet med test-data. Ta kontakt med Dapla-teamene dersom det dukker opp behov for å kunne de-pseudonymisere i prod-miljøet.

Re-pseudonymisering

Under utvikling.

Stabil ID

I statistikkproduksjon og forskning er det viktig å kunne følge de samme personene over tid. Derfor har fødselsnummer ofte blitt oversatt til en mer stabilt identifikator, ofte kalt SNR eller stabil ID6. Funksjonene with_stable_id() og validator() bruker stabilID-katalogen til å henholdsvis bytte ut fødselsnummer med stabil ID, og for å validere om fødselsnummer er gyldige(se under).

Du kan selv spesifisere hvilken versjon av SNR-katalogen du ønsker å bruke. Det gjør du ved å oppgi en gyldighetsdato og så finner dapla-toolbelt-pseudo hvilken versjon av katalogen som ligger nærmest i tid.

Papis-pseudonym

Dapla tilbyr samme pseudonym som Papis-prosjektet7. Denne kan benyttes på 2 måter:

  1. Pseudonymisere hvilken som helst informasjon med samme nøkkel som Papis.
  2. Transformere fødselsnummer til snr-nummer og deretter pseudonymisere med samme nøkkel som Papis.

Punkt 1 er nyttig for de som har pseudonymisert informasjon på bakken tidligere og vil ha samme pseudonym på Dapla8. Dette kan gjelde hvilken som helst informasjon, også direkte pseudonymisering av fødselsnummer, uten at det er gått via snr-nummer. Her er et eksempel på hvordan man pseudonymiserer på denne måten:

Notebook
result = (
    Pseudonymize.from_pandas(df)
    .on_fields("fornavn")                      
    .with_papis_compatible_encryption()         
    .run()                               
    .to_pandas()                                  
)

Punkt 2 over er nok den som benyttes mest i SSB siden den sikrer at pseudonymisert fødselsnummer kan kobles med data som er pseudonymisert på bakken. Her er et eksempel på hvordan man pseudonymiserer snr med Papis-nøkkelen:

Notebook
result = (
    Pseudonymize.from_pandas(df)
    .on_fields("fnr")                      
    .with_stable_id()         
    .run()                               
    .to_pandas()                                  
)
Hva betyr det å tilby samme pseudonym?

At Papis og Dapla tilbyr samme pseudonum betyr egentlig at vi bruker samme krypteringsalgoritme, og en felles krypteringsnøkkel. Krypteringsalgoritmen som benyttes er formatpreserverende (FPE) og biblioteket som brukes er Tink FPE Python. En svakhet med algoritmen er at den ikke håndterer tekst som kortere enn 4 karakterer lang, og strategien som benyttes for å håndtere dette er SKIP, noe som betyr den originale verdien blir returnert som den var.

Validere fødselsnummer

Validator-metoden kan benyttes til å sjekke om fødselsnummer finnes i SNR-katalogen (se over), og returnerer de ugyldige fødselsnummerne tilbake. Her kan man også spesifisere hvilken versjon av SNR-katalogen man ønsker å bruke. Standard, hvis ingenting velges, er at siste tilgjengelige versjon velges. Under er et eksempel på hvordan man validerer fødselsnummer for en gitt gyldighetsdato:

Notebook
from dapla_pseudo import Validator

result = (
    Validator.from_polars(df)
    .on_field("fnr")
    .validate_map_to_stable_id(
        sid_snapshot_date="2023-08-29"
    )
)
# Vis hvilken versjon av SNR-katalogen som er benyttet
result.metadata
# Vis fødselsnummer som ikke fikk treff i SNR-katalogen som en Polars dataframe
result.to_polars()

Filbasert prosessering

Selv om mange av eksemplene i dette kapitlet viser hvordan man bruker dapla-toolbelt-pseudo ved å gi funksjonene et datasett fra minnet, så støtter den også å prosessere filer som er lagret på disk/bøtter. Dette kan være en fordel hvis dataene er for store for Kildomaten (>32GB RAM) eller de har en dyp hierarkisk struktur (f.eks. json-filer).

dapla-toolbelt-pseudo støtter følgende filtyper:

  • csv
  • json

Her er et eksempel på hvordan man pseudonymiserer en fil som ligger lagret på disk:

Notebook
from dapla_pseudo import Pseudonymize
from dapla import AuthClient


file_path = "gs://bucket/pseudo-examples/andeby_personer.csv"

options = {
    "dtypes": {"fnr": pl.Utf8, "fornavn": pl.Utf8, "etternavn": pl.Utf8, "kjonn": pl.Categorical, "fodselsdato": pl.Utf8}
}

result_df = (
    Pseudonymize.from_file(file_path)
    .on_fields("fnr")
    .with_default_encryption()
    .run()
    .to_polars(**options)
)

Se flere eksempler i dokumentasjonen.

Wildcards

Kommer snart.

Dataminimering

Kommer snart.

Algoritmer

Kommer snart.

Metadata

Det genereres to typer av metadata når man pseudonymiserer:

  1. Datadoc
  2. Metadata

De to typene av metadata returneres til brukeren i to forskjellige objekter.

Datadoc

Datadoc-metadata er på et format som er planlagt integrert i Datadoc9 på et senere tidspunkt. I koden til høyre så printes metadataene fra et kall til Pseudonomize ved å skrive print(result.datadoc). Da printer man objektet interaktivt i f.eks. Jupyterlab, noe som kun er mulig i test-miljøet. Skal man kjøre dette i Kildomaten så er det lettere å skrive filen direkte til riktig json-format med to_file-funksjonen. Da får får filen endelsen __DOC på slutten av filnavnet, og man slipper å tenke på om filen skrives med riktig formattering, osv..

Notebook
result = (
    Pseudonymize.from_polars(df)    
    .on_fields("fnr")           
    .with_stable_id()
    .run()                      
)

1print(result.datadoc)
2result.to_file("gs://bucket/test.parquet")
1
Printer metadata i en Notebook.
2
Skriver metadata direkte til bøtte.

Når man kjører pseudonymisering fra Kildomaten er det viktig å tenke på at felter som er pseudonymisert ikke må endres uten at metadataene også endrer. Da kan man risikere at metadatene ikke lenger beskriver riktig informasjon.

Under ser man hvilken informasjon som genereres fra pseudonymiseringen.

Datadoc
{
  "document_version": "0.0.1",
  "datadoc": null,
  "pseudonymization": {
    "document_version": "0.1.0",
    "pseudo_dataset": null,
    "pseudo_variables": [
      {
        "short_name": "fnr",   
        "data_element_path": "fnr",
        "data_element_pattern": "**",
        "stable_identifier_type": "FREG_SNR",
        "stable_identifier_version": "2023-08-31",
        "encryption_algorithm": "TINK-FPE",
        "encryption_key_reference": "papis-common-key-1",
        "encryption_algorithm_parameters": [
          {
            "keyId": "papis-common-key-1"
          },
          {
            "strategy": "SKIP"
          }
        ],
        "source_variable": null,
        "source_variable_datatype": null
      }
    ]
  }
}

Av metadatene kan vi se fra pseudo_variables at det bare var feltet fnr som ble pseudonymisert. Vi ser også av stable_identifier_type ser vi at fnr ble oversatt til snr, og at versjonen av SNR-katalogen var fra 2023-08-31. encryption_algorithm angir at det var det var den formatpreserverende algoritmen TINK-FPE som ble benyttet. keyID: "papis-common-key-1" angir hvilken nøkkel-id som ble benyttet. strategy: "SKIP" refererer til at den format-preserverende algoritmen skal “hoppe over” ugyldige karakterer og la de være som de er.

Denne informasjonen vil være svært nyttig i SSB hvis man senere skal kunne de-pseudonymisere eller re-pseudonymisere data.

Metadata

Den andre typen av metadata kan hentes ut etter et kall til Pseudonymize med kommandoen result.metadata. Den returnerer en python dictionary. Den inneholder hovedsaklig logginformasjon og metrikker foreløpig. For de som pseudonymiserer med with_stable_id() kan output se slik ut:

Metadata
{
    'fnr': {
        'logs': [
            'No SID-mapping found for fnr 999999*****',
            'No SID-mapping found for fnr XX',
            'No SID-mapping found for fnr X8b7k2*'
        ],
        'metrics': [
            {'MAPPED_SID': 10},
            {'MISSING_SID': 3}
        ]
    }
}

I Kildomaten kan det vært nyttig å printe denne informasjonen til loggene. Av eksempelet over ser vi at verdier som er over 6 karakterer lange blir maskert.

Brukerveiledning

På grunn av den strenge tilgangsstyringen til dapla-pseudo-toolbelt og kildedata er det anbefalt å utvikle kode for overgangen fra kildedata til inndata i test-miljøet til teamet. I denne delen viser vi hvordan denne arbeidsflyten kan se ut, fra testing til en automatisert produksjon med ekte data, med et helt konkret eksempel.

Interaktiv utvikling

For å kunne kjøre pseudonymiseringen interaktivt i f.eks. en notebook i Jupyter, så må man jobbe i test-miljøet til teamet.

Kildomaten i test

Kommer snart.

Kildomaten i prod

Kommer snart.

Fotnoter

  1. Et Dapla-team har både et test- og et prod-miljø. Kildomaten må være skrudd på i det miljøet du ønkser å benytte dapla-toolbelt-pseudo fra.↩︎

  2. Med interaktiv jobbing menes at man skriver og kode og får tilbake output i samme verktøy. F.eks. er Jupyterlab et eksempel på et verktøy som lar deg jobbe interaktivt med data.↩︎

  3. I Kildomaten må koden blant annet pakke inn i main()-funksjon.↩︎

  4. Pandas og Polars dataframes er eksempler på dataformater som lever i minnet, og må konverteres før de skrives til et lagringsommråde. I praksis vil det ofte si at man jobber med dataframes når man jobber i verktøy som Jupyterlab, mens man skriver til lagringsområde når man er ferdig i Jupyterlab.↩︎

  5. Standardalgoritmen i dapla-toolbelt-pseudo er den deterministiske krypteringsalgoritmen Deterministic Authenticated Encryption with Associated Data, eller DAEAD-algoritmen.↩︎

  6. SNR-katalogen eies og tilbys av Team Register på Dapla.↩︎

  7. Papis var et prosjekt med fokus på bakkesystemene i SSB som skulle bringe SSBs behandling av personopplysninger, som brukes i statistikkproduksjon, i samsvar med GDPR gjennom en enhetlig pseudonymiseringsløsning.↩︎

  8. Generelt sett er det ikke å anbefale å benytte denne nøkkelen på annen informasjon enn fødselsnummer. Grunnen er at den er svakere enn andre algoritmer, der blant annet tekst som er kortere enn 4 karakter lang ikke blir pseudonymisert.↩︎

  9. Datadoc er det nye systemet for dokumentasjon av datasett i SSB. Systemet er fortsatt under utvikling.↩︎