Na navigaci | Klávesové zkratky

Escapování – definitivní příručka

Největší programátorský evergreen jsou zmatky a nejasnosti kolem escapování. Neznalost způsobuje, že nejtriviálnější metody narušení webových stránek, jako třeba Cross Site Scripting (XSS) nebo SQL injection, patří bohužel mezi nejrozšířenější.

Escapování je náhrada znaků majících v daném kontextu speciální význam za jiné odpovídající sekvence.

Příklad: do řetězce ohraničeného uvozovkami chceme zapsat uvozovky. Jelikož uvozovky mají v kontextu řetězce speciální význam a jejich prosté zapsání by bylo chápáno jako ukončení řetězce, je potřeba je zapsat jinou odpovídající sekvencí. Jakou přesně určují pravidla kontextu.

Předpoklady

Každá escapovací funkce předpokládá, že vstupem je vždy surový řetězec v určitém kódování (znakové sadě).

Ukládat třeba do databáze řetězce již předem escapované pro HTML výstup a podobně je zcela kontraproduktivní.

S jakými kontexty se setkáváme?

Jak bylo řečeno, escapování převádí znaky mající v určitém kontextu speciální význam. Pro každý kontext se používají jiné escapovací funkce. Tato tabulka je pouze orientační, je nutné si přečíst poznámky níže.

kontext escapovací funkce reverzní funkce
HTML htmlspecialchars html_entity_decode
XML htmlspecialchars
regulární výraz preg_quote
PHP řetězce var_export
MySQL databáze mysql_real_escape_string
MySQL improved mysqli_real_escape_string
SQLite databáze sqlite_escape_string
PostgreSQL databáze pg_escape_string
PostgreSQL, typ bytea pg_escape_bytea pg_unescape_bytea
JavaScript, JSON json_encode json_decode
CSS addcslashes
URL rawurlencode urldecode

Vysvětlení k následujícím poznámkám:

  • řada kontextů má své podkontexty a v nich se escapování liší. Nebude-li řečeno jinak, je uvedená escapovací funkce použitelná plošně bez dalšího rozlišování podkontextů.
  • pod pojmem obvyklá znaková sada se rozumí znaková sada s 1bajtovým nebo UTF-8 kódováním

HTML

V HTML kontextech mají souhrnně speciální význam znaky < & " ' a odpovídající sekvence jsou &lt; &amp; &quot; &#039;. Výjimkou je ovšem HTML komentář, kde má speciální význam jen dvojice --.

K escapování se používá:

$s = htmlspecialchars($s, ENT_QUOTES);

Funguje s libovolnou obvyklou znakovou sadou. Ale nezohledňuje podkontext HTML komentářů (tj. neumí nahradit dvojici -- za něco jiného).

Reverzní funkce:

$s = html_entity_decode($s, ENT_QUOTES, 'UTF-8');

XML / XHTML

XML 1.0 se od HTML liší v tom, že zakazuje použití kontrolních znaků C0 (a to včetně zápisu v podobě entity) s výjimkou tabulátoru, odřádkování a mezery. XML 1.1 tyto zakázané znaky s výjimkou NUL v podobě entit naopak povoluje a dále přikazuje kontrolní znaky C1 s výjimkou NEL taktéž zapisovat jako entity. Dále v XML má speciální význam sekvence ]]>, proto je třeba jeden z těchto znaků také escapovat.

Pro XML 1.0 a libovolnou obvyklou znakovou sadu tak lze použít:

$s = preg_replace('#[\x00-\x08\x0B\x0C\x0E-\x1F]+#', '', $s);
$s = htmlspecialchars($s, ENT_QUOTES);

Regulární výraz

