Privacy by Design in PA: regulační povinnost a architektonická volba

Obecné nařízení o ochraně osobních údajů (GDPR), které vstoupilo v platnost 25. května 2018, se neomezuje pouze na ukládání povinností byrokratické pro orgány veřejné správy: Článek 25 stanoví, že ochrana údajů musí být integrována již od fáze návrhu informačních systémů a procesů, které zpracovávají osobní údaje. Tento princip, známý jako Privacy by Design, mění dodržování předpisů v jeden architektonická kvalita softwaru.

Pro vývojáře nebo architekta pracujícího na italských PA systémech - digitální registr, elektronický zdravotní záznam, servisní přístupové portály s SPID nebo CIE, systémy řízení administrativních procedur - pochopit a implementovat správně GDPR-by-Design není volitelné. Sankce stanovené Garantským orgánem pro ochranu osobních údajů mohou dosáhnout 4 % celosvětového ročního obratu soukromých subjektů; pro CHÚ důsledky zahrnují blokády ošetření, náhradu škody a značné poškození dobrého jména.

Co se dozvíte v tomto článku

  • 7 základních principů Privacy by Design a jak je namapovat na konkrétní architektonická rozhodnutí
  • Pseudonymizační a anonymizační vzory pro databáze a vládní API
  • Implementace vzoru minimalizace dat v mikroslužbách PA
  • Consent management: architektura a implementace systému v souladu s GDPR
  • Automatické uchovávání dat a právo na vymazání v relačních databázích
  • Vyhovující protokolování auditu: jak sledovat léčbu bez narušení soukromí
  • DPIA (Data Protection Impact Assessment) pro vysoce rizikové systémy

7 základních principů a jejich dopad na architekturu

Ann Cavoukian, bývalá komisařka pro informace a soukromí z Ontaria, formalizovala 7 principů Privacy by Design, které GDPR bylo implementováno. Každý princip se promítá do konkrétních architektonických voleb:

princip PbD Článek GDPR architektonický vzor Konkrétní techniky
Proaktivní, nereaktivní článek 25 Soukromí modelování hrozeb Posouzení rizika ochrany soukromí ve fázi návrhu
Soukromí jako výchozí 25 odst. 2 Přihlaste se ve výchozím nastavení Výslovný souhlas, standardně minimalizace
Integrováno do designu 25 odst. 1 Privacy-Embedded Architecture Pseudonymizace, šifrování v klidu
Plná funkčnost článek 5 Vyhýbání se nulovému součtu Soukromí + zabezpečení nejsou v rozporu
End-to-end zabezpečení článek 32 Obrana do hloubky TLS, šifrování, správa klíčů
Viditelnost a transparentnost Článek 13/14 Audit trail + zveřejnění Anonymizované protokolování, upozornění na ochranu osobních údajů
Respektování soukromí uživatelů Článek 7/8/17 Ovládací prvky zaměřené na uživatele Uživatelské rozhraní pro souhlas, právo být zapomenut, přenositelnost

Vzor 1: Minimalizace dat v PA Microservices

Zásada minimalizace (čl. 5 odst. 1 písm. c) GDPR) vyžaduje, aby shromažďované údaje byly „přiměřené, relevantní a omezené na to, co nezbytné s ohledem na účely, pro které jsou zpracovávány“. V architektuře mikroslužeb se to promítá do vzoru Minimalizace dat na hranici: každá služba musí přijímat pouze data, která potřebuje k provedení jeho specifická funkce.

Zvažte službu ověření identity, abyste získali přístup ke zdravotní péči. Služba nepotřebuje název úplné informace občana k ověření, že má na dávku nárok: stačí mu anonymní identifikátor a oprávněný stav. Vzor je implementován přes Selektivní objekty DTO (Data Transfer Objects). e projekční dotazy.

# Pattern Data Minimization - Esempio Python/FastAPI
# Scenario: servizio prestazioni sanitarie PA

from pydantic import BaseModel
from typing import Optional
import hashlib

