A Késő címke, teljesítmény és újratanítás
alfejezetben felépített késői címke monitorozó szolgáltatás most a
fejlesztői gépén fut, és a Streamlit mint
ellenőrizhető bemutató streamlit demója ugyanazt a modellt tölti be.
A CI: kódellenőrzés és kis számítás új
környezetben CI-ja csak kis adatos teszteket futtat; a titkok és a
személyes adatok kezelése viszont a helyi konfigurációban, naplókban és
a kifelé irányuló kapcsolatokban dől el. Ebben az alfejezetben egy
fiktív projektet, a northstar-monitor mappát auditáljuk:
megkeressük, hol szivárog titok vagy indokolatlan személyes azonosító,
és megtervezzük a javított határokat.
Konkrét bemenetek:
config.yaml: adatbázis-kapcsolat és
modellartefaktum-útvonal..env.example: a fejlesztőnek szánt, kitöltésre váró
környezeti változók.logs/monitor_sample.log: szándékosan hibás
naplóminta.artifacts/manifest.json: modellverzió és tanítási
cutoff, személyes azonosító nélkül.A megfigyelési egység egy-egy fájlsor. Az audit célja, hogy minden
sorhoz besorolást adjon: inline-secret,
raw-pii, env-reference vagy ok.
Előzetes becslésként, mielőtt a kódot látnád: hány sorban vársz valódi
titkot, hányban nyers személyes azonosítót, és a konfigurációs fájlban
miért lehet nulla valódi titok? Jegyezd fel a becslésedet, a szakasz
végén ellenőrizzük.
A titok (secret) olyan hitelesítő adat, amelynek ismerete önmagában hozzáférést adhat: jelszó, API-kulcs, token, privát kulcs. A biztonságos munkafolyamat elválasztja a titok tárolását a titok hivatkozásától. A kód nem a tényleges jelszót tartalmazza, hanem környezeti változóra hivatkozik:
import os
password = os.environ["DB_PASSWORD"]A fejlesztői .env fájl a titkokat tárolja, ezért nem
kerülhet verziókezelőbe. A .env.example ezzel szemben csak
a változóneveket és replace_me helyőrzőket tartalmaz, és
bekerülhet a repositoryba. Ha a .env mégis commitolódik, a
puszta törlés a Git történetéből nem elég: a titkot érvényteleníteni,
azaz rotálni kell. A környezeti változó sem automatikus védelem; ha a
folyamat a környezetét kiírja egy naplóba, a titok ugyanúgy
szivárog.
A legkisebb jogosultság elve (least privilege) azt mondja, hogy egy
folyamat vagy felhasználó csak a feladatához szükséges minimális jogot
kapja meg. A monitorozó szolgáltatásnak például olvasnia kell a
predikciós táblát, de nincs szüksége DROP TABLE jogra; a
batch predikció írhat a saját eredménytáblájába, de nem törölheti a
nyers rendeléseket. A jogokat célszerű szerepkörönként szétválasztani: a
monitor_readonly adatbázis-felhasználó más kapcsolati
karakterláncot kap, mint a prediction_writer. Az audit
szkript maga is legyen korlátozott: olvashatja a konfigurációt és a
naplómintát, de ne kapjon írási jogot ugyanarra a mappára, ahol a titkok
élhetnek.
Kis kontrollpélda: ha a monitorozó folyamat véletlenül
DELETE FROM predictions utasítást hajtana végre, a least
privilege szerint a művelet már az adatbázis-engedély szintjén
meghiúsul, mielőtt a Python-kód tovább futna.
A naplóredakció (logging redaction) azt jelenti, hogy a naplóüzenet eleve nem tartalmazhat titkot vagy felesleges személyes azonosítót. Nem a napló utólagos cenzúrázása az elsődleges védelem, hanem a naplózó hívás megtervezése: a logger formátumában nem szerepelhet a teljes kérés, a fejléc, az email vagy a jelszó. Helyette a napló kap egy pszeudonim hivatkozást vagy csak egy logikai tényt.
| Rossz napló | Javított napló |
|---|---|
user=customer@example.com token=sk-abc123 |
user_ref=USER-7F3A auth=ok |
password=ChangeMe9! |
db_auth=from-env |
customer_id=CUST-0042 |
customer_ref=USER-7F3A |
A naplóban a redakció célja nem a szépség, hanem az adatminimalizálás:
Biztonsági teszt. A redakció nem lehet kizárólag regex-alapú: a strukturált naplózásnál engedélyezett mezők listájával (allowlist), titokszkenneléssel és jogosultsággal védett naplótárolóval kell kiegészíteni. Az álnevesítés nem tesz személyes adatot automatikusan anonimmá. ha a diagnózishoz elég tudni, hogy a hitelesítés sikeres volt, akkor a jelszó soha ne jelenjen meg.
A megőrzési idő (retention) azt rögzíti, hogy az adott adatfajtát
meddig szabad tárolni, és mi történik utána. A CI:
kódellenőrzés és kis számítás új környezetben alfejezetben az
átmeneti modellartefaktumok rövid megőrzését láttuk; ugyanez a naplókra
és a pszeudonim leképezésekre is igaz. A forever nem
alapértelmezés. Például az Anomália és ritka esemény
felügyelet nélkül emberi anomália-vizsgálati listája hét napig él,
utána csak az összesített P@k marad, az egyedi azonosítók
nélkül. A törlést is tesztelni kell: egy retention_check.py
naponta törölje a határidőnél régebbi fájlsorokat, és a CI ellenőrizze,
hogy a törlés után a lemezhasználat ténylegesen csökken.
A pszeudonimizálás (pseudonymization) az azonosító lecserélése egy
kódra. A customer_id=CUST-0042 értéket leképezhetjük
USER-7F3A alakra egy titkos, hosszú élettartamú kulccsal. A
pszeudonim azonosító azonban nem anonim azonosító: ha a leképező kulcs
vagy a maradék adatok rendelkezésre állnak, a kapcsolat visszaállítható.
Egy ritka rendelési kombináció — például egy szokatlan
basket_minor és egy ritka régió együttese — akár önmagában
is egyetlen ügyfélre mutathat. A pszeudonim ID tehát továbbra is
közvetett azonosításra alkalmas adatként kezelendő.
A szolgáltatói határ (provider boundary) azt a vonalat jelöli, ahol az adat elhagyja a saját környezetünket. Minden kifelé irányuló kérésnél külön szerződés kell: mi az a minimális séma, amelyet a külső szolgáltató megkaphat. Ha a monitorozó szolgáltatás egy külső metrika-API-nak küld állapotot, a kimenő üzenet ne tartalmazza a nyers emailt, a kosár tartalmát vagy a pontos vásárlási időpontot; elég a modellverzió, a pszeudonim eseményazonosító, a predikció és a későbbi kimenet.
Példa kimenő szerződés:
{
"model": "northstar-late-v3",
"event_ref": "evt-7f3a",
"prediction": 0.84,
"outcome_label_days": 9,
"provider_status": "ok"
}A határ túloldalán lévő folyamat naplója is a mi adatvédelmi felelősségünk része, ezért a kimenő sémát ugyanúgy tesztelni kell, mint a belső konfigurációt.
Induljunk ki a következő fájlokból. A munkakönyvtár
~/projects/northstar-monitor, a példa Python 3.12-t és csak
a standard libraryt használ.
config.yaml:
database:
host: db.internal
user: monitor_readonly
password: ${DB_PASSWORD}
model_artifact: artifacts/northstar_late_v3.joblib
retention_days: 30.env.example:
DB_PASSWORD=replace_me
MONITOR_API_KEY=replace_me
logs/monitor_sample.log:
2026-10-20T09:15:00Z INFO monitor start user=customer@example.com customer_id=CUST-0042
2026-10-20T09:15:01Z DEBUG db_connect host=db.internal user=monitor password=ChangeMe9!
2026-10-20T09:15:02Z INFO prediction model=northstar-late-v3 event_id=evt-7f3a score=0.84
2026-10-20T09:15:03Z ERROR api call failed api_key=sk-live-7f3a9c2b1e5f8d4a
A naplóminta szándékosan tartalmazza a két leggyakoribb hibát: nyers emailt és ügyfél-azonosítót, egy beégetett jelszót, valamint egy API-kulcsot. Az audit szkript a fájlsoronkénti besorolást állítja elő. Fontos tervezési döntés, hogy a találat kimenete nem írja ki újra a titok tényleges karaktereit, csak a pozíciót és a hosszt; így az auditjelentés sem válik másodlagos szivárgási forrássá.
config_audit.py:
from pathlib import Path
import re
PROJECT = Path("~/projects/northstar-monitor").expanduser()
AUDIT_FILES = ["config.yaml", ".env.example", "logs/monitor_sample.log"]
SECRET_RULES = {
"inline-password": re.compile(
r"password\s*[:=]\s*([^\s'\"${]+)", re.I
),
"api-key-literal": re.compile(r"\bsk-[A-Za-z0-9_-]{8,}"),
}
PII_RULES = {
"raw-email": re.compile(
r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}"
),
"raw-customer-id": re.compile(r"\bCUST-\d{4}\b"),
}
PLACEHOLDER_OK = re.compile(r"replace[_ -]?me", re.I)
def audit_file(path: Path):
findings = []
for lineno, line in enumerate(
path.read_text(encoding="utf-8").splitlines(), 1
):
if PLACEHOLDER_OK.search(line):
continue
for rule_name, pattern in {**SECRET_RULES, **PII_RULES}.items():
for m in pattern.finditer(line):
findings.append({
"file": str(path.relative_to(PROJECT)),
"line": lineno,
"rule": rule_name,
"match": (
f"col {m.start() + 1}, "
f"length {len(m.group(0))}"
),
})
return findings
for name in AUDIT_FILES:
for finding in audit_file(PROJECT / name):
print(finding)Futtatás Linux/macOS terminálból:
cd ~/projects/northstar-monitor
python config_audit.pyAz alábbi kimenet szemléltető, a megadott bemenetből a szabályok alapján levezetett, nem helyi futásból mért:
{'file': 'logs/monitor_sample.log', 'line': 1, 'rule': 'raw-email', 'match': 'col 46, length 21'}
{'file': 'logs/monitor_sample.log', 'line': 1, 'rule': 'raw-customer-id', 'match': 'col 80, length 9'}
{'file': 'logs/monitor_sample.log', 'line': 2, 'rule': 'inline-password', 'match': 'col 58, length 11'}
{'file': 'logs/monitor_sample.log', 'line': 4, 'rule': 'api-key-literal', 'match': 'col 47, length 24'}
A kézi kontroll négy találatot vár. Az első naplósorban az email és a
CUST-0042 nyers azonosító külön-külön találat; a második
sorban a ChangeMe9! jelszó; a negyedik sorban a
sk-live-... kulcs. A harmadik sor tiszta, mert a predikció
pontszáma és a modellverzió önmagában nem titok és nem közvetlen
személyes azonosító.
A konfigurációs fájlban a password: ${DB_PASSWORD} sor
nem hoz inline-password találatot, mert a szabály
karakterosztálya kizárja a $ jelet. Ez a helyes viselkedés:
a ${DB_PASSWORD} környezeti hivatkozás, nem beégetett
titok. A .env.example sorai a replace_me
helyőrző miatt kimaradnak; a fájl tehát publikálható a repositoryban. A
naplóban szereplő titkok viszont már kompromittáltnak tekintendők: a
jelszót és az API-kulcsot rotálni kell, a naplófájl korábbi másolatait
pedig a retention szabály szerint törölni.
A javított napló lehet:
2026-10-20T09:15:00Z INFO monitor start user_ref=USER-7F3A occurred=1
2026-10-20T09:15:01Z DEBUG db_connect host=db.internal user=monitor auth=from-env
2026-10-20T09:15:02Z INFO prediction model=northstar-late-v3 event_id=evt-7f3a score=0.84
2026-10-20T09:15:03Z ERROR external call failed provider_ref=monitor-gw status=401
Ezen a javított naplón az audit már nulla találatot ad, mert a
USER-7F3A nem illeszkedik a CUST-\d{4}
mintára, és egyetlen sorban sem szerepel email vagy beégetett titok.
A fejlesztő a .env fájlt felvette a
.gitignore-ba, ezért úgy gondolja, a jelszó biztonságban
van. Miért nem elegendő ez, ha a ChangeMe9! érték mégis
megjelenik a monitor_sample.log-ban? Fogalmazd meg a titok
tárolási és a naplóredakciós hiba különbségét.
Bővítsd a config_audit.py szkriptet egy
env-reference szabállyal, amely a
${DB_PASSWORD} alakú környezeti hivatkozásokat jelöli. A
bővített kódban a konfigurációs fájl
password: ${DB_PASSWORD} sora env-reference
besorolást kapjon, de ne inline-password hibát. Add meg a
szabályt reguláris kifejezésként, és indokold, miért nem zavarja össze a
két szabályt a sorrend.
Adott a következő, állítólag már redakált naplósor:
2026-10-20T10:00:00Z INFO user=***@example.com customer_id=CUST-0042
Miért nem tekinthető ez megfelelő adatminimalizálásnak? Nevezd meg a két konkrét hibát, és írd le a javított sort a pszeudonim hivatkozásos mintával.
A termékcsapat a monitorozó napló megőrzési idejét 30 napról 365 napra emelné, hogy hosszabb trendeket vizsgálhasson. Milyen kockázatot hordoz a nyers napló hosszú megőrzése, és milyen feltételekkel fogadnád el a változtatást? Adj konkrét retention-policy vázlatot legalább három adatfajtára, és írd le, milyen ellenőrzést futtatnál a törlési folyamatra.
A .gitignore csak azt akadályozza meg, hogy a
.env a Git historyba kerüljön. Ha a futó folyamat a
környezeti változóból kiolvasott jelszót a logger üzenetébe illeszti, a
titok a naplófájlba, a naplóaggregátorba, a mentésekbe és minden olyan
rendszerbe eljut, ahová a naplót továbbítják. A tárolási hiba tehát a
.env commitolása lenne; ez most nem történt meg. A
naplóredakciós hiba viszont független attól, hogy a titok honnan
érkezett: a jelszó értékének egyáltalán nem szabad megjelennie a
naplóban. A két hiba külön kezelendő, és a .gitignore
megléte nem mentesíti a naplózó hívást.
Az új szabály:
ENV_REF = re.compile(r"\$\{[A-Za-z_][A-Za-z0-9_]*\}")Az audit_file ciklusában először a környezeti
hivatkozást kell vizsgálni. Ha password: ${DB_PASSWORD}
szerepel, a ${DB_PASSWORD} illeszkedik az
ENV_REF mintára, ezért env-reference találatot
adunk. Az inline-password szabály karakterosztálya már
kizárja a $ jelet, így ugyanaz a sor nem lesz hibás
jelszó-találat is. A sorrend azért nem zavaró, mert a két szabály
különböző részt illeszt: az env-reference a
${...} blokkra, az inline-password pedig csak
akkor talál, ha a jelszó értéke $ nélkül kezdődik. A
bővített ciklus a találatot {"rule": "env-reference", ...}
formában írja ki, a konfigurációs fájlban tehát nem hiba, hanem
ellenőrzött hivatkozás lesz.
Ellenőrző invariant: a bővített kód a megadott
config.yaml-ra pontosan egy env-reference
találatot ad, a logs/monitor_sample.log-ra pedig továbbra
is négy hibát; a két halmaz diszjunkt, mert egyetlen sor sem kerülhet
egyszerre mindkét halmazba a $ kizárása miatt.
A ***@example.com maszk két okból is rossz. Egyrészt a
domain megmarad, így a *** hosszával és a környező mezőkkel
együtt az email gyakran visszafejthető; másrészt a
CUST-0042 nyers azonosító korlátlanul összekapcsolható a
rendelési előzménnyel. A megfelelő sor nem tartalmaz emailt vagy nyers
ügyfél-azonosítót:
2026-10-20T10:00:00Z INFO user_ref=USER-7F3A event_seen=1
A USER-7F3A pszeudonim hivatkozás leképezése külön,
hozzáférés-korlátozott, rövid retentionű táblában él. A diagnózishoz
csak az kell, hogy egy ismert felhasználói esemény megtörtént, nem pedig
az, hogy ki volt az.
A nyers napló 365 napos megőrzése növeli az expozíciós időt: minden későbbi adatszivárgás több naplósorra terjed ki, és a pszeudonim leképezés is tovább él. A változtatást csak akkor érdemes elfogadni, ha a hosszú megőrzés nem a nyers azonosítókra, hanem az aggregált statisztikára vonatkozik. Lehetséges policy:
| Adatfajta | Retention |
|---|---|
| Nyers monitorozó napló | 30 nap |
| Pszeudonim hivatkozásos napló | 90 nap |
| Aggregált napi statisztika | 365 nap |
| Modellartefaktum és manifest | 365 nap |
A modellartefaktum csak akkor maradhat 365 napig, ha a modell nem
tárol közvetlen ügyfél-azonosítót, és a betöltési határt a Streamlit mint ellenőrizhető bemutató
modellkártyája rögzíti. A törlési folyamatra naponta futó
retention_check.py ellenőrzi, hogy a határidőnél régebbi
fájlok eltűntek, és a lemezhasználat a várt mértékben csökkent. A
provider boundary felé küldött üzenetbe a hosszú trendhez is csak
aggregált értékek kerülnek: modellverzió, nap, predikcióátlag,
eseményszám.
A leggyakoribb tévedés azt hinni, hogy a pszeudonim azonosító
automatikus anonimitást ad. A CUST-0042 →
USER-7F3A leképezés attól még visszafordítható, ha a
leképező kulcs vagy a maradék adatok hozzáférhetők. A modell maga is
hordozhat érzékeny információt: ha egy ritka basket_minor
és region kombináció egyetlen ügyfélre mutat, akkor a
pszeudonim azonosítóval ellátott predikciós sor közvetett azonosításra
alkalmas marad. Ez visszavezet az Anomália és ritka
esemény felügyelet nélkül anomáliakeresés tanulságához: az emberi
vizsgálati lista is csak a feltétlenül szükséges mezőket tartalmazhatja,
mert a pontszám-rangsor önmagában nem mentesít a minimalizálás alól. A
CI: kódellenőrzés és kis számítás új
környezetben rövid artifact retentionje ugyanezt az elvet erősíti: a
törlési határidő a védelem része, nem utólagos kényelmi lépés.
Önellenőrzés: futtasd az auditot a saját repositorydra a
password, token, secret,
api_key, AKIA és email mintázatokra. Ha egy
találat a naplóban vagy a konfigurációban van, ne a találati sort töröld
először, hanem kérdezd meg, miért volt ott egyáltalán, és rotáld a
titkot, ha az érték valaha elhagyta a gépet.
A titkok, hozzáférések és személyes adatok határait már a kódolás
előtt meg kell jelölni. A környezeti változó a titok hivatkozására való,
a .env.example csak a neveket publikálja; a naplóba soha
nem kerülhet jelszó, kulcs, email vagy nyers ügyfél-azonosító. A
legkisebb jogosultság, a naplóredakció és a megőrzési határidők együtt
csökkentik a szivárgási felületet. A pszeudonim azonosító hasznos, de
nem anonim, és a modell is hordozhat érzékeny mintázatot. A szolgáltatói
határon csak a minimális szerződéses séma mehet át. A következő lépésben
a IX. ellenőrző pont ezekből a döntésekből állít össze működő rendszert
és bizonyítékokat.