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í (14.9.2016, Praha).

Jakub Vrána, Výuka, 17.4.2009, diskuse: 17 (nové: 0)

Diskuse

ikona v6ak:

Taky mě zajímá, k čemu tu to $_REQUEST vymysleli.

Dundee:

Používat pole $_REQUEST považuji téměř za stejnou špatnost jako používat register_globals.

Srigi:

Dalsi priklad nebezpecneho pouzitia $_REQUEST:

http://shiflett.org/articles/cross-site-request-forgeries

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?

ikona Jakub Vrána OpenID:

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"].

dadmtb:

ked už tak ja dávam: <form action="<?php echo $["PHP_SELF"]; ?>">

ikona Jakub Vrána OpenID:

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.

Jan Tvrdík:

Mohl by jsi uvést příklad, jak se do $_SERVER['PHP_SELF'] můžou dostat ty speciální znaky?

ikona Jakub Vrána OpenID:

index.php/"><script>alert('XSS');</script>

Pokud je v Apache povolena direktiva AcceptPathInfo (defaultně je), tak se celý řetězec dostane do PHP_SELF.

Washo:

Neco jako tohle:
http://php.vrana.cz/index.php/%27%3E%3Cb%…%3E%3C/aaa%3E
?

Martin:

Washo: pekne :)
chybka se obcas vloudi

ikona Jakub Vrána OpenID:

Ano, něco takového :-). Děkuji za upozornění.

Jan Tichý:

Když už, tak aspoň <form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>">

ikona 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);
?>

ikona v6ak:

Podívej se v http://cz2.php.net/manual/en/function.htmlspecialchars.php na výchozí hodnotu druhého parametru. Ošetřuje i dvojité uvozovky. Jednoduché uvozovky (apostrofy) ne. Ale to není potřeba, když je parametr vymezený uvozovkama.

ikona 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.

ikona v6ak:

Není nad názornou ukázku ;-)

Vložit příspěvek

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2016 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.