# SBAGLIATO: trasferisce dati non necessari
class CitizenFullDTO(BaseModel):
    citizen_id: str
    fiscal_code: str
    first_name: str      # Non necessario per verifica
    last_name: str       # Non necessario per verifica
    birth_date: str      # Non necessario per verifica
    address: str         # Non necessario per verifica
    phone: str           # Non necessario per verifica

# CORRETTO: minimizza i dati al minimo necessario
class CitizenEligibilityDTO(BaseModel):
    # Solo un token opaco, non il codice fiscale reale
    pseudonymous_id: str
    is_eligible: bool
    benefit_category: str
    # Nessun dato personale identificativo

# Service layer con minimizzazione
class HealthBenefitService:
    def __init__(self, citizen_repo, pseudonym_service):
        self.citizen_repo = citizen_repo
        self.pseudonym_service = pseudonym_service

    def check_eligibility(self, pseudonymous_id: str, benefit_code: str) -> CitizenEligibilityDTO:
        # Risolve il pseudonimo solo internamente, non lo espone
        real_id = self.pseudonym_service.resolve(pseudonymous_id)

        # Query mirata: solo il dato necessario
        is_eligible = self.citizen_repo.check_benefit_eligibility(
            citizen_id=real_id,
            benefit_code=benefit_code
        )

        return CitizenEligibilityDTO(
            pseudonymous_id=pseudonymous_id,
            is_eligible=is_eligible,
            benefit_category=benefit_code
        )
        # NON ritorna: nome, CF, indirizzo, telefono, email

# Repository con projection query (minimizzazione lato DB)
class CitizenRepository:
    def check_benefit_eligibility(self, citizen_id: str, benefit_code: str) -> bool:
        # SELECT solo la colonna necessaria, non SELECT *
        query = """
            SELECT EXISTS(
                SELECT 1 FROM citizen_benefits
                WHERE citizen_id = {citizen_id}
                AND benefit_code = {benefit_code}
                AND valid_until >= CURRENT_DATE
            )
        """
        return self.db.execute(query, {"citizen_id": citizen_id, "benefit_code": benefit_code}).scalar()

Vzor 2: Pseudonymizace a tokenizace

Pseudonymizace (definovaná v čl. 4 odst. 5 GDPR) je jedním z technických opatření výslovně zmíněných v čl. 25 lajků adekvátní k prokázání souladu s Privacy by Design. Liší se od anonymizace: Pseudonymizovaná data mohou být zpětně vysledován k zainteresované straně prostřednictvím dodatečných informací uchovávaných odděleně, zatímco anonymní údaje nikoli mohou být (a tedy nepodléhají GDPR).

U systémů PA je pseudonymizace často výhodnější než anonymizace, protože vám umožňuje udržovat vnitřní sledovatelnost (nezbytná pro audit a dodržování předpisů) při současné ochraně identity subjektů údajů v systémech front-end a v protokolech.

# Pattern Pseudonimizzazione con Vault separato
# Architettura: Pseudonym Vault isolato dal sistema principale

import secrets
import hmac
import hashlib
from datetime import datetime, timedelta
from dataclasses import dataclass

@dataclass
class PseudonymRecord:
    pseudonym: str
    real_id: str
    created_at: datetime
    expires_at: datetime
    purpose: str  # Finalità del trattamento (Art. 5(1)(b))

