AWSRevokeOlderSessions — jak natychmiast zablokować przejęte credentials IAM
Tymczasowych poświadczeń STS nie możesz unieważnić — ale możesz je zablokować jedną inline policy. Wyjaśniam jak działa aws:TokenIssueTime i jak z niego skorzystać podczas incydentu.
📋 Spis treści
Scenariusz: credentials wyciekły. Co teraz?
Dostajesz alert z GuardDuty albo widzisz dziwne wpisy w CloudTrail. Ktoś używa Twoich tymczasowych poświadczeń IAM (ASIA...) spoza oczekiwanego adresu IP. Wiesz, że nastąpił wyciek.
Odruchowo szukasz przycisku “Revoke” dla tego konkretnego klucza. I tu zaczyna się problem — takiego przycisku nie ma.
Dlaczego tymczasowych credentials nie można po prostu usunąć?
Tymczasowe poświadczenia STS (ASIA*) działają inaczej niż długoterminowe klucze IAM (AKIA*):
- Są generowane “w locie” przez usługę STS (Secure Token Service)
- AWS nie przechowuje ich po wygenerowaniu — nie ma listy aktywnych tokenów
- Wygasają same — ale dopiero po upływie czasu życia sesji (domyślnie 1h, maks. 12h dla
AssumeRole) - Można je unieważnić… ale nie bezpośrednio
Kiedy aplikacja lub osoba wywołuje sts:AssumeRole, AWS generuje trójkę:
AccessKeyId(ASIA...)SecretAccessKeySessionToken
Tego zestawu po wygenerowaniu nie ma jak “cofnąć”. Działa aż do momentu wygaśnięcia, chyba że…
Trik: aws:TokenIssueTime
AWS IAM udostępnia specjalny klucz kontekstu żądania — aws:TokenIssueTime. Jego wartość to dokładna data i czas wystawienia tokenu STS dla bieżącej sesji.
Dzięki temu można napisać politykę, która mówi:
“Zablokuj wszystkie akcje, jeśli token pochodzi sprzed daty X.”
I to właśnie robi AWSRevokeOlderSessions — inline policy dodawana do roli, której credentials wyciekły:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": ["*"],
"Resource": ["*"],
"Condition": {
"DateLessThan": {
"aws:TokenIssueTime": "2026-02-26T14:30:00.000Z"
}
}
}
]
}
Podajesz datę i godzinę dodania tej polityki. Każdy token wystawiony przed tą chwilą dostaje twardy Deny na wszystkie akcje — niezależnie od innych uprawnień roli.
Nowe sesje (AssumeRole po tej dacie) działają normalnie.
Jak to działa mechanicznie?
IAM ewaluuje polisy w określonej kolejności. Explicit Deny zawsze wygrywa — nawet jeśli inna polisa daje Allow. Schemat ewaluacji wygląda tak:
1. Czy istnieje Deny pasujący do żądania? → TAK → ODMOWA
2. Czy istnieje Allow pasujący do żądania? → NIE → ODMOWA (implicit deny)
3. → TAK → DOSTĘP
Nasza inline policy dodaje warunek do każdego żądania: “czy token jest starszy niż data X?”. Dla atakującego, który ma stary token — wynik to zawsze Deny. Koniec historii.
Co ważne — aws:TokenIssueTime jest zawsze dostępny w kontekście żądania przy użyciu tymczasowych credentials. AWS gwarantuje tę wartość dla każdego tokenu STS.
Implementacja: Konsola AWS
- Przejdź do IAM → Roles i wybierz zaatakowaną rolę
- Zakładka Add permissions → Create inline policy
- Przełącz na widok JSON i wklej:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": ["*"],
"Resource": ["*"],
"Condition": {
"DateLessThan": {
"aws:TokenIssueTime": "TUTAJ-DATA-ISO8601"
}
}
}
]
}
- Datę ustaw na aktualny czas UTC (np.
2026-02-26T14:35:00.000Z) - Nazwij politykę
AWSRevokeOlderSessions - Kliknij Create policy
Alternatywnie: w zakładce Revoke sessions kliknij przycisk Revoke active sessions — AWS robi dokładnie to samo automatycznie.
Implementacja: AWS CLI
# 1. Pobierz aktualny czas UTC w formacie ISO 8601
REVOKE_TIME=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
# 2. Utwórz plik z polisą
cat > revoke-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": ["*"],
"Resource": ["*"],
"Condition": {
"DateLessThan": {
"aws:TokenIssueTime": "${REVOKE_TIME}"
}
}
}
]
}
EOF
# 3. Dodaj inline policy do roli
aws iam put-role-policy \
--role-name NazwaZaatakowanejRoli \
--policy-name AWSRevokeOlderSessions \
--policy-document file://revoke-policy.json
Efekt jest natychmiastowy — nie ma opóźnienia propagacji.
Ważne zastrzeżenia
Co ta polityka robi:
- Blokuje wszystkich posiadaczy tokenów starszych niż podana data
- Dotyczy KAŻDEGO, kto zrobił
AssumeRole— nie tylko atakującego - Użytkownicy/serwisy mogą odnowić sesję (
AssumeRoleponownie) i działać bez przeszkód
Czego ta polityka nie robi:
- Nie działa dla service-linked roles — te role mają własny mechanizm i inline policy dla nich nie można dodać w ten sposób
- Nie odwołuje długoterminowych kluczy (
AKIA*) — te wymagają dezaktywacji lub usunięcia w IAM - Nie blokuje dostępu do konsoli przez federację z zewnętrznym dostawcą tożsamości, jeśli token jest niezależny
Pułapka z “Revoke sessions” w konsoli:
Przycisk ustawia datę i godzinę kliknięcia. Jeśli atakujący nadal ma dostęp i zrobi AssumeRole po Twoim kliknięciu — jego nowy token nie będzie zablokowany. Dlatego równolegle napraw Trust Policy roli, żeby uniemożliwić nowe AssumeRole ze strony nieuprawnionego podmiotu.
Pełna reakcja na incydent — sekwencja działań
1. IZOLACJA
└─ Dodaj AWSRevokeOlderSessions do zaatakowanej roli
2. ZABLOKOWANIE ŹRÓDŁA
└─ Napraw Trust Policy — usuń/ogranicz kto może AssumeRole
└─ Jeśli EC2/Lambda: rotate credentials, zabroń IMDS lub ogranicz do IMDSv2
3. ANALIZA
└─ CloudTrail: sprawdź co zrobił atakujący przez ostatnie godziny
└─ GuardDuty findings: oceń zakres naruszenia
4. NAPRAWA
└─ Wyeliminuj przyczynę (SSRF, debug mode, hardcoded credentials w kodzie)
└─ Sprawdź czy atakujący nie stworzył backdoora (nowe role, użytkownicy IAM)
5. PRZYWRÓCENIE
└─ Usuń AWSRevokeOlderSessions gdy zagrożenie minęło
└─ Zrotuj credentials używane przez legitymowanych aktorów
Dlaczego ta polityka zasługuje na uwagę?
To eleganckie rozwiązanie problemu, który wydaje się nierozwiązywalny — jak cofnąć coś, czego nie można cofnąć.
AWS nie przechowuje listy aktywnych tokenów. Ale każdy token nosi w sobie datę urodzin w postaci aws:TokenIssueTime. I właśnie ta cecha — niezmienność czasu wystawienia — staje się dźwignią, którą można wymusić blokadę.
Jedna inline policy, jedna data, natychmiastowy efekt. Żadnych rebootów, żadnego downtime dla nowych sesji.
Odnośniki: