Cosa fa#
Git memorizza tutti i dati come oggetti immutabili nel database .git/objects/. Ogni oggetto e' identificato da un hash SHA-1 del suo contenuto. Una volta che un oggetto esiste nel database, non sparisce mai — anche se rimuovi un file o cancelli un commit, l'oggetto rimane fino al garbage collection.
TL;DR#
Quando fai git commit:
file.txt (contenuto)
│
▼
blob object ← il contenuto del file
│
▼
tree object ← la struttura della directory
│
▼
commit object ← punta al tree + metadati (autore, data, messaggio)
│
▼
.git/objects/ab/cd1234... ← tutti e tre salvati come file
Se rimuovi file.txt e fai un nuovo commit:
il blob del vecchio file.txt RIMANE in .git/objects
e' raggiungibile dalla storia del repoI quattro tipi di oggetti#
1. Blob — contenuto di un file#
git cat-file -t abc123 # mostra il tipo → blob
git cat-file -p abc123 # mostra il contenuto del fileUn blob e' il contenuto puro di un file — senza nome, senza path. Due file identici condividono lo stesso blob.
2. Tree — struttura di una directory#
git cat-file -p HEAD^{tree} # albero del commit corrente
# 100644 blob abc123 README.md
# 100644 blob def456 key.txt
# 040000 tree ghi789 src/Un tree associa nomi di file ai blob e ai subtree.
3. Commit — snapshot con metadati#
git cat-file -p HEAD
# tree abc123def456...
# parent xyz789...
# author Nome <email> 1234567890 +0000
# committer Nome <email> 1234567890 +0000
#
# messaggio del commitUn commit punta a un tree (lo snapshot), al commit padre, e contiene i metadati.
4. Tag — puntatore con nome#
git cat-file -p v1.0 # tag annotato
# object abc123...
# type commit
# tag v1.0
# tagger Nome <email>
#
# Release version 1.0Un tag leggero e' solo un riferimento a un hash. Un tag annotato e' un oggetto vero con metadati. In Bandit 30 il tag puntava direttamente a un blob — caso insolito ma possibile.
Dove vivono gli oggetti#
.git/
├── objects/
│ ├── ab/
│ │ └── cd1234... ← primi 2 char = directory, resto = filename
│ ├── pack/
│ │ ├── pack-xxx.pack ← oggetti compressi insieme
│ │ └── pack-xxx.idx ← indice del pack
│ └── info/
├── refs/
│ ├── heads/
│ │ ├── master ← punta all'ultimo commit di master
│ │ └── dev ← punta all'ultimo commit di dev
│ └── tags/
│ └── secret ← punta a un oggetto tag o blob
└── HEAD ← punta al branch correntePerche' i segreti sopravvivono#
Commit A: README.md contiene "password: abc123"
│
▼
Commit B: README.md aggiornato → "password: xxxxxxxxxx"
│
▼
stato attuale: "password: xxxxxxxxxx"Il commit A esiste ancora in .git/objects. E' raggiungibile con:
git log --all -p | grep -i "password"
git diff <hash-commit-A> <hash-commit-B>
git show <hash-commit-A>:README.mdL'unico modo per rimuovere un segreto dalla storia e' riscrivere la storia con git filter-branch o git filter-repo — e anche in quel caso, chiunque abbia clonato il repo prima ha ancora la versione con il segreto.
I quattro posti dove nascondersi in git#
| Posto | Comando per trovare | Livello Bandit |
|---|---|---|
| Storia commit | git log -p | 28 |
| Branch nascosti | git branch -a | 29 |
| Tag | git tag + git show | 30 |
| Stash | git stash list | — |
Pack files#
Quando un repository cresce, git comprime gli oggetti in pack files:
ls .git/objects/pack/
# pack-abc123.pack ← oggetti compressi
# pack-abc123.idx ← indice per trovare gli oggetti nel pack
# Estrarre il contenuto di un pack (per analisi forense)
git verify-pack -v .git/objects/pack/pack-abc123.idxI pack files contengono tutti gli oggetti storici — inclusi quelli di branch cancellati e commit rimossi con git reset.
Scenario Reale#
Un developer commette per errore una chiave API in un commit, se ne accorge e la rimuove nel commit successivo. La chiave e' ancora accessibile:
# Trovare la chiave nella storia
git log --all -p | grep -i "api_key\|secret\|password\|token"
# Verificare quando e' stata aggiunta e rimossa
git log --all --oneline | head -20
# Leggere il file com'era in quel commit
git show abc123:config.pyStrumenti professionali che fanno questo automaticamente: truffleHog, git-secrets, gitleaks. GitHub ha un sistema integrato che notifica automaticamente quando trova pattern di chiavi note (AWS, GCP, Stripe...) nei push.
Dove l'ho incontrato#
- bandit-28 — secret scanning nella storia dei commit
- bandit-29 — branch nascosti con credenziali
- bandit-30 — tag che puntano a blob con password
- bandit-31 — .gitignore bypass per push
Collegato a#
- system — categoria
- incidents — categoria
- git — comando
- secret-scanning — applicazione pratica
- bandit-28 — primo secret scanning