Perlových regulárních výrazech mají souhrnně speciální význam znaky . \ + * ? [ ^ ] $ ( ) { } = ! < > | : - a tzv. delimiter, což je znak ohraničující regulární výraz (např. pro výraz '#[a-z]+#i' je to #). Escapuje se znakem \.

$s = preg_quote($s, $delimiter);

V řetězci, kterým se hledaný výraz nahrazuje (tedy například 2. parametr funkce preg_replace), má speciální význam zpětné lomítko a dolar:

$s = addcslashes($replacement, '$\\');

Kódování musí být buď 1bajtové nebo UTF-8, podle modifikátoru v regulárním výrazu. Viz také Escapování v regulárních výrazech.

PHP řetězce

PHP rozlišuje tyto typy řetězců:

  • v jednoduchých uvozovkách, kde speciální význam mohou mít znaky \ '
  • ve dvojitých uvozovkách, kde speciální význam mohou mít znaky \ " $
  • NOWDOC, kde speciální význam nemá žádný znak
  • HEREDOC, kde speciální význam mohou mít znaky \ $

Escapuje se znakem \. To obvykle provádí programátor při psaní kódu, pro generátory PHP kódu lze využít funkci var_export.

Poznámka: protože zmíněné regulární výrazy se obvykle zapisují uvnitř PHP řetězce, je potřeba zkombinovat obě escapování. Např. znak \ se pro regulární výraz zapíše jako \\ a v řetězci s uvozovkami je třeba psát \\\\.

SQL a databáze

Každá databáze má svou vlastní escapovací funkci, viz tabulka výše. Téměř vždy je ale dostupná jen funkce pro escapování řetězců a tu nelze použít k ničemu jinému, zejména chybí funkce escapující zástupné znaky používané v konstrukcích LIKE (v MySQL jde o znaky % _) nebo identifikátory, jako jsou názvy tabulek či sloupců. Databáze nevyžadují odstraňování escapování na výstupu! (S výjimkou např. typu bytea.)

Znakové sady s neobvyklým vícebajtovým kódováním je nutné v MySQL nastavit funkcí mysql_set_charset resp. mysqli_set_charset.

Doporučuji používat databázový layer (např. dibi, Nette Database, PDO) nebo parametrické dotazy, které escapování obstarají za vás.

JavaScript, JSON

Jakožto programovací jazyk má řadu velmi odlišných podkontextů. K escapování řetězců lze využít vedlejší efekt funkce

$s = json_encode((string) $s);

která navíc obalí řetězec do uvozovek. Striktně vyžaduje UTF-8.

JavaScript zapsaný uvnitř HTML atributů (např. onclick) je nutné ještě escapovat podle HTML pravidel, neplatí to však pro JavaScript uvnitř značek <script>, kde musí být ošetřen pouze případný výskyt koncové značky </script> uvnitř řetězce. To ovšem funkce json_encode zajistí, jelikož JSON escapuje lomítko /. Neošetří však konec HTML komentáře --> (což v HTML nevadí) nebo XML bloku CDATA ]]>, do kterého se skript obaluje. Pro XML/XHTML je řešením

$s = json_encode((string) $s);
$s = str_replace(']]>', ']]\x3E', $s);

Jelikož JSON využívá podmnožinu syntaxe JavaScriptu, je reverzní funkce json_decode plně použitelná jen pro JSON, pro JavaScript omezeně.

CSS

V CSS kontextech je rozsah platných znaků přesně vymezen, pro escapování identifikátorů lze použít například tuto funkci:

$s = addcslashes($s, "\x00..\x2C./:;<=>?@[\\]^`{|}~");

Pro CSS uvnitř HTML kódu platí totéž, co bylo řečeno o JavaScriptu a jeho escapování uvnitř HTML atributů a značek (zde se jedná o atributy style a značky <style>).

URL

V kontextu URL se escapuje vše kromě písmen anglické abecedy, číslic a znaků - _ . nahrazením za % + hexadecimálně vyjádřený bajt.

$s = rawurlencode($s);

Podle RFC 2718 (z roku 1999) nebo RFC 3986 (z roku 2005) je preferován zápis znaků v kódování UTF-8.

Reverzní funkcí je v tomto případě urldecode, která rozeznává i znak + s významem mezery.


Pokud se vám zdá celá problematika příliš složitá, nezoufejte. Brzy přijdete na to, že jde vlastně o jednoduché tranformace a celý trik spočívá v uvědomění, v jakém kontextu se nacházím a jakou musím pro něj zvolit funkci. Nebo ještě lépe, zkuste použít inteligentní šablonovací systém, který dokáže kontexty rozeznat sám a použít správné escapování: Latte


phpFashion © 2004, 2024 David Grudl | o blogu

Ukázky zdrojových kódů smíte používat s uvedením autora a URL tohoto webu bez dalších omezení.