class PseudonymVault:
    """
    Vault isolato che gestisce la mappatura pseudonimo <-> identità reale.
    Accesso ristretto: solo servizi autorizzati con chiave vault.
    Log di ogni accesso per audit GDPR.
    """

    def __init__(self, vault_key: bytes, db_connection):
        self._vault_key = vault_key
        self._db = db_connection

    def create_pseudonym(
        self,
        real_id: str,
        purpose: str,
        validity_days: int = 365
    ) -> str:
        """
        Genera pseudonimo crittograficamente sicuro.
        Usa HMAC-SHA256 con chiave vault per essere deterministico
        (stesso real_id + purpose = stesso pseudonimo) ma non invertibile
        senza la chiave vault.
        """
        # HMAC deterministico: stesso input = stesso output
        # Ma non reversibile senza vault_key
        pseudonym_bytes = hmac.new(
            key=self._vault_key,
            msg=f"{real_id}:{purpose}".encode(),
            digestmod=hashlib.sha256
        ).digest()

        # Converti in stringa URL-safe
        pseudonym = pseudonym_bytes.hex()[:32]  # 128-bit, abbondante

        # Registra nel vault (separato dal DB principale)
        record = PseudonymRecord(
            pseudonym=pseudonym,
            real_id=real_id,
            created_at=datetime.utcnow(),
            expires_at=datetime.utcnow() + timedelta(days=validity_days),
            purpose=purpose
        )
        self._db.insert_pseudonym(record)

        return pseudonym

    def resolve_pseudonym(self, pseudonym: str, requesting_service: str) -> str:
        """
        Risolve pseudonimo -> identità reale.
        Richiede autorizzazione esplicita del servizio richiedente.
        Logga ogni accesso per audit.
        """
        # Audit log obbligatorio
        self._log_resolution_access(
            pseudonym=pseudonym,
            requesting_service=requesting_service,
            timestamp=datetime.utcnow()
        )

        record = self._db.get_pseudonym(pseudonym)

        if not record:
            raise ValueError("Pseudonym not found")

        if datetime.utcnow() > record.expires_at:
            raise ValueError("Pseudonym expired")

        return record.real_id

    def _log_resolution_access(self, pseudonym: str, requesting_service: str, timestamp: datetime):
        """
        Log GDPR-compliant: registra chi ha risolto quale pseudonimo e quando.
        Il log stesso usa solo lo pseudonimo (non l'identità reale).
        """
        self._db.insert_audit_log({
            "action": "PSEUDONYM_RESOLVED",
            "pseudonym_hash": hashlib.sha256(pseudonym.encode()).hexdigest(),
            "requesting_service": requesting_service,
            "timestamp": timestamp.isoformat(),
            "legal_basis": "GDPR Art. 6(1)(e) - Task public interest"
        })

Vzor 3: Správa souhlasu v souladu s GDPR

Souhlas (čl. 7 GDPR) musí být volné, konkrétní, informované a jednoznačné. Pro PA ve většině V některých případech není zpracování založeno na souhlasu, ale na různých právních základech (čl. 6 odst. 1 písm. e): úkol veřejného zájmu, o Čl. 6 odst. 1 písm. c): právní povinnost). Pokud je však zvoleným právním základem souhlas – například pro komunikaci marketing, newslettery nebo volitelné zpracování — systém správy souhlasu musí splňovat přesné požadavky.

Un Platforma pro správu souhlasu (CMP) pro PA si musí zapamatovat: co bylo přijato, kdy, s kým verze informací, z kterého kanálu, a musí umožňovat odvolání tak snadné, jak bylo poskytnutí.

-- Schema SQL: Consent Management GDPR-compliant
-- Database separato o schema isolato con accesso controllato

CREATE TABLE consent_purposes (
    purpose_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code VARCHAR(64) UNIQUE NOT NULL,
    name_it TEXT NOT NULL,
    name_en TEXT,
    description_it TEXT NOT NULL,
    legal_basis VARCHAR(32) NOT NULL,  -- 'consent', 'legal_obligation', 'public_task'
    data_categories TEXT[] NOT NULL,   -- Categorie di dati trattati
    retention_days INTEGER NOT NULL,
    third_parties TEXT[],              -- Destinatari (trasparenza)
    created_at TIMESTAMPTZ DEFAULT NOW(),
    version INTEGER NOT NULL DEFAULT 1,
    is_active BOOLEAN DEFAULT TRUE
);

CREATE TABLE citizen_consents (
    consent_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    -- Pseudonimo, non ID reale
    citizen_pseudonym VARCHAR(64) NOT NULL,
    purpose_id UUID REFERENCES consent_purposes(purpose_id),
    status VARCHAR(16) NOT NULL CHECK (status IN ('granted', 'denied', 'withdrawn')),
    granted_at TIMESTAMPTZ,
    withdrawn_at TIMESTAMPTZ,
    -- Prova del consenso (Art. 7(1): titolare deve dimostrare il consenso)
    proof_channel VARCHAR(32) NOT NULL,    -- 'web_portal', 'mobile_app', 'paper'
    proof_ip_hash VARCHAR(64),             -- Hash dell'IP, non IP raw
    proof_session_id VARCHAR(128),
    privacy_policy_version VARCHAR(16) NOT NULL,
    -- Lingua in cui è stata mostrata l'informativa
    consent_language VARCHAR(8) NOT NULL DEFAULT 'it',
    user_agent_hash VARCHAR(64)
);

