Vývoj softwaru
Útoky založené na časování v PHP: Praktický příklad — proč je funkce `hash_equals()` důležitá
Chcete vidět reálný příklad časového útoku na API tokeny ve WordPressu/Laravelu/PHP? Zde se tím budeme zabývat, včetně tipů a osvědčených postupů pro obranu proti časovým útokům.
Mnoho vývojářů se při ověřování API klíčů, tokenů nebo jiných tajných řetězců spoléhá na jednoduché porovnání pomocí operátorů === nebo ==. Na první pohled to vypadá rozumně — má to však háček: útočníci mohou zneužít časové rozdíly při porovnávání.
Tento příspěvek popisuje realistický příklad toho, jak koncepčně probíhá časová útok.
Skutečný scénář (zjednodušený příklad)
Představte si, že interní mikroslužba přijímá požadavky s dotazovým parametrem „token“ a porovnává jej s tokenem API služby GitHub uloženým v prostředí.
Nezabezpečený kód (zjednodušeně):
$expected = getenv('GITHUB_API_TOKEN');
$provided = $_GET['token'] ?? '';
if ($provided === $expected) {
}
PHP
Proč je to problematické, vysvětlím v následující části.
Jak funguje časová útok?
Pokud implementace porovnání tokenu porovnává znak po znaku a při první chybě se okamžitě přeruší, trvá požadavek se správným prvním znakem v průměru o něco déle.
Správný první znak tedy v průměru vede k minimálně delší době zpracování než nesprávný první znak. Totéž platí pro druhý znak atd.
Útočník toho využije tím, že pro každou pozici otestuje všechny možné znaky, velmi často měří doby odezvy a statisticky je vyhodnocuje. Znak s největší průměrnou/relativně delší dobou je pravděpodobně správný. Takto se token krok za krokem rekonstruuje.
Proč je hash_equals() správnou volbou
PHP poskytuje funkci hash_equals(), která umožňuje porovnání řetězců v konstantním čase (constant-time comparison). Zajišťuje, že doba provedení porovnání nezávisí na společných prefixech – pokud jsou oba řetězce stejně dlouhé.
Bezpečná alternativa (pro Laravel, PHP, WordPress):
$expected = getenv('GITHUB_API_TOKEN');
$provided = $_GET['token'] ?? '';
if (hash_equals($expected, $provided)) {
}
PHP
Poznámka: Funkce hash_equals() je „nezávislá na čase“ pouze v případě, že jsou délky řetězců stejné. Osvědčeným postupem je navrhovat tokeny tak, aby měly konstantní délku (např. HMAC, UUID s pevnou délkou nebo náhodné řetězce Base64 s pevnou délkou).
Věděl jsi to? Pouze UUID-V4 je generováno zcela náhodně, a proto se často používá pro bezpečnostní tokeny. Protože neobsahuje žádné informace související s časem ani zařízením, nabízí vysokou nepředvídatelnost a chrání tak před útoky založenými na vzorcích nebo předpovědích. Právě proto je UUID-V4 preferovanou volbou, pokud jde o bezpečné identifikátory, které je obtížné uhodnout.
Další ochranná opatření — více než jen hash_equals()
- Konstantní délka tokenu
Používejte tokeny s pevnou délkou, např. UUID-V4. Pokud útočník uhádne délku, není to příliš užitečné, pokud jsou samotné znaky bezpečné.
- Omezení frekvence
Omezte počet požadavků na jednu IP adresu nebo jeden účet. Útoky založené na měření času vyžadují mnoho měření; omezení frekvence výrazně zvyšuje úsilí a náklady útočníka.
- Protokolování a monitorování
Rozpoznávejte neobvyklé vzorce (mnoho pokusů o odhad tokenu, systematické změny určitých parametrů) a reagujte automaticky.
- Salt / HMAC / podpisy
Používejte podepsané tokeny (HMAC s tajným klíčem) nebo tokeny OAuth namísto otevřeného porovnávání nezpracovaných statických klíčů.
- TLS/HTTPS
Šifrování chrání komunikační kanály – sice nezabrání časovým útokům na vnitřní součásti serveru, ale je základním předpokladem.
- Rotace tokenů
Krátká životnost tokenů snižuje užitnou hodnotu kompromitovaného tokenu.
Závěr
Útoky typu „timing attack“ nejsou čistě teoretickým problémem — byly zkoumány v praxi a mohou mít, zejména u veřejně přístupných koncových bodů, vážné následky. Dobrá zpráva: Pro vývojáře PHP/Laravel/WordPress je první protiopatření velmi jednoduché:
- Nahraďte přímé porovnávání citlivých řetězců funkcí `
hash_equals()`.
- Zajistěte konstantní délky tokenů, omezení frekvence, protokolování a bezpečnou správu tokenů.