Po bëja një rishikim të përgjithshëm të projektit tim, në kërkim të gjërave të harruara. Gjeta një që nuk do ta kisha dashur: një skedar konfigurimi për deploy, me brenda çelësin që nënshkruan token-at e hyrjes, fjalëkalimin e administratorit dhe fjalëkalimin për dërgimin e email-eve. Në tekst të hapur, në historinë e git-it, në tri degë.
Si ndodhi
Skedari ishte te .gitignore. Problemi është se përfundoi aty pasi ishte bërë commit një herë, dhe .gitignore injoron vetëm skedarët që nuk ndiqen ende: një skedar që ndiqet tashmë vazhdon të ndiqet, ndryshim pas ndryshimi, edhe nëse emri i tij është në listë.
Repository-ja ishte private, por private nuk do të thotë e sigurt: e shohin bashkëpunëtorët, shërbimet e CI-së, backup-et dhe çdo kompjuter ku është klonuar.
Heqja nga e gjithë historia
Fshirja e skedarit me një commit të ri nuk ndihmon: ai mbetet në të gjitha commit-et e mëparshme. Duhet rishkruar historia e çdo dege. Përdora git filter-repo mbi një klon mirror, pastaj bëra push të detyruar dhe verifikova që asnjë commit nuk e përmbante më:
# 1. Stop tracking the file (it stays on disk)
git rm --cached .env.deploy
git commit -m "Stop tracking .env.deploy"
# 2. Rewrite the history of EVERY branch on a mirror clone
git clone --mirror git@github.com:me/my-repo.git
cd my-repo.git
git filter-repo --path .env.deploy --invert-paths
git push --force --mirror
# 3. Verify: no commit on any branch may still contain it
git log --all --oneline -- .env.deploy
Pas një rishkrimi të tillë, kushdo që ka një kopje lokale duhet ta rirreshtojë me remote-n e ri: commit-et e vjetra nuk duhen ripublikuar më.
Pse nuk mjafton
Këtu qëndron mësimi më i rëndësishëm. Edhe pas rishkrimit, commit-et e vjetra mund të mbeten të arritshme: GitHub, për shembull, mban referenca të brendshme për pull request-et që një push i detyruar nuk mund t'i fshijë. Dhe nuk dihet kush e ka shkarkuar repository-n në të kaluarën.
Heqja e një sekreti nga historia shërben që situata të mos përkeqësohet; e vetmja gjë që e zgjidh është ndryshimi i tij. Në praktikë:
- Një çelës i ri për nënshkrimin e token-ave: i zhvlerëson të gjitha sesionet ekzistuese, ndaj të gjithë duhet të hyjnë sërish. Është çmimi që paguhet.
- Një fjalëkalim i ri për administratorin.
- Revokimi i fjalëkalimit të aplikacionit të postës dhe krijimi i një të riu.
- Përditësimi i variablave në çdo mjedis ku përdoren.
Herën e dytë, e njëjta skemë
Disa javë më vonë gjeta të njëjtin problem në një formë tjetër: një skript shumë i rehatshëm për instalimin e serverit, që i shkruante kredencialet drejtpërdrejt në skedarin e konfigurimit. Është skema që përsëritet: skripti i rehatisë që mban sekretet që të mos kesh nevojë të konfigurosh asgjë. E rishkrova që t'i gjenerojë çelësat në moment dhe t'i kërkojë fjalëkalimet kujt e ekzekuton:
# Before: secrets written into a script tracked by git
# JWT_SECRET=hardcoded-value...
# After: generated or asked at runtime, never committed
JWT_SECRET="$(openssl rand -base64 48)"
read -rsp "SMTP password: " SMTP_PASS; echo
umask 077 # the .env file is readable by its owner only
printf 'JWT_SECRET=%s\nSMTP_PASS=%s\n' "$JWT_SECRET" "$SMTP_PASS" > .env
Zakonet që e parandalojnë përsëritjen
- Një
.env.examplei bërë commit, me emrat e variablave dhe pa vlera;.env-i i vërtetë nuk hyn kurrë në repository. - Një skaner sekretesh para çdo commit-i, si gitleaks në një hook pre-commit: e bllokon commit-in para se të bëhet dëmi.
- Mbrojtja e push-eve që ofrojnë GitHub dhe GitLab, e cila refuzon push-et që përmbajnë sekrete të njohshme.
- Kurrë sekrete në skripte: gjenerohen në runtime ose lexohen nga mjedisi.
- Një kontroll periodik i historisë: një sekret që e gjen ti është një incident i vogël, një që e gjejnë të tjerët jo.
Në përmbledhje
Një sekret në një repository duhet trajtuar si i komprometuar në çastin që e gjen. Rishkrimi i historisë është i nevojshëm, por jo i mjaftueshëm: korrigjimi i vërtetë është ndryshimi i çdo kredenciali të ekspozuar. Pastaj ia vlen ta bësh problemin të pamundur për t'u përsëritur, me skanerë dhe rregulla të qarta. Praktika të tjera sigurie kam mbledhur në këtë udhëzues.