-- Indice per query rapide sulla revoca
CREATE INDEX idx_consents_pseudonym ON citizen_consents(citizen_pseudonym, status);

-- Vista per audit: mostra solo dati necessari al DPO
CREATE VIEW consent_audit_view AS
SELECT
    c.consent_id,
    c.citizen_pseudonym,
    p.code AS purpose_code,
    p.name_it AS purpose_name,
    c.status,
    c.granted_at,
    c.withdrawn_at,
    c.proof_channel,
    c.privacy_policy_version
FROM citizen_consents c
JOIN consent_purposes p ON c.purpose_id = p.purpose_id;

-- Funzione per il diritto di revoca (Art. 7(3))
CREATE OR REPLACE FUNCTION withdraw_consent(
    p_citizen_pseudonym VARCHAR(64),
    p_purpose_code VARCHAR(64)
) RETURNS VOID AS $
BEGIN
    UPDATE citizen_consents
    SET
        status = 'withdrawn',
        withdrawn_at = NOW()
    WHERE
        citizen_pseudonym = p_citizen_pseudonym
        AND purpose_id = (
            SELECT purpose_id FROM consent_purposes WHERE code = p_purpose_code
        )
        AND status = 'granted';

    -- Logga l'evento per audit
    INSERT INTO consent_events (
        event_type, citizen_pseudonym, purpose_code, occurred_at
    ) VALUES (
        'CONSENT_WITHDRAWN', p_citizen_pseudonym, p_purpose_code, NOW()
    );
END;
$ LANGUAGE plpgsql;

Vzor 4: Automatické uchovávání dat a právo na vymazání

Zásada „omezení ukládání“ (čl. 5 odst. 1 písm. e) GDPR) vyžaduje, aby byly osobní údaje uloženy "ve formě, která umožňuje identifikaci zainteresovaných stran po dobu nepřesahující dosažení účelů, pro které jsou zpracováványV praxi musí každý PA systém implementovat politiku uchovávání automatické, které bez ručního zásahu vymaže nebo anonymizuje data, jejichž platnost vypršela.

Právo na výmaz (článek 17 GDPR), známé jako „právo být zapomenut“, přidává další vrstvu složitosti: systém musí být schopen na požádání smazat údaje konkrétní fyzické osoby při respektování případných výjimek (například údaje nezbytné pro splnění zákonných povinností).

# Data Retention Manager - Python con scheduling automatico
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from dataclasses import dataclass
from enum import Enum
from datetime import datetime, timedelta
from typing import List

class RetentionAction(Enum):
    DELETE = "delete"
    ANONYMIZE = "anonymize"
    ARCHIVE = "archive"

@dataclass
class RetentionPolicy:
    table_name: str
    date_column: str
    retention_days: int
    action: RetentionAction
    legal_basis: str  # Documentazione dell'obbligo legale

# Registro delle politiche di retention (da configurazione, non hardcoded)
RETENTION_POLICIES: List[RetentionPolicy] = [
    RetentionPolicy(
        table_name="citizen_session_logs",
        date_column="created_at",
        retention_days=90,  # 3 mesi per log sessione
        action=RetentionAction.DELETE,
        legal_basis="GDPR Art. 5(1)(e) - Storage limitation"
    ),
    RetentionPolicy(
        table_name="service_requests",
        date_column="completed_at",
        retention_days=2555,  # 7 anni per atti amministrativi
        action=RetentionAction.ANONYMIZE,
        legal_basis="D.Lgs. 82/2005 Art. 40 - Conservazione atti"
    ),
    RetentionPolicy(
        table_name="consent_records",
        date_column="withdrawn_at",
        retention_days=365,  # 1 anno dopo revoca per prova
        action=RetentionAction.DELETE,
        legal_basis="GDPR Art. 7(1) - Prova consenso"
    ),
]

