Problémy pole $_REQUEST
Školení, která pořádám
Jaký bezpečnostní problém má následující kód?
<?php
if (isset($_REQUEST["ulozit"])) {
mysql_query("
UPDATE uzivatele
SET vek = " . intval($_REQUEST["vek"]) . "
WHERE id = " . intval($_SESSION["uzivatel"])
);
}
?>
<form action="" method="post">
<p>
<input type="hidden" name="ulozit" value="1" />
Věk: <input name="vek" />
<input type="submit" value="Uložit" />
</p>
</form>
Problém je ten, že pokud na stránku přijde uživatel s nastavenou cookie ulozit
, tak se mu věk přenastaví, aniž by odeslal formulář. Cookie může omylem nastavit jiná část aplikace, která tento formulář nebere v potaz, ale může jít i o cílený útok – pokud aplikace běží na free-hostingu (kde se přidělují domény třetího řádu), může cookie nastavit i úplně jiná stránka. To samé platí s některými prohlížeči i u domén jako .co.uk
.
V PHP 5.3 navíc vznikla direktiva request_order, pomocí které lze nastavit, co bude obsahem pole $_REQUEST. Na obsah tohoto pole se tak ani nedá spolehnout.
Když si uvědomíme, jaké metody se mají používat u webových formulářů, tak pole $_REQUEST k ničemu nepotřebujeme. Pokud odeslání formuláře vede pouze ke zobrazení stránky, použijeme metodu GET (to jsou typicky vyhledávací formuláře). Pokud odeslání formuláře způsobí změnu stavu aplikace (provedení nějaké akce), použijeme metodu POST (např. uložení záznamu, odeslání e-mailu, ale také přihlášení nebo odhlášení). No a cookies s tím nijak nesouvisí – ty slouží k ukládání informací na stranu uživatele (např. jméno vyplněné do diskusního formuláře nebo session identifikátor).
Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací.
Diskuse
Taky mě zajímá, k čemu tu to $_REQUEST vymysleli.
Používat pole $_REQUEST považuji téměř za stejnou špatnost jako používat register_globals.
hokuka:
doplním jeden důsledek, který mi z výše uvedeného vyplývá: když má jeden skript zpracovat třeba formulář na editaci metodou POST a zobrazení detailu přes GET pomocí parametru ID. Pak je nutné ve formuláři používat action=URL?id=5 místo input{id=5,hidden=true}. Chápu to správně že?
Nejlepší je používat <form action="">, tedy odesílat formulář na stejnou adresu, na které se zobrazuje. Detekci odeslání je pak možno dělat ověřením neprázdnosti pole $_POST nebo porovnáním hodnoty $_SERVER["REQUEST_METHOD"].
ked už tak ja dávam: <form action="<?php echo $["PHP_SELF"]; ?>">
Nejen, že to je špatně syntakticky, je to špatně i bezpečnostně. PHP_SELF může obsahovat speciální znaky jako ">, takže uvedený zápis je náchylný ke XSS. Prázdné URL je definováno v HTTP jako odkaz na aktuální dokument a všechny prohlížeče to respektují, takže není důvod to nepoužívat.
Mohl by jsi uvést příklad, jak se do $_SERVER['PHP_SELF'] můžou dostat ty speciální znaky?
index.php/"><script>alert('XSS');</script>
Pokud je v Apache povolena direktiva AcceptPathInfo (defaultně je), tak se celý řetězec dostane do PHP_SELF.
Martin:
Washo: pekne :)
chybka se obcas vloudi
Jan Tichý:
Když už, tak aspoň <form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">
Patrik:
Keďže výstup posielame ako obsah atribútu, je treba ošetriť aj uvodzovky, ktoré by ukončili aktuálny atribút. Prešlo by napríklad:
index.php/%22%20onclick%3D%22alert%28document.cookie%29%3B
Takže vo vnútri atribútov radšej takto:
<?php
echo htmlspecialchars($_SERVER["PHP_SELF"], ENT_QUOTES);
?>
Patrik:
To je pravda. Osobne som si ale zvykol v HTML escapovat vždy s ENT_QUOTES bez ohľadu na to, či používam uvodzovky alebo apostrof.
v6ak:
Není nad názornou ukázku ;-)
Diskuse je zrušena z důvodu spamu.