Wyciek danych uwierzytelniających w OpenCode — praktyczny plan reakcji dla developera
Wyciek danych uwierzytelniających w OpenCode to dobra okazja, by przećwiczyć rotację sekretów, ograniczanie zakresów tokenów i skanowanie repozytoriów pod kątem wycieków.

Informacja o tym, że narzędzie, z którego korzysta część zespołu, mogło ujawnić dane uwierzytelniające, brzmi jak zadanie dla działu security. W praktyce w małych i średnich zespołach nie ma działu security — jest osoba, która pierwsza przeczyta komunikat i to ona musi w ciągu kilku godzin podjąć decyzje. Poniższy tekst jest o tym, jak te decyzje poukładać w kolejności, która minimalizuje ryzyko.
Punktem wyjścia jest komunikat opublikowany przez zespół OpenCode: OpenCode Hacked: User credentials leaked. OpenCode to narzędzie terminalowe do pracy z modelami języka w codziennym developmentie, a tego typu programy z natury rzeczy przechowują poświadczenia — do dostawcy modelu, do repozytoriów, czasem do usług zewnętrznych, z których korzystają agenci. Właśnie te poświadczenia są łakomym kąskiem: często mają szerokie uprawnienia i długi czas życia.
Zamiast czekać na pełny raport, potraktuj sytuację jako ćwiczenie: przejdź przez inwentaryzację, rotację i weryfikację tak, jakby skradzione dane miały trafić w ręce osoby, która wie, co z nimi zrobić. Nawet jeśli okaże się, że ryzyko było ograniczone, zostaje po tym porządek w sekretach — a to jest realna wartość.
Co wiemy, a czego nie wiemy
Komunikat podlinkowany powyżej to krótka informacja o wycieku poświadczeń użytkowników. Nie znajdziesz w nim odpowiedzi na pytania, które dla oceny ryzyka są najważniejsze: jaki był wektor, jakiego typu dane wyciekły (tokeny sesyjne, klucze API, hasła), w jakim oknie czasowym i czy atakujący zdążyli ich użyć. Tego nie da się wywnioskować z samego wpisu i nie ma sensu zgadywać.
Co możesz ustalić samodzielnie w ciągu kilkunastu minut:
- czy z narzędzia korzysta ktoś w zespole i w jakiej wersji (także w kontenerach deweloperskich i na maszynach CI),
- jakie poświadczenia były wtedy w konfiguracji narzędzia i czy mają prawo działać teraz,
- czy w tym samym okresie ktoś nie podłączał do narzędzia klucza o szerokich uprawnieniach „na chwilę”, który został na dłużej.
Praktyczna reguła, która zdejmuje z ciebie ciężar oceny: każde poświadczenie, które w momencie wycieku miało prawo działać, traktuj jako ujawnione. Reszta to optymalizacja kosztów rotacji, a nie dyskusja o ryzyku. Odwrotna postawa — „pewnie nic się nie stało, bo nie dostaliśmy alertu” — jest najczęstszym błędem w takich sytuacjach, bo alerty dotyczą ruchu nietypowego, a nie ruchu skradzionym kluczem z domu atakującego.
Inwentaryzacja: gdzie faktycznie trzymacie sekrety
Zanim cokolwiek unieważnisz, zrób listę. Bez niej rotacja skończy się na trzech oczywistych tokenach i pominięciu czwartego, który został w skrypcie pomocniczym.
Miejsca, które trzeba sprawdzić:
- Lokalne pliki env i konfiguracje narzędzi —
.env,.env.local, katalog konfiguracyjny w katalogu domowym (na Linuksie zwykle~/.config/<nazwa-narzędzia>), pliki auth z tokenami. - CI/CD — sekrety repozytorium, sekrety organizacji, zmienne w pipeline’ach, klucze deploya.
- Historia gita — plik usunięty commitem nadal żyje w historii i w forkach.
- Obrazy kontenerów i artefakty — sekret „wbity” w warstwę obrazu jest w rejestrze tak samo dostępny jak w repo.
- Notatki, tickety, wątki na czacie — miejsce, w którym najczęściej ląduje token „do testów”.
Szybki przegląd bieżącego drzewa i historii:
# podejrzane wzorce w plikach roboczych
rg -n --hidden -g '!.git' \
-e 'sk-[A-Za-z0-9]{20,}' \
-e 'ghp_[A-Za-z0-9]{36}' \
-e 'AKIA[0-9A-Z]{16}'
# pełna historia (wolniejsze, ale konieczne)
gitleaks detect --source . --report-format json --report-path gitleaks.json
trufflehog git file://. --only-verified
Do każdego znalezionego sekretu dopisz cztery rzeczy: właściciela, zakres uprawnień, miejsca użycia i datę ważności. Ten dokument przyda się przy każdej kolejnej rotacji.
Rotacja krok po kroku
Kolejność ma znaczenie. Nowy token wystawiony przed unieważnieniem starego nie zamyka okna — tylko dokłada drugi działający klucz.
- Unieważnij stary sekret. Zamknij okno ryzyka zanim zaczniesz cokolwiek konfigurować.
- Wystaw nowy, z węższym zakresem. Jeśli stary miał zapis do wszystkich repozytoriów, nowy powinien mieć odczyt do jednego. Przy okazji ustaw datę wygaśnięcia.
- Wdróż nowy sekret do CI, środowisk staging/prod i konfiguracji lokalnych. Sprawdź, czy pipeline faktycznie go widzi — cicha rotacja bez redeployu zostawia stary klucz w zmiennej środowiskowej kontenera.
- Unieważnij sesje. Wyloguj wszystkie urządzenia i unieważnij refresh tokeny. Token dostępowy, który został skradziony, żyje do wygaśnięcia — nie do wylogowania, więc przy krótkim TTL to kwestia minut, a przy długim trzeba działać natychmiast.
- Posprzątaj kopie. For Ki, gałęzie tematyczne, stare obrazy, kopie zapasowe konfiguracji.
Osobny problem to klucze współdzielone w zespole. Jeden token w CI, z którego korzysta pięć osób, jest wygodny do momentu incydentu — wtedy nie wiadomo, kto i co nim zrobił, a rotacja wymaga skoordynowanego wdrożenia w kilku miejscach naraz. Docelowo każdy developer powinien mieć własne poświadczenie, a pipeline własną tożsamość. W GitHub Actions zamiast długotrwałego sekretu da się użyć OIDC i wymieniać zaufanie na krótkotrwały token w ramach joba — to eliminuje sekret z repozytorium jako klasę problemu.
Sprawdzić zakres istniejącego tokenu GitHub możesz tak:
curl -sSI -H "Authorization: token $TOKEN" https://api.github.com/user \
| grep -i '^x-oauth-scopes:'
Weryfikacja: czy ktoś już użył skradzionych danych
Przegląd logów to etap, który zespoły najczęściej pomijają, bo wygląda na stratę czasu, gdy „nic się nie stało”. Bez niego nie wiesz jednak, czy rotacja była reakcją na incydent, czy na artykuł.
Gdzie patrzeć w pierwszej kolejności:
- audit log repozytorium — nowe klucze deploya, nowe tokeny, nowi collaboratorzy, zmiany w webhookach i ustawieniach zabezpieczeń,
- logi dostawców chmury — tworzenie kluczy dostępowych, nietypowe regiony, wywołania z adresów spoza twojej infrastruktury,
- zużycie API modeli — skok kosztów przy braku wzrostu ruchu w aplikacji to klasyczny sygnał,
- logi CI — commity i buildy z gałęzi, których nikt nie pamięta tworzyć.
Przykładowe zapytanie do audit logu organizacji na GitHubie:
gh api -X GET "orgs/$ORG/audit-log" \
-f phrase="created:>2026-10-01" -f per_page=100 --paginate \
| jq -r '.[] | [.created_at, .action, .actor] | @tsv'
Jak odróżnić fałszywy alarm od realnego użycia? Trzy sygnały, które warto skorelować: czas (zdarzenie po momencie wycieku, nie sprzed tygodnia), źródło (adres IP i user-agent niepasujące do nikogo w zespole, szczególnie ASN z sieci VPS) oraz zakres (skradzionym tokenem ktoś próbuje robić coś, czego zespół nigdy nie robi tym tokenem — np. tworzy repozytoria albo instaluje aplikację OAuth). Pojedynczy nieudany login z obcego IP to najczęściej bot; seria wywołań API z nowego IP z poprawną autoryzacją to zupełnie inna kategoria zdarzenia.
Jeśli token trafił do historii gita, samo przepisanie historii nie wystarczy. git filter-repo porządkuje repozytorium, ale klucz był publiczny przez cały czas, w którym tam leżał — unieważnienie jest jedynym remedium.
Higiena na przyszłość
Incydent jest dobrym pretekstem, żeby zmienić domyślne ustawienia, zamiast tylko gasić pożar:
- minimalne zakresy OAuth i fine-grained PAT z datą wygaśnięcia, zamiast klasycznych tokenów z pełnym
repo, - tokeny krótkotrwałe tam, gdzie platforma to umożliwia; w CI — OIDC zamiast sekretów,
- rozdzielone środowiska — klucz deweloperski nigdy nie ma praw produkcyjnych,
- sekrety poza repozytorium — menedżer sekretów albo sekrety platformy, nigdy plik w repo „bo tak jest szybciej”.
Do tego twarde 2FA na kontach organizacji i przegląd uprawnień przy każdej rotacji. Jeśli nie potrafisz odpowiedzieć na pytanie „kto ma teraz dostęp do produkcji”, to nie wiesz też, co rotować.
Automatyzacja, która robi to za ciebie
Trzy mechanizmy warto wdrożyć raz i zapomnieć o problemie do następnego razu.
Skanowanie w pre-commit (zablokuje sekret, zanim trafi do historii):
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4 # sprawdź aktualny tag
hooks:
- id: gitleaks
Skanowanie w CI na każdym pushu:
name: secret-scan
on: [push, pull_request]
jobs:
gitleaks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Trzeci mechanizm to test rotacji na sucho raz na kwartał: wybierz jeden token, unieważnij go w środowisku staging i sprawdź, jak szybko zespół wykryje awarię i czy procedura wystawienia nowego jest opisana. Jeśli rotacja zajmuje pół dnia i wymaga obecności jednej konkretnej osoby, masz problem większy niż jakikolwiek wyciek.
Lista kontrolna na ten tydzień
- Ustal, kto w zespole używa narzędzia i w jakich wersjach (lokalnie, w CI, w kontenerach).
- Zinwentaryzuj poświadczenia: właściciel, zakres, miejsca użycia, data ważności.
- Unieważnij wszystkie tokeny, które mogły być ujawnione — w kolejności: revoke, potem nowy.
- Wystaw nowe z minimalnym zakresem i datą wygaśnięcia, wdróż je w CI i środowiskach.
- Unieważnij sesje i refresh tokeny na wszystkich urządzeniach.
- Przejrzyj audit log organizacji, logi chmury i zużycie API od dnia wycieku.
- Dodaj skanowanie sekretów do pre-commit i CI; włącz push protection w repozytoriach.
- Rozpisz procedurę rotacji z osobą odpowiedzialną i przetestuj ją na środowisku nieprodukcyjnym.
Wniosek jest mniej sensacyjny, niż sugeruje nagłówek o wycieku: większość szkód w takich sytuacjach wynika nie z samego ujawnienia tokenu, ale z tego, że token miał szersze uprawnienia niż potrzebował i żył dłużej, niż powinien, a zespół nie wiedział, gdzie wszystkich kopii szukać. Incydent to okazja, żeby te trzy rzeczy naprawić — i to niezależnie od tego, co ostatecznie okaże się w raporcie.