class DataRetentionManager:
    def __init__(self, db, audit_logger):
        self.db = db
        self.audit_logger = audit_logger
        self.scheduler = AsyncIOScheduler()

    async def run_retention_policy(self, policy: RetentionPolicy) -> dict:
        """Esegue una politica di retention e ritorna statistiche."""
        cutoff_date = datetime.utcnow() - timedelta(days=policy.retention_days)
        stats = {"policy": policy.table_name, "action": policy.action.value, "rows_affected": 0}

        if policy.action == RetentionAction.DELETE:
            result = await self.db.execute(
                f"DELETE FROM {policy.table_name} WHERE {policy.date_column} < $1",
                cutoff_date
            )
            stats["rows_affected"] = result.rowcount

        elif policy.action == RetentionAction.ANONYMIZE:
            # Anonymize: sostituisce dati identificativi con hash o NULL
            result = await self.db.execute(
                f"""UPDATE {policy.table_name}
                    SET
                        fiscal_code = 'ANONYMIZED_' || gen_random_uuid()::text,
                        first_name = NULL,
                        last_name = NULL,
                        email = NULL,
                        phone = NULL
                    WHERE {policy.date_column} < $1
                    AND fiscal_code NOT LIKE 'ANONYMIZED_%'""",
                cutoff_date
            )
            stats["rows_affected"] = result.rowcount

        # Audit log obbligatorio per ogni operazione di retention
        await self.audit_logger.log({
            "event": "RETENTION_EXECUTED",
            "policy": policy.table_name,
            "action": policy.action.value,
            "cutoff_date": cutoff_date.isoformat(),
            "rows_affected": stats["rows_affected"],
            "legal_basis": policy.legal_basis,
            "executed_at": datetime.utcnow().isoformat()
        })

        return stats

    async def handle_erasure_request(self, citizen_pseudonym: str, request_id: str) -> dict:
        """
        Gestisce il diritto alla cancellazione (Art. 17 GDPR).
        Verifica le eccezioni prima di procedere.
        """
        # Verifica eccezioni Art. 17(3) - obblighi legali, interesse pubblico, etc.
        exceptions = await self._check_erasure_exceptions(citizen_pseudonym)

        if exceptions:
            # Non cancella, documenta il motivo del diniego
            await self.audit_logger.log({
                "event": "ERASURE_REQUEST_DENIED",
                "citizen_pseudonym": citizen_pseudonym,
                "request_id": request_id,
                "reasons": exceptions
            })
            return {"status": "denied", "reasons": exceptions}

        # Procede con la cancellazione
        tables_affected = []
        for table in ["citizen_session_logs", "consent_records", "service_requests_temp"]:
            rows = await self.db.execute(
                f"DELETE FROM {table} WHERE citizen_pseudonym = $1",
                citizen_pseudonym
            )
            if rows.rowcount > 0:
                tables_affected.append(table)

        await self.audit_logger.log({
            "event": "ERASURE_REQUEST_EXECUTED",
            "citizen_pseudonym": citizen_pseudonym,
            "request_id": request_id,
            "tables_affected": tables_affected,
            "executed_at": datetime.utcnow().isoformat()
        })

        return {"status": "completed", "tables_affected": tables_affected}

DPIA: kdy je povinný a jak jej strukturovat

La Posouzení dopadu na ochranu údajů (DPIA, Data Protection Impact Assessment) je povinné podle Čl. 35 GDPR při zpracování "může představovat vysoké riziko pro práva a svobody fyzické osobyPro italskou PA zveřejnil Garant ochrany osobních údajů seznam ošetření, která vyžadují DPIA je povinné.

Léčba PA vyžadující povinné DPIA

  • Systematické profilování občanů (např. socioekonomické hodnocení)
  • Ošetření ve velkém měřítku konkrétních kategorií údajů (zdravotní, soudní)
  • Systematický dohled veřejných prostor (video dohled)
  • Párovací/propojovací systémy datových sad z různých zdrojů
  • Údaje zranitelných osob (nezletilí, pacienti, žadatelé o azyl)
  • Inovativní využití technologií (AI/ML pro automatizovaná rozhodnutí, čl. 22)
  • Mezinárodní převod osobních údajů

