Ekspozycja .git/HEAD
ważność wysokamierzonysposób exposure
Czy repozytorium Git jest publicznie dostępne. Ujawnia kod, klucze i historię commitów.
WIKI
artykuł z 16.09.2026 · źródeł 10Ekspozycja .git/HEAD to parametr monitoringu stron internetowych, który sprawdza, czy pod adresem /.git/HEAD serwer WWW oddaje plik z katalogu repozytorium Git, zamiast odmówić dostępu albo zgłosić jego brak. Dostępny HEAD jest sygnałem, że cały katalog .git, a z nim historia projektu, może być do pobrania. Pomiar kończy się jednym z trzech wyników: plik dostępny publicznie, plik niedostępny albo nie udało się uzyskać odpowiedzi. W zwykłym repozytorium Git katalog .git leży w katalogu głównym drzewa roboczego [1].
Jak działa
Katalog .git ma stały układ. HEAD wskazuje aktywną gałąź w przestrzeni refs/heads/ albo, w stanie odłączonym, zapisuje wprost konkretny commit [1]. Pliki refs/heads/<nazwa> przechowują ostatni commit każdej gałęzi, a packed-refs zawiera te same informacje w zwartej postaci [1]. Plik config to konfiguracja repozytorium, logs zapisuje zmiany referencji, a index to bieżący indeks [1].
Treść projektu leży w objects. Git jest magazynem adresowanym treścią: każdy obiekt ma sumę SHA-1, a zapisany jest skompresowany zlib w podkatalogu nazwanym dwoma pierwszymi znakami sumy i w pliku nazwanym pozostałymi 38 znakami [2]. Commit wskazuje drzewo katalogów projektu i commit poprzedni, więc od jednego commita da się przejść całą historię [2]. Wiele obiektów może też leżeć w paczkach w objects/pack [1].
Znajomość układu wystarcza do odtworzenia repozytorium bez spisu katalogu. Adres każdego obiektu wynika z jego sumy [2], a plik index zaczyna się sygnaturą DIRC i dla każdego śledzonego pliku zawiera sumę obiektu oraz ścieżkę względem katalogu głównego projektu [3].
Narzędzie git-dumper opisuje ten mechanizm wprost. Gdy serwer pokazuje spis katalogu, pobiera .git rekurencyjnie [4]. W przeciwnym razie pobiera typowe pliki (.git/HEAD, .git/index i inne), szuka referencji w HEAD, logs/HEAD, config i packed-refs, zbiera sumy obiektów z packed-refs, index, refs i logs, pobiera obiekty rekurencyjnie po rodzicach commitów, a na końcu odtwarza drzewo robocze poleceniem git checkout . [4].
Standardy i specyfikacje
- gitrepository-layout opisuje pliki i katalogi repozytorium, w tym
HEAD,config,refs,packed-refs,logs,indexiobjects[1]. - Format indeksu Git definiuje nagłówek z sygnaturą
DIRC, numerem wersji i liczbą wpisów oraz wpisy z sumą obiektu i ścieżką pliku [3]. - CWE-527 („Exposure of Version-Control Repository to an Unauthorized Control Sphere”) to repozytorium CVS, Git lub innego systemu kontroli wersji zapisane w katalogu, archiwum albo zasobie dostępnym dla nieuprawnionych [6]. Jest potomkiem CWE-552 [6]. MITRE wymienia skutki: nazwy użytkowników, nazwy plików, ścieżki, adresy IP i szczegółowe różnice zmian, które mogą ujawnić fragmenty kodu nigdy nieprzeznaczone do publikacji [6].
Zagrożenia i skutki
Skala. W lipcu 2015 roku zespół Internetwache przeskanował listę miliona popularnych domen Alexa i znalazł około 9700 publicznie dostępnych repozytoriów Git [5]. Opisał odtwarzanie repozytorium bez spisu katalogu przez pobieranie obiektów wskazanych w plikach takich jak refs/heads/master i analizę kolejnych commitów i drzew [5].
Sekrety w konfiguracji i historii. Ten sam raport zauważa, że ponad 100 projektów zapisało w .git/config adres repozytorium razem z nazwą użytkownika i hasłem do uwierzytelniania HTTP [5]. Takie dane dawały dostęp do kont w usługach hostujących repozytoria, a w części repozytoriów zespół znalazł dane dostępowe do AWS, baz danych, SMTP i FTP [5]. Plik usunięty z bieżącej wersji projektu nadal istnieje w starszych commitach, bo każdy commit wskazuje własne drzewo [2].
Usunięcie z historii nie wystarcza. GitHub zaleca, by przy wycieku hasła, tokenu lub klucza najpierw go unieważnić albo wymienić, a dopiero potem rozważać przepisywanie historii [7]. Usunięcie danych z historii wymaga koordynacji ze wszystkimi, którzy mają kopię repozytorium [7].
Blokada w serwerze. Domyślna konfiguracja Apache odmawia dostępu tylko do plików pasujących do .ht* [10], więc katalog .git nie jest nią objęty. Dyrektywa DirectoryMatch stosuje zestaw reguł do katalogów, których ścieżka pasuje do wyrażenia regularnego [9]; Internetwache proponuje na niej regułę Require all denied dla ścieżek zawierających /.git/ albo trzymanie repozytorium poza katalogiem dokumentów [5]. Przykładowa konfiguracja nginx dla WordPressa zawiera blok location ~ /\. z deny all, opisany jako blokada plików ukrytych [8].
Przykłady
| Sytuacja | Odpowiedź na GET /.git/HEAD | Znaczenie |
|---|---|---|
| Repozytorium w katalogu dokumentów, bez reguł odmowy [1] [10] | 200 z referencją gałęzi lub sumą commita | historia do odtworzenia [4] |
| Jak wyżej, ze spisem katalogu [4] | 200, a /.git/ pokazuje listę plików | pobranie rekurencyjne [4] |
| Spis katalogu wyłączony [3] [4] | 200 | odtworzenie przez index i sumy obiektów |
Apache z regułą DirectoryMatch dla .git [5] [9] | odmowa dostępu | repozytorium istnieje, nie jest podawane |
nginx z blokiem location ~ /\. [8] | odmowa dostępu | blokada obejmuje wszystkie pliki ukryte |
| Repozytorium poza katalogiem dokumentów [5] | brak pliku | nic do pobrania |
Hasło w .git/config lub w starym commicie [5] | 200 | sekret do unieważnienia [7] |
- [1] gitrepository-layout — Git Repository Layout · Git Project
- [2] Pro Git, 10.2 Git Internals — Git Objects · Git Project / Scott Chacon, Ben Straub
- [3] Git index format · Git Project
- [4] git-dumper — README · arthaud/git-dumper
- [5] Don't publicly expose .git or how we downloaded your website's sourcecode — An analysis of Alexa's 1M · Internetwache.org
- [6] CWE-527: Exposure of Version-Control Repository to an Unauthorized Control Sphere · MITRE
- [7] Removing sensitive data from a repository · GitHub Docs
- [8] Nginx — Advanced Administration Handbook · WordPress.org
- [9] Apache Core Features — dyrektywa DirectoryMatch · Apache Software Foundation
- [10] httpd.conf.in — domyślna konfiguracja Apache HTTP Server · Apache Software Foundation
STATS
wyniki z systemu · tylko liczby zbiorczeTen parametr nie zapisuje odczytu per strona — przebieg zostawia tylko ustalenia, więc liczby sprawdzonych stron i wyników „ok” system nie ma. Pojawią się po wdrożeniu zapisu przebiegów per strona per parametr.
Ten parametr nie otworzył jeszcze żadnego ustalenia w bazie LAB247. Pusto nie znaczy „czysto” — znaczy, że nic nie przekroczyło reguły.
- Przebiegów
- 33sposób exposure
- W 30 dni
- 0przebiegów
- Ostatni
- 19.09.2026start przebiegu
- Stron w przebiegach
- —33 przebiegów bez liczby stron
CARD
karta z kodu CREATO_PING, odczyt 22.09.2026Jak ten parametr trafia do ClickUpa: wynik sondy to linia w subtasku 2. Pomiar, a usterka, którą otwiera — linia w subtasku 1. Podsumowanie (z identyfikatorem CWE i kategorią OWASP), skąd liczy się wskaźnik zagrożenia i status strony. Projekcja z bazy skanera odświeża oba subtaski co godzinę. Do 22.09 każde ustalenie dostawało własny subtask i status ze starego słownika (PODATNA, WARNING…) — te słowa zostały jeszcze w części opisów pomiaru niżej jako język wewnętrzny sond.
| Obszar i ważność | Bezpieczeństwo · wysoka (B12) |
|---|---|
| Workflow i częstotliwość | creato_ping_security_v2 · P3 działa na LAB247Skaner 2.0, rytm „p3” / „bezpieczenstwo”: premium co 6 h, reszta co 24 h (tier 1–3), co 9 dni (tier 4) · Dokładny skan. dns_integrity porównuje NS, MX, SPF i DMARC z wzorcem z poprzedniego przebiegu. |
| Sonda i helper | sposób exposure · CREATO_PING/helpers/exposure_check.py |
| Testy |
|
| Typ wyniku | 0 albo 1 ustalenie exposure_git |
| Jednostka | brak — wynik jakościowy; w dowodzie ścieżka, kod HTTP i długość treści |
| Próg i reguła | Timeout odpowiedzi 15 s, połączenia 5 s. Reguła: kod 200 + niepusta treść = exposure_git. |
| Gdzie leżą pokrętła | CREATO_PING/helpers/exposure_check.py → PROBES (/.git/HEAD, reguła 200+nonempty), _soft_200CREATO_PING/helpers/config.py → TIMEOUTS["exposure"], TIMEOUTS["security_connect"]CREATO_PING/helpers/security_runner.py → do_writera (bramka gate z polityki; sito powagi zniknęło 18.09)CREATO_PING/config/incident_policy.toml → [finding] exposure_git |
| Retencja odczytów | Surowy pomiar jako NIEZMIENNY wiersz tabeli `odczyty` w bazie skanera (wejście, wyjście, dowody, sha karty sondy i helpera, commit kodu) — trigger odrzuca każdą zmianę. Okno retencji z kohorty: 90 dni premium, 30 dni reszta; poza oknem zostają odczyty z werdyktem problemu, akcją albo etykietą, bo replay reguł ich potrzebuje (`skaner/retencja.py`). Do LAB247 ten kanał nie idzie (do bazy serwisu jadą dziś tylko sprawdzenia dostępności), więc poza odczytem zostaje werdykt w tabeli `werdykty` i sprawa w tabeli `sprawy`. |
| Znane rozbieżności |
|
| Karta przepisana z | CREATO_PING/helpers/exposure_check.py · CREATO_PING/helpers/config.py · CREATO_PING/config/incident_policy.toml · CREATO_PING/helpers/security_runner.py · CREATO_PING/skaner/adapter_clickup.py · CREATO_PING/skaner/sprawy.py · CREATO_PING/skaner/klasyfikacja.py · CREATO_PING/skaner/usterki.toml · CREATO_PING/skaner/ocena.py · odczyt 22.09.2026 |
exposure_git: bramka polityki otwarta (gate=true) → sprawa w skanerze → Repozytorium .git dostępne publicznie — waga 3 → ocena 5–6 (żółty), status strony „sredni”, dopóki sprawa jest otwarta. Kolejne przebiegi odświeżają opis i datę; helper nie wysyła sygnału zamknięcia.
| Kod | Finding | Severity | Pewność | Skutek | Finding / alarm | Waga → ocena | Co zrobić |
|---|---|---|---|---|---|---|---|
| B12.01 | Publiczne repozytorium .gitexposure_git | Krytyczna | Ewidentne | otwarta podatność | tak / tak | waga 3 → ocena 5–6 (żółty) | Zablokuj dostęp do katalogu .git na serwerze. |
| Kod i klucz | B12.01 · exposure_git · w tabeli typów |
|---|---|
| Co znaczy | Plik /.git/HEAD jest dostępny — wyciek kodu, kluczy i historii. |
| Kiedy powstaje | GET /.git/HEAD (bez podążania za przekierowaniami, cap 65 536 B): status == 200 i pierwsze 5000 znaków treści po strip() niepuste (check "200+nonempty"). Nie sprawdza treści 'ref:'.Ocena: _evaluate :211-212. Działa tylko wtedy, gdy strażnik soft-200 (:348-370) nie zadziałał. Dedup (domain, finding_type) :373-383. Brak wpisów w wyjatki_falszywych_alarmow.toml. |
| Severity · pewność | Krytyczna · Ewidentne |
| Skutek dla strony | otwarta podatność · zakłada Finding · alarm tak |
| Waga → ocena | waga 3 → ocena 5–6 (żółty) |
| Klasyfikacja | CWE: CWE-527 (Exposure of Version-Control Repository to an Unauthorized Control Sphere)OWASP Top 10:2025: A01:2025 Broken Access ControlOWASP ASVS 5.0.0: 13.4.1OCSF 1.9.0: Vulnerability Finding (class_uid 2002) |
| Rekord OCSF | finding_info.analytic.uid = "B12.01" · analytic.name = "exposure_git" · finding_info.types = ["B12 Ekspozycja .git/HEAD"] |
| Co zrobić | Zablokuj dostęp do katalogu .git na serwerze. |
| Wersje typu |
|
LOOP
pętli: 2Trafności jeszcze nie liczymy: zamknięcie subtaska nie niesie dziś werdyktu (prawdziwy / fałszywy / nie dało się sprawdzić). Cztery liczby pojawią się, gdy każde zamknięcie dostanie werdykt — to warunek startu pętli 1.
Sygnał: Tarcze antybotowe hostingu, strony parkingowe i serwery catch-all odpowiadają 200 na każdą ścieżkę, więc sondy oparte na kodzie 200 dawały „wystawiony” wszędzie. Równocześnie sondy na wolnych hostingach odpowiadały po około 15 s, a limit 5 s ucinał je bez śladu. · dotyczy też: Ekspozycja .env, Kopie wp-config.php, Ekspozycja .htaccess.bak, Dostępność xmlrpc.php, REST API: enumeracja użytkowników, Dostępność readme.html, Dostępność wp-cron.php, Ekspozycja kopii zapasowych, Ekspozycja zrzutu bazy, Ekspozycja manifestów dev, Ekspozycja .DS_Store
- 1. Zbiórkaręcznieodczyt z nocnego skanu, bez zapisu per strona
- 2. Werdyktręcznieprzegląd skanera: 200 na wszystko i ciche timeouty to fałszywe wyniki
- 3. Miarabrakliczników trafności wtedy nie było
- 4. Kartabrakkarta parametru powstała 2026-09-16
- 5. Zmianazrobionestrażnik ścieżki, 15 s, logowanie błędów
- 6. Replaybrakbrak zapisanych odczytów do replayu
| Data | Zmiana | Powód | Wpływ na system | Commit · replay |
|---|---|---|---|---|
| 2026-09-05 | Przed sondami GET losowej, nieistniejącej ścieżki; odpowiedź 200 z treścią = zero sond i jedno ustalenie exposure_scan_unreliable (niska powaga, bez subtaska).helpers/exposure_check.py · config/incident_policy.toml | Odpowiedź 200 na losową ścieżkę dowodzi, że kod 200 na tym serwerze nic nie znaczy; dalsze sondowanie tarczy grozi banem IP monitoringu. | cały system Obejmuje wszystkie sondy ekspozycji naraz i wprowadza typ „nie dało się sprawdzić”; tę samą konwencję dostało wykrywanie wtyczek. Ustalenie ma niską powagę, więc filtr przebiegu je odrzuca i w produkcji nie zostawia śladu. | b0fc77cbez replayubez zapisu zatwierdzenia |
| 2026-09-05 | Limit odpowiedzi z 5 s na 15 s i połączenia z 2 s na 5 s, przeniesione do config.py; błąd sieci sondy logowany zamiast cichego pustego wyniku; do 4 sond naraz na stronę.helpers/config.py · helpers/exposure_check.py · helpers/html_fetch.py · helpers/wp_fingerprint.py | Wolne hostingi odpowiadały po około 15 s, a pusty wynik po timeoucie był nieodróżnialny od „sprawdzone, czysto”. | cały system Limit połączenia 5 s przejęło wspólne pobieranie HTML całego skanu bezpieczeństwa i wykrywanie wersji — strony z wolnym TLS przestały wypadać z całego skanu. | b0fc77cbez replayubez zapisu zatwierdzenia |
Sygnał: Brak pamięci podczas skanu: publiczny debug.log jednej ze stron był bardzo duży, a klient HTTP buforował całą odpowiedź. · dotyczy też: Sitemap.xml, Kopie wp-config.php, Ekspozycja .env, Ekspozycja .htaccess.bak, Dostępność xmlrpc.php, REST API: enumeracja użytkowników, Dostępność readme.html, Dostępność wp-cron.php, Ekspozycja kopii zapasowych, Ekspozycja zrzutu bazy, Ekspozycja manifestów dev, Ekspozycja .DS_Store
- 1. Zbiórkaręcznieodczyt z nocnego skanu, bez zapisu per strona
- 2. Werdyktręcznieawaria procesu przypisana ścieżce debug.log
- 3. Miarabrakliczników trafności wtedy nie było
- 4. Kartabrakkarta parametru powstała 2026-09-16
- 5. Zmianazrobionestrumień z limitem 64 KB
- 6. Replaybrakbrak zapisanych odczytów do replayu
| Data | Zmiana | Powód | Wpływ na system | Commit · replay |
|---|---|---|---|---|
| 2026-07-19 | Odpowiedź sondy czytana strumieniem do 64 KB; do oceny idzie pierwsze 5000 znaków.helpers/exposure_check.py · helpers/html_fetch.py · helpers/sitemap_check.py | Do rozpoznania ekspozycji wystarcza początek pliku; zmiana dotyczy sposobu czytania, nie werdyktu. | cały system Brak pamięci w helperze zatrzymywał cały proces skanu bezpieczeństwa partii stron; ten sam limit dostało sprawdzenie sitemap. | 343b699bez replayubez zapisu zatwierdzenia |