Strukturovaný DPIA zahrnuje: popis zpracování a jeho účelů, posouzení nezbytnosti a přiměřenosti, identifikaci a posouzení rizik pro práva subjektů údajů a předpokládaná opatření k řešení rizik (včetně záruk a bezpečnostních mechanismů). Zapojení DPO (Data Protection Officer) je povinné (čl. 35 odst. 2) a DPIA musí být aktualizovány, když se léčba změní.

Vzor 5: Protokolování auditu v souladu s GDPR

Podceňovaným požadavkem GDPR je odpovědnost (odpovědnost, čl. 5 odst. 2): vlastník léčby musí být schopen prokázat shodu. To vyžaduje systém protokolování auditu, který sleduje operace s osobními údaji, které se však – paradoxně – samy o sobě nestávají zdrojem nadměrného zpracování osobních údajů.

Auditní protokol v souladu s GDPR musí:

  • Záznam SZO přistupoval k jakým datům a kdy, s použitím pseudonymů nebo systémových ID, kde je to možné
  • Být neměnný (pouze příloha), aby byla zajištěna integrita důkazů
  • Mít vlastní zásady uchovávání (protokoly nejsou uchovávány navždy)
  • Být chráněn před neoprávněným přístupem (odděleně od hlavní DB)
  • Podporujte efektivní dotazy, abyste mohli reagovat na požadavky DPO nebo ručitelského úřadu
# Audit Logger immutabile con PostgreSQL - append-only tramite trigger
# Usa una tabella separata con accesso write-only dall'applicazione

-- Schema audit log (database o schema separato)
CREATE TABLE gdpr_audit_log (
    log_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    -- Identificatori: pseudonimizzati dove possibile
    subject_pseudonym VARCHAR(64),       -- Chi ha eseguito l'azione
    affected_entity_pseudonym VARCHAR(64), -- Su quale entità
    -- Cosa è successo
    action_type VARCHAR(64) NOT NULL,    -- READ, UPDATE, DELETE, EXPORT, etc.
    resource_type VARCHAR(64) NOT NULL,  -- citizen_record, consent, health_data, etc.
    -- Contesto
    legal_basis VARCHAR(128),            -- Base giuridica del trattamento
    purpose VARCHAR(256),                -- Finalità del trattamento
    -- Metadati tecnici (senza dati personali)
    service_name VARCHAR(128) NOT NULL,
    request_id VARCHAR(128),
    -- Timestamp immutabile
    occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
    -- NON includere: IP raw, user agent completo, payload della request
);

-- Nega UPDATE e DELETE sulla tabella audit (append-only)
CREATE RULE no_update_audit AS ON UPDATE TO gdpr_audit_log DO INSTEAD NOTHING;
CREATE RULE no_delete_audit AS ON DELETE TO gdpr_audit_log DO INSTEAD NOTHING;

-- Funzione trigger per log automatico su tabelle sensibili
CREATE OR REPLACE FUNCTION audit_sensitive_access() RETURNS TRIGGER AS $
BEGIN
    INSERT INTO gdpr_audit_log (
        action_type,
        resource_type,
        subject_pseudonym,
        affected_entity_pseudonym,
        service_name,
        legal_basis
    ) VALUES (
        TG_OP,                          -- INSERT/UPDATE/DELETE
        TG_TABLE_NAME,
        current_setting('app.current_user_pseudonym', true),
        NEW.citizen_pseudonym,
        current_setting('app.service_name', true),
        current_setting('app.legal_basis', true)
    );
    RETURN NEW;
END;
$ LANGUAGE plpgsql SECURITY DEFINER;

-- Applica il trigger alle tabelle sensibili
CREATE TRIGGER audit_health_records
    AFTER INSERT OR UPDATE OR DELETE ON health_records
    FOR EACH ROW EXECUTE FUNCTION audit_sensitive_access();

Celková architektura: GDPR-by-Design ve službě PA

Když dáme dohromady všechny popsané vzory, architektura služby PA v souladu s GDPR zahrnuje:

  • Brána API: Vstupní bod, který ověřuje autentizaci (SPID/CIE), používá pseudonymizaci a propaguje právní základ v kontextu požadavku (prostřednictvím hlavičky nebo middlewaru)
  • Pseudonymní trezor: izolovaná služba pro mapování pseudonymu a identity s omezeným přístupem a protokol auditu každého usnesení
  • Mikroslužby s minimalizací dat: každá služba přijímá pouze nezbytná data, odhaluje minimální DTO, používat projekční dotazy do DB
  • Služba správy souhlasu: spravuje souhlasy, ověřuje právní základy, podporuje odvolání a export dat (přenositelnost čl. 20)
  • Plánovač retence: pravidelné úlohy, které uplatňují zásady uchovávání a spravují požadavky zrušení a vypracování zpráv pro DPO
  • Služba protokolu auditu: pouze připojení, samostatné, s přístupem pouze pro zápis z aplikace a pouze pro čtení ze strany DPO/ručitele

Užitečné nástroje a rámce pro GDPR-by-Design v PA

  • OpenDP: Knihovna Pythonu pro rozdílné soukromí, užitečná pro anonymizované analýzy
  • Nástroj pro anonymizaci dat ARX: open source nástroj pro anonymizaci datové sady
  • Klíčenka: Poskytovatel identity s otevřeným zdrojovým kódem s vestavěnou podporou správy souhlasu
  • Datasketch: pro hašování a MinHash, užitečné při škálovatelné pseudonymizaci
  • PgAudit: Rozšíření PostgreSQL pro protokolování auditu na úrovni databáze
  • Návrháři Itálie: oficiální pokyny AgID k ochraně soukromí již od návrhu pro PA
  • CNIL Privacy by Design Guide: praktická metodika francouzského úřadu platná v Itálii

Role DPO a spolupráce s vývojovým týmem

Podle Čl. 37 GDPR je jmenování pověřence pro ochranu osobních údajů (DPO) povinné pro všechny orgány veřejné moci a orgány veřejné moci (s výjimkou soudů při výkonu jurisdikčních funkcí). DPO není pouze údaj o souladu: pro vývojové týmy zastupuje interního konzultanta pro ochranu dat, který musí být zapojen od počátečních fází projektu.

V praxi by vývojový tým měl:

  • Při zpracování osobních údajů zapojit DPO do přezkumu architektury
  • Volby návrhu ochrany soukromí dokumentů v Architecture Decision Records (ADR)
  • Zahrňte kontroly souladu s GDPR do definic uživatelského příběhu hotovo
  • U každé nové funkce, která zavádí zpracování osobních údajů, proveďte Posouzení rizika ochrany soukromí
  • Pokud je to možné, aktualizujte registr zpracování (článek 30) automatizovaným způsobem

Závěry a další kroky

GDPR-by-Design není byrokratický kontrolní seznam, který by se měl odškrtnout na konci vývoje: je to architektonický přístup který, pokud je integrován od raných fází projektu, vytváří bezpečnější, transparentnější a robustnější systémy. Vzorce popsané v tomto článku — minimalizace dat, pseudonymizace, správa souhlasu, uchovávání automatické protokolování auditu – vztahují se na jakoukoli digitální službu italské PA, od jednoduché stránka shromažďování souhlasu pro institucionální zpravodaj až po složité systémy, jako je zdravotní záznam elektronické nebo digitální platební platformy.

V dalším článku této série budeme analyzovat, jak implementovat přístupná uživatelská rozhraní pro druhý PA standard WCAG 2.1 AA: další regulační požadavek, který stejně jako GDPR odměňuje ty, kteří jej integrují již ve fázi návrhu.

Související články v této sérii

  • GovTech #01: eIDAS 2.0 a EUDI Wallet – evropská digitální identita a ověřitelné přihlašovací údaje
  • GovTech #02: OpenID Connect pro vládní identitu – SPID, CIE a osvědčené postupy zabezpečení
  • GovTech #05: Přístupné uživatelské rozhraní pro implementaci PA - WCAG 2.1 AA
  • GovTech #06: Integrace vládního API - SPID, CIE a pagoPA