5/3.3 BEZPEČNOSTNÍ ASPEKTY JAZYKA PHP



Podobné dokumenty
Zranitelnosti webových aplikací. Vlastimil Pečínka, Seznam.cz Roman Kümmel, Soom.cz

Úvod do PHP s přihlédnutím k MySQL

2. blok část B Základní syntaxe příkazů SELECT, INSERT, UPDATE, DELETE

Testování webových aplikací Seznam.cz

Kolekce ArrayList. Deklarace proměnných. Import. Vytvoření prázdné kolekce. napsal Pajclín

Redakční systém. SimpleAdmin Beta. Jan Shimi Šimonek

10. Editor databází dotazy a relace

Zadání úlohy do projektu z předmětu IPP 2013/2014

Intervalové stromy. Představme si, že máme posloupnost celých čísel p 0, p 1,... p N 1, se kterou budeme. 1. Změna jednoho čísla v posloupnosti.

ZPRACOVÁNÍ NEURČITÝCH ÚDAJŮ V DATABÁZÍCH

Využití OOP v praxi -- Knihovna PHP -- Interval.cz

1. Dříve než začneme Trocha historie nikoho nezabije Co budete potřebovat Microsoft versus zbytek světa...

Hitparáda webhackingu nestárnoucí hity. Roman Kümmel

Šablonovací systém htmltmpl vypracoval: Michal Vajbar, Šablonovací systém htmltmpl

DATA ARTICLE. AiP Beroun s.r.o.

Individuální projekt z předmětu webových stránek Anketa Jan Livora

KAPITOLA 1 SOCIÁLNÍ SÍTĚ A PHP...17

Programování. Debugging a testování. Martin Urza

Maturitní témata z předmětu PROGRAMOVÉ VYBAVENÍ pro šk. rok 2012/2013

Pokročilé schopnosti OOP

Jaku b Su ch ý 1

Západočeská univerzita v Plzni Katedra informatiky a výpočetní techniky. 9. června krovacek@students.zcu.cz

Lekce 25 IMPLEMENTACE OPERAČNÍHO SYSTÉMU LINUX DO VÝUKY INFORMAČNÍCH TECHNOLOGIÍ

KAPITOLA 3. Architektura aplikací na frameworku Rails. V této kapitole: modely, pohledy, řadiče.

Uložené procedury Úvod ulehčit správu zabezpečení rychleji

Instalujeme a zakládáme databázi Oracle Database 11g

Šifrování Autentizace Bezpečnostní slabiny. Bezpečnost. Lenka Kosková Třísková, NTI TUL. 22. března 2013

DJ2 rekurze v SQL. slajdy k přednášce NDBI001. Jaroslav Pokorný

17. července :51 z moravec@yahoo.com

2. přednáška. Databázový přístup k datům (SŘBD) Možnost počítání v dekadické aritmetice - potřeba přesných výpočtů, např.

Použití databází na Webu

INOVACE PŘEDMĚTŮ ICT. MODUL 11: PROGRAMOVÁNÍ WEBOVÝCH APLIKLACÍ Metodika

Databázové systémy II. KIV/DB2 LS 2007/2008. Zadání semestrální práce

Návrh a tvorba WWW stránek 1/14. PHP a databáze

VYUŽITÍ REGIONÁLNÍCH FUNKCÍ A WWW ROZHRANÍ V INTEGROVANÉM KNIHOVNÍM SYSTÉMU KPWINSQL

Uživatelský manuál Správce úloh. Verze dokumentu 1.0

Semestrální práce z DAS2 a WWW


Obsah přednášky. Představení webu ASP.NET frameworky Relační databáze Objektově-relační mapování Entity framework

Systémová volání Mgr. Josef Horálek

PREPROCESOR POKRAČOVÁNÍ

IB108 Sada 1, Příklad 1 Vypracovali: Tomáš Krajča (255676), Martin Milata (256615)

20. Projekt Domácí mediotéka

1 of :27

MANUÁL uživatelská příručka Speciální IT systémové řešení

Zobrazování dat pomocí tabulek

Verzování a publikace dat na webu za pomoci PostgreSQL

Automatický přenos dat z terminálů BM-Finger

5 Rekurze a zásobník. Rekurzivní volání metody

NÁVRH A REALIZACE WWW PREZENTACE ČKR

Univerzita Pardubice Fakulta elektrotechniky a informatiky. WWW aplikace s využitím relační databáze pro správu sportovního centra Michal Nosil

Uživatelský manuál Radekce-Online.cz

Elektronická pošta... 3 Historie... 3 Technické principy... 3 Komunikační protokoly... 3 MBOX... 4 Maildir... 4 Jak funguje POP3...

Minebot manuál (v 1.2)

Jazyk C# (seminář 5)

Informatika pro moderní fyziky (6) Chytré šablony a interaktivní dokumenty

Malý průvodce Internetem

Aplikační vrstva. Úvod do Php. Ing. Martin Dostal

Třídy a objekty. Třídy a objekty. Vytvoření instance třídy. Přístup k atributům a metodám objektu. $z = new Zlomek(3, 5);

4. POČÍTAČOVÉ CVIČENÍ

Vkládání, aktualizace, mazání

1 Webový server, instalace PHP a MySQL 13

Obsah. Část I Začínáme s jazykem AppleScript

Redakční a informační systém pro správu měst a obcí

Tabulka fotbalové ligy

MapleCloud a jeho použ ití. Vladimír Žák

Databázové systémy trocha teorie

INSTALACE DATABÁZE ORACLE A SYSTÉMU ABRA NA OS WINDOWS

Mýty o účasti veřejnosti

Univerzita Pardubice. Centrální správa dokumentů

BankKlient. FAQs. verze 9.50

UŽIVATELSKÁ DOKUMENTACE PRO DODAVATELE. Stav ke dni v. 2.0

ESTATIX INFORMAČNÍ SYSTÉM REALITNÍCH KANCELÁŘÍ UŽIVATELSKÁ PŘÍRUČKA UŽIVATELSKÁ PŘÍRUČKA STRANA 1 / 23

PHP tutoriál (základy PHP snadno a rychle)

Ing. Přemysl Brada, MSc., Ph.D. Ing. Martin Dostal. Katedra informatiky a výpočetní techniky, FAV, ZČU v Plzni

SPJA, cvičení 1. ipython, python, skripty. základy syntaxe: základní datové typy, řetězce. podmínky: if-elif-else, vyhodnocení logických výrazů

PL/SQL. Jazyk SQL je jazykem deklarativním, který neobsahuje procedurální příkazy jako jsou cykly, podmínky, procedury, funkce, atd.

Databázové a informační systémy

PHP PHP je skriptovací programovací jazyk dynamických internetových stránek PHP je nezávislý na platformě

Marek Laurenčík. Excel. práce s databázemi a kontingenčními tabulkami

Jazyk S Q L základy, příkazy pro práci s daty

5. POČÍTAČOVÉ CVIČENÍ

Informační systémy 2008/2009. Radim Farana. Obsah. Dotazy přes více tabulek

ISO 400 1/250 sekundy f/2,8 ohnisko 70 mm. 82 CANON EOS 550D: Od momentek k nádherným snímkům

Operační systémy. Přednáška 8: Správa paměti II

Informační systémy 2008/2009. Radim Farana. Obsah. Aktivní serverové stránky ASP. Active Server Pages. Activex Data Objects. LDAP database.

Popis licencování, nastavení a ovládání replikací - přenosů dat

Mobilní aplikace Novell Filr Stručný úvod

12. Základy HTML a formuláře v HTML

Střední škola informačních technologií a sociální péče, Brno, Purkyňova 97. Vybrané části Excelu. Ing. Petr Adamec

A7B36SI2 Tematický okruh SI08 Revidoval: Martin Kvetko

Odstavení automatického zpracování hypertextových odkazů

Čtvrtá část odpovědi aneb jak je to vlastně s interakcí <<include>>

Vladimír

Seminární práce. Téma. Jméno, Příjmení, rok,

Filr 2.0 Uživatelská příručka k aplikaci Filr Web. Únor 2016

Návrh a tvorba WWW stránek 1/38 PHP

Instalace. Produkt je odzkoušen pro MS SQL server 2008 a Windows XP a Windows 7. Pro jiné verze SQL server a Windows nebyl testován.

Instalace a konfigurace OpenAdmin tool na M$ a Linuxu

Co je nového v SolidWorks Enterprise PDM 2009

Transkript:

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3, str. 1 5/3.3 BEZPEČNOSTNÍ ASPEKTY JAZYKA PHP Register_globals Hlavním bezpečnostním rizikem především začínajícího programátora je opomenutí resetování proměnné, když pracuje v prostředí s direktivou register_globals nastavenou na On. To způsobuje, že proměnné z GET, POST a cookies jsou změněny na standardní proměnné skriptu a tím pádem k nerozeznání od jakýchkoliv jiných. Jeden příklad za všechny: {if ($heslo== 123 ) $autentizovan=1; } if ($autentizovan) {... privátní část. } Pokud bude v tomto případě skript volán s parametrem?autentizovan=1, provede se privátní část i v případě, že heslo bude zadáno špatně. Pro zamezení této srpen 2006

část 5, díl 3, kap. 3, str. 2 BEZPEČNÁ POČÍTAČOVÁ SÍŤ chyby je nutné před touto částí vyresetovat proměnnou $autentizovan. Obtížně odhalitelnou chybou je přidávání hodnot do pole. Pokud se pole inicializuje pouhým přidáním první hodnoty, např. $pole[]= 123, mohou být další hodnoty přidány pomocí?pole[]=456&pole[]=789. Hranaté versus složené závorky CPG pořadí Pokud je register_globals nastavena na On, je třeba si dát pozor na další možnost průniku, která pramenní ze zpětné kompatibility pro přístup k jednotlivým znakům řetězce pomocí hranatých závorek, přičemž se mají používat složené. Opět, pokud definujeme pole jeho hodnotami, například $uzivatel[ jmeno ]= pepa ; $uzivatel[ heslo ]= 123 a následně budeme tyto hodnoty testovat na odpovídající z $_GET (GET data), můžeme být nemile překvapeni. Pokud bude pole $uzivatel inicializováno parametrem?uzivatel=nejaky, bude se s $uzivatel při přiřazování jednat tak, jako kdyby šlo o řetězec, nikoliv o pole. Jiné než první znaky hodnot (epa, eslo) se nepoužijí, indexy jméno, heslo budou neplatné a první znak $uzivatel se nastaví na p, následně na h. Následně se ta samá hodnota ( h ) bude testovat na jméno a heslo z GET/POST dat. To dopadne pozitivně, pokud budou jméno a heslo jedno písmeno, stejné jako první písmeno hesla. Na jeho uhádnutí se redukuje bezpečnost aplikace. V konfiguraci PHP můžete specifikovat priority jednotlivých zdrojů (cookies, POST, GET). Programátor musí mít na paměti, že zdroj s vyšší prioritou přepisuje ten s nižší, pokud by se proměnné jmenovaly stejně. Odeslat GET data je pro útočníka značně jednodušší než POST. srpen 2006

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3, str. 3 Vedle $_POST, $_GET a $_COOKIE nevěřte ani těm datům, které poskytuje webserver. Některá mohou pocházet od uživatele, např. z Host hlavičky, častěji QUERY_STRING, REQUEST_URI a PATH_INFO. Funkce serialize() umožňuje převést jakoukoliv proměnnou na řetězec, nehledě na její strukturu. Častá praktika je, že se tento výsledek ukládá do souboru, pro příští načtení. Ale pozor, pokud použijete nějakou obecnou příponu, řekněme.ser, může obsah kdokoliv získat (obdobně jako třeba.inc apod.). U této techniky se na tento problém často zapomíná. Pokud bude mít soubor příponu.php, problému se ale nezbavíte, citlivá data z proměnné budou stále k dispozici. Jedinou možností je ještě navíc přidat na začátek souboru instrukci <? die();?> a teprve až za ni zapsat vlastní obsah. Při dotazu na.php soubor se provede a žádný obsah se nevrátí. To je pochopitelně třeba ošetřit i při následném čtení, aby unserialize() nevrátila chybu. U všech nedůvěryhodných dat je vhodné zajistit, že do dalšího zpracování půjdou s takovým typem, jaký odpovídá situaci. Především není vhodné, aby se číselné proměnné od uživatele se po skriptu potulovaly jako řetězce, nakonec také kvůli rychlosti. Použijte například přetypování: $id = (int) $_GET[ id ];. Dále se naučte používat rodinu funkcí ctype, která dokáže efektivně kontrolovat proměnné, zda neobsahují zakázané znaky. Pokud otvíráte soubory, jejichž název čerpáte z proměnných, je dobré se seznámit s nějakými nástroji. Předně každý soubor před otevřením testujte pomocí funkce file_exists(). Dále existuje funkce basename(), která expanduje název souboru na celou lokální cestu. Nakonec je asi nejlepší používat nějaký whitelist povolených souborů. Důvěra v $_SERVER Výsledek serialize() v souboru Používejte typy akontrolujte obsah Nástroje na cesty srpen 2006

část 5, díl 3, kap. 3, str. 4 BEZPEČNÁ POČÍTAČOVÁ SÍŤ Podvržený odkaz bez XSS Úvod do kódování Je třeba si uvědomit, že značnou škodu může útočník způsobit podobnou technikou jako Cross-Site Scripting (XSS), o kterém jsme psali v obecné části, nicméně bez získání přihlašovacích údajů. O XSS se může čtenář dočíst v každé publikaci, nicméně diskuse o tzv. Cross-Site Request Forgery tak častá není. Princip je přitom podobný: oběť přinutíme vykonat nějaký (nejlépe javascriptový) kód (ale popřípadě i prosté zobrazení obrázku), přičemž výsledek bude mít stejný efekt, jako kdyby na dané adresy (odkazy) uživatel kliknul. Tak se dá způsobit značná škoda v nejednom administračním rozhranní (smazání článků, obsahu). Pokud navíc aplikace není zabezpečena proti útokům od ostatních uživatelů, kteří mají přihlašovací účet (programátor ošetřil jen útoky zvenku, což je relativně časté ušetření si práce), dá se takto napáchat mnoho špatného bez vlastního zjištění jakýchkoliv přihlašovacích údajů. Pokud se nám povede podstrčit uživateli javascript, může uživatel volat jednu adresu, resp. příkaz za druhou/druhým, například pomocí neviditelného obrázku. Jak jsme již říkali u XSS, proti tomuto postupu nepomůže zafixování session na jednu IP adresu. Nástup vícebytového kódování UTF se dotkl celé řady oblastí. Uživatelé většinou mívají tu čest číst špatně zobrazené národní znaky kvůli zpětně či dopředně nekompatibilnímu softwaru, programátoři řeší převody mezi dalšími kódováními. Z hlediska bezpečnosti se změnilo několik faktorů, o kterých je nutné se zmínit trochu šířeji, protože daná problematika značně přesahuje rámec webových aplikací. Nejprve několik poznámek o tom, co to vlastně UTF je. srpen 2006

BEZPEČNÁ POČÍTAČOVÁ SÍŤ Pokud si vzpomenete na výklad o buffer overflows a základních technikách hackování, které jsou v principu jen správným zacházením s textovými řetězci plus uměním dobře počítat znaky a adresy, asi tušíte, že něco takového je tak zásadní zásah do archičást 5, díl 3, kap. 3, str. 5 V minulosti byla základní jednotka pro jeden znak navržena jako osmice bitů, tedy byte. Vytvořila se jakási tabulka, která jednotlivým znakům připisovala kódy od 0 do 255. V rámci každého národního prostředí, kde nebyla angličtina oficiálním jazykem, se muselo navrhnout nějaké kódování, tedy vlastně podmnožina znaků z daných 255, kterých se vzdáme a které nahradíme při vykreslování lokálními symboly, jako ě ščžý. Pro mnoho jazyků vzniklo několik kódování (pro češtinu například Windows-1250, Latin2, Kamenických), takže se záhy řešily nejen převody mezi různými kódováními ve smyslu mezinárodním, ale i lokálním. To vše pochopitelně zvyšuje náklady na IT a z toho vyústila nutnost nějakého lepšího řešení. Prvotním nápadem je rozšířit byte třeba na 9 bitů. To v praxi není možné, číslo 8 je zabudováno hluboko v každém dílu elektroniky. Je možné přijít s násobkem, tedy vlastně zdvojit znaky - každý by byl reprezentován dvěma byty, 16 bity. Nebo třeba i více, třemi či čtyřmi. Problémem je, že jelikož jednotlivé jazyky pro 99 % komunikace používají pouhých pár znaků, často by stačilo méně než 256 a docházelo by k obrovskému plýtvání (v principu by veškeré texty zdvojnásobily místo, které na disku zabírají). Jediným možným řešením je tedy kódovat jednotlivé znaky proměnným počtem bytů, podle toho, o jaký jazyk jde. srpen 2006

část 5, díl 3, kap. 3, str. 6 BEZPEČNÁ POČÍTAČOVÁ SÍŤ UTF hacking srpen 2006 tektury, že celý obor informační bezpečnosti musí tuto skutečnost reflektovat. Nyní se nám tedy návrh jednotného kódování rozpadá na dvě části. Za prvé vytvoření pořadí jednotlivých znaků, nejlépe všech znaků všech abeced na světě (každému znaku přiřadíme číslo), za druhé je zde vlastní kódování, tedy způsob jak zakódovat tato čísla do bytů, aby nedocházelo k velkému nárůstu objemu. Detaily nejsou pro bezpečnost podstatné, jen popišme logiku: Běžné znaky západní abecedy by měly zabírat jeden byte, čímž by se docílilo stoprocentní zpětné kompatibility. Dále by s narůstající vzácností dalších znaků měly tyto znaky postupně zabírat dva a více bytů. Již nyní se objevují první exotické vlastnosti výsledku: například dvě slova mohou být stejná, ale v různých kódováních budou zabírat různý počet bytů. Vrcholem nebezpečnosti by bylo, kdyby toto bylo možné i v rámci UTF, což naštěstí algoritmus pro zápis znaků vylučuje. V UTF často bývají taková data, které jsou i jinak moderní, například zapsaná v nějakém dialektu XML. Krátce po rozšíření UTF se mezi hackery začala diskutovat otázka, zda existuje způsob, jak napsat instrukce strojového kódu tak, aby byl výsledkem validní UTF řetězec. Snaha je pochopitelná, uvědomíme-li si obrovský vzestup XML, který takřka do každé aplikace implementuje nějaký XML parser. Ten má v sobě validátor kódování (XML je na chyby v kódování extrémně náchylné), a pokud hackeři chtějí zneužívat chyby v softwaru, který pracuje s XML, musí nějakým způsobem propašovat spustitelný kód, který bude vyhovovat UTF kódování. Důležitost odpovědi na položenou otázku je tak jednoznačná.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3, str. 7 V jednom z minulých čísel legendárního hackerského časopisu Phrack (www.phrack.org) vyšel článek, který se snaží algoritmus převodu strojového kódu do UTF řetězce zkonstruovat. Výsledek sice není absolutní, ale podle všeho se zdá, že odpověď bude kladná, minimálně pro účely hackování. Nyní se ale vraťme zpět k webovým aplikacím a ukažme si, jak zde problémy s kódováním komplikují bezpečnost. Podívejme se na jednoduchý webový skript, pracující s UTF-7 (rozdíl mezi různými variantami UTF pro účely této publikace není podstaný): header( Content-Type: text/html; charset=utf-7 ); $vystup = mb_convert_encoding(`<script> alert( Prikaz );</script>`, UTF-7 ); echo htmlentities($vystup); UTF a escapování Skript dělá následující. Na první řádce pošle webovému prohlížeči hlavičku, že má očekávat obsah kódovaný v UTF-7. Následně je do proměnné $vystup zapsán jednoduchý javascript. Řekněme, že programátorův editor pracuje třeba v kódování ISO, takže obsah převedeme pomocí mb_convert_encoding do UTF-7 (mb je předpona značící rodinu funkcí pro multibyte kódování). Následně obsah proměnné vypíšeme, a abychom zabránili např. ukradení session, zajistíme bezpečnost pomocí funkce htmlentities, která převede všechny HTML tagy na text. Jaké je ovšem programátorovo překvapení, když je tato jinak používaná bezpečnostní funkce naprosto neúčinná a skript se provede. Proč je tomu tak? Na vině je fakt, že htmlentities, stejně jako jiné funkce podobného druhu, musí řetězci rozumět, ve smyslu srpen 2006

část 5, díl 3, kap. 3, str. 8 BEZPEČNÁ POČÍTAČOVÁ SÍŤ musí rozumět jeho kódování, aby mohly být bezpečnostní změny v něm provedeny. Ale UTF-7 zde není podporováno a ochrana je neúčinná. V praxi nemusíme chodit pro příklad k žádným malým webům v koutech internetu, na variantu této chyby doplatil minulý rok Google ve své aplikaci GMail. Trik útočníků spočíval ve faktu, že chyběla definice kódování v hlavičce. Internet Explorer, pokud k tomuto dojde, se ho pokusí automaticky zjistit z prvních 4096 znaků. To skýtá následující možnost: nastavit pomocí XSS kódování na UTF-7 třeba jedním jediným znakem a následně poslat skriptu jakýkoliv zákeřný javascript, opět v UTF-7. Jenže onen skript pracuje v jiném kódování, a i když správně provede všechny bezpečnostní filtry nad vstupem od uživatele, řetězec v UTF-7 projde díky výše zmíněné nepodpoře v mnoha funkcích. Takovýchto skrytých problémů s nefunkčním escapováním (filtrováním) existuje více. Konkrétně PHP (speciálně MySQL) se týká následující z nich. Při vytváření SQL dotazu se dohromady skládá několik řetězců, typicky se do kostry dotazu doplňují data od uživatele. Aby data nemohla měnit kostru, je třeba je opět nějak ohraničit - dělá se to apostrofem - a escapovat, tj. ve vlastním obsahu nějak pozměnit apostrofy, aby nebyly interpretovány jako ohraničení, ale jako obsah. Dělá se to tak, že se před ně vloží zpětné lomítko. Jenže málokdo ví, že GBK (zjednodušená čínština) obsahuje znak, který pro tento účel často používané funkce addslashes() přímo na apostrof konverguje. Jak je to možné? Problém je opět v tom, že addslashes() GBK neumí. Slepě nahradí apostrof za lomítko a apostrof. Jenže lomítko je následně v mul- srpen 2006

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3, str. 9 tibyte kódování GBK spojeno s předcházejícím bytem, se kterým náhodou dohromady dávají jeden validní GBK znak. Zbude tak apostrof. Řešením pro tento problém i některé další je používat modernější funkci. srpen 2006

část 5, díl 3, kap. 3, str. 10 BEZPEČNÁ POČÍTAČOVÁ SÍŤ srpen 2006

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.1, str. 1 5/3.3.1 XSS DETAILNĚJI (PHP) V oblasti informační bezpečnosti se takřka každý týden objeví nějaká studie ukazující nějakou hrozbu, nějaký trend či směr. Většinou se ukazují počty chyb v různých konkurenčních systémech či počty útoků na ně. Na přelomu září a října roku 2006 jedna taková studie prolétla elektronickými médii s trochu větší mírou povšimnutí, než je obvyklé. Jednalo se o materiál organizace Mitre Corporation, což je americká, de facto vládní agentura (placená ze státních prostředků). Její studie měla za úkol zjistit, jaký druh bezpečnostních problémů nás aktuálně v IT světě nejvíce ohrožuje. Zda se jedná o profláknutý buffer overflow (přetečení bufferu), který můžeme s lehkou mírou sarkasmu označit za všudypřítomný, či o lidský faktor, který se eliminuje ještě hůře. Studie dala na první místo Cross Site Scripting (XSS) - relativně primitivní techniku zneužívání webových aplikací, o které jsme mluvili o pár stránek dříve. To je

část 5, díl 3, kap. 3.1, str. 2 BEZPEČNÁ POČÍTAČOVÁ SÍŤ docela revoluční výsledek, protože až dosud nikdy nikdo (důležitý) tak vysokou míru nebezpečnosti nepřiřkl věci, která má v porovnání s ostatním způsoby hackování tak mělký odborný podtext. Komentář si můžete přečíst například v médiích Computerworld (http://www.computerworld.com/action/ article.do?command=viewarticlebasic&article- Id=9003710), Slashdot (http://it.slashdot.org/article. pl?sid=06/09/25/1440220&from=rss) či ZDNet (http://news.zdnet.co.uk/internet/security- /0,39020375,39283373,00.htm). Článek na ZDNetu má drobnou interpretační chybu v titulku Browser flaws biggest software security risk. Cross Site Scripting je chybou webové aplikace a souvisí s chybami prohlížečů jen nepřímo v tom smyslu, že některé z nich se dají zneužít k lehčímu či úspěšnějšímu zneužití. To se děje v případě, kdy kontext, ze kterého se provádí útok na danou aplikaci, je jiný, než ve kterém běží aplikace samotná (jako kontext je zde myšlena především doména). V tomto případě prohlížeče z bezpečnostních důvodů odmítnou skriptům poskytnout například hodnoty formulářových polí či adresu nějakého rámu (frame) aplikace. V nerespektování tohoto oddělení ale právě spočívá mnoho chyb prohlížečů, XSS je pak někdy možný i v případech, kdy by tomu jinak bylo zabráněno. Účelem této kapitoly je ukázat jednoduché řešení především pro ty účely, kde se z nějakého důvodu buď zapomnělo bezpečnostní ochranu implementovat, nebo je třeba ji z nějakého důvodu třeba zavést nyní, jednoduchým a účinným způsobem. Typicky, pokud se jedná o cizí aplikaci. Skutečně profesionální programátor, který vyvíjí svůj webový software, může být nucen řešit detailněji, o čemž si také povíme (v tomto

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.1, str. 3 smyslu kód odpovídá příslovečné hrubé záplatě, která řeší vše několika řádky). Nicméně pokusme se zamyslet nad tím, proč je Cross Site Scripting stále častěji uváděn v souvislosti s vážnými bezpečnostními hrozbami. Hlavním důvodem, bez kterého by o žádnou hrozbu nešlo, je především reálná šance škodit. XSS, přestože jeho vyvolání je jednoduché, zkrátka funguje, lze pomocí něj pronikat do cizích účtů a různě zcizovat identitu ostatních uživatelů. Stejně tak lze způsobit velké škody provozovateli, ať už na pověsti, nebo na aktuálních datech. XSS můžeme sice považovat za hříčku, ale výsledky zneužití mohou být vážné. Druhým důvodem je fakt, že toto zneužití je i začínajícím webovým programátorům jaksi na dosah. Že odborných znalostí pro vyvolání XSS nemusí mít hacker mnoho, jsme již zmiňovali vícekrát. Velkému (ne)bezpečnostnímu potenciálu XSS dále nahrává, že webové aplikace jsou stále populárnější. Obrovské množství služeb, na které jsme kdysi byli zvyklí jen offline, se přesouvá na web, a to buď úplně, nebo zčásti - expediční firma například nechá vyrobit pro své zákazníky software, který umožní sledování stavu zásilky na webu. Finálním podpůrným faktorem pro smělé šíření XSS technik je nedbalý přístup zadavatelů. Je třeba si uvědomit, že zatímco běžná firma nakupuje běžný offline software na zakázku jen velmi zřídka a menší či střední společnosti často vůbec, webový software je unikátní produkt od produktu. Pokud má služba nějaké přihlašovací rozhranní, je s výjimkou různých diskusních fór (které komerční subjekty stejně nevyužijí) patrně unikátní. Nyní si uvědomme, v jaké situaci

část 5, díl 3, kap. 3.1, str. 4 BEZPEČNÁ POČÍTAČOVÁ SÍŤ Řešení v PHP firma je: převezme webovou aplikaci psanou na míru a kvůli odlišnému zaměření není možnost, jak si může svými vlastními silami ověřit její bezpečnostní kvality. Technické aspekty de facto ani řešit nechce - požaduje, aby vše bez problémů fungovalo. Kvality dodavatelů jsou různé, smluvní odpovědnost často specifikována vágně. Přitom proti XSS je obrana lehká. Potvrzuje se ale známé pořekadlo, že čím je něco lehčí udělat, tím je také lehčí to neudělat, třeba na to zapomenout. Ukažme si hrubou záplatu na problém Cross Site Scriptingu, naprogramovanou v jazyce PHP. Cílem bude, aby se do skriptu nedostaly žádné nebezpečné znaky. Za tímto účelem rekurzivně projdeme všechny proměnné, ve kterých se obsah od klienta nachází. Některé věci necháme obecné (nevyřešené), z důvodu variability účelu. Důvodem pro to je také fakt, že bezpečnostní opatření by neměla být implementována stylem Cut&Paste, což může natropit více škody než užitku. Tedy, nejdříve naprogramujme rekurzivní funkci, která vezme pole referencí. Na každý jeho prvek aplikuje následující: pokud jde o hodnotu, provede filtrování nebezpečných znaků, pokud o pole, pustí na něho sama sebe. Filtrování nechť provádí zatím nespecifikovaná funkce secure(). function safe(&$a) { foreach ($a as $key => $val) if (is_array($val)) safe($a[$key]); else { $a[key] = secure($a[key]) } }

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.1, str. 5 Nyní je třeba safe() zavolat na uživatelské vstupy GET, POST, COOKIE a REQUEST. Není třeba volat safe() postupně, udělejme si z této čtyřky pole a zavolejme ji na něj. $A=array(&$_GET,&$_POST,&$_COOKIE,&$_RE QUEST); safe($a); PHP je pro skriptování skutečně výborný jazyk, jednou z jeho předností je obrovský výběr funkcí. Takřka na každou jen trochu používanou operaci s daty existuje funkce (mnohdy si programátoři píší na jednotlivé úkony vlastní rutiny, aniž by věděli, že taková funkce v PHP již existuje). A právě díky tomu bychom mohli safe() radikálně zkrátit. Místo podmínky použijeme rozhodovací přiřazovací výraz a vlastní aplikování filtru provedeme pomocí array_map(). Výsledek bude vypadat přibližně takto: $a = is_array($a)? array_map( safe, $a): secure($a); A máme jednu řádku. Typické PHP. Všimněte si, že toto zkrácení bude fungovat rekurzivně, přestože array_map rekurzivně nepracuje. Rekurzivita byla zachována opětovným voláním safe(). Jaký je účel rekurzivity? O tom bychom se měli zmínit detailněji. Jde o aspekt, na který zapomínají i zkušení programátoři. Totiž zabezpečit jednotlivé hodnoty, které přicházejí od klienta, profesionál zapomene málokdy. Neuvědomí si ale, že PHP automaticky vytváří ze správně pojmenovaných proměnných pole. Ukažme si případ. Mějme parametry skriptu následující:?a[]=abcd&a[]=efgh&a[]=ijkl&a[pocet]=3

část 5, díl 3, kap. 3.1, str. 6 BEZPEČNÁ POČÍTAČOVÁ SÍŤ Řešení XSS promyslet Zdá se, že jsme třikrát specifikovali stejnou proměnnou, pokaždé s jinou hodnotou. Chyba! PHP z tohoto pozná, že proměnná $a bude pole. Jelikož první tři prvky nespecifikují žádný index, patrně začne se od nuly. V paměti pak bude toto: $a[0]= abcd ; $a[1]= efgh ; $a[2]= ijkl ; $a[pocet]=3; Co se nyní stane, když na $a aplikujeme nějakou funkci bezpečnostního charakteru, která pracuje s řetězcem? PHP odpoví varovným hlášením, že se pokoušíme pole konvertovat na řetězec (array to string conversion) (na hostinzích bývá vypisování chybových hlášení často vypnuto, takže se toto vůbec nedozvíme). Horší ale bude, že zmíněná funkce se na jednotlivé položky pole $a vůbec neaplikuje a jejich obsah projde do nitra aplikace tak, jak jej uživatel serveru poslal. To je obrovské nebezpečí. Pokud by se obsah vypisoval klientovi v HTML, bylo by možné takto propašovat spustitelný skript. Pokud by se dostal do SQL, šlo by provést SQL injection. Nyní ještě pár slov k příkladu. Jak jsme již psali, některé otázky jsou otevřené. Předně je třeba se zamyslet nad tím, jak provádět vlastní filtrování. Do HTML by se neměly dostat především menšítka a většítka, které uvozují HTML tagy. To ale nestačí. Dále je třeba zamezit propagaci těch uvozovacích znaků, které autor používá pro atributy, tj. nejčastěji uvozovky. Důvodem je možnost navěsit skript na nějakou událost objektu, jak jsme se zmiňo-

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.1, str. 7 vali v kapitole o XSS. Pro účely obrany proti SQL injection je nutné escapovat apostrofy, alespoň pokud používáme MySQL. Toto vše řeší například funkce htmlspecialchars(), která převede menšítko na <, většítko na >, uvozovky na " a apostrof na &apos;. A jsme u již zmiňovaného problému. Při ukládání dat do databáze se tak uloží všechny hodnoty poznamenané touto funkcí, což nemusí být vhodné, speciálně v angličtině, kde používáme apostrof častěji. V databázi by měla být data v čisté formě. Obecně by se v programování měly věci implementovat tak, jak jsou funkčně myšleny, nikoliv tak, jak co nejrychleji dosáhnou efektu shodného se správným. Pro účely obrany proti SQL injection se hodnoty escapují jinými funkcemi než pro účely HTML. A to náš skript neřeší a v této podobě může řešit jen velmi špatně. Nicméně zneužití aplikace zabrání a v nových případech stačí. Dále je třeba si promyslet otázku kódování, kterou rozebíráme v souvislosti s UTF-8 na jiném místě. Především pokud aplikaci píšeme v iso-8859-2 či win- 1250, což jsou populární česká kódování, můžeme narazit na nekompatibilitu některých funkcí, konkrétně již zmíněné htmlspecialchars(). Implementaci funkce secure() ze skriptu tak necháme na čtenáři.

část 5, díl 3, kap. 3.1, str. 8 BEZPEČNÁ POČÍTAČOVÁ SÍŤ

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 1 5/3.3.2 OBRANA PROTI SQL INJECTION V minulé kapitole jsme si ukázali praktický příklad, jak lze v PHP řešit jeden z nejčastějších bezpečnostních problémů webových aplikací - Cross Site Scripting. Takřka stejným druhem problému je SQL injection, o čemž se zmiňujeme v obecné části publikace. Tyto dvě hackerské techniky tvoří tak trochu nerozlučnou dvojici. Nespojuje je ani tak programátorská či technologická stránka věci, ale jen ta znalostní. Zkusit šťourat do nějaké webové aplikace ve snaze vyvolat SQL injection útok je stejně jednoduché jako hrát si s XSS, a hacker tak takřka instinktivně zkusí obě dvě techniky. Ukážeme si proto konkrétněji, jak se mohou programátoři proti SQL injection bránit. Informace budou hodnotné i pro IT manažery - neprogramátory, protože jakožto zadavatelé webových projektů si mohou některé popisované techniky (či jejich ekvivalentní obdoby) vynucovat při sjednávání projektu. A tyto tech-

část 5, díl 3, kap. 3.2, str. 2 BEZPEČNÁ POČÍTAČOVÁ SÍŤ Popis problému niky (některé z nich), dokáží SQL injection odrazit z principu, úplně a navždy (pokud jsou na všech místech používány, samozřejmě). Zmíníme se také o nové, připravované vlastnosti PHP 6, o databázové vrstvě PDO. Základní problém SQL injection tkví ve faktu, že při konstrukci SQL dotazu se volně zřetězuje řídicí část SQL dotazu s daty. Laicky řečeno, mám-li vybrat z databáze všechny uživatele starší 30 let, a příkazem pro toto má být jeden řetězec, musí v něm být jak sekvence identifikující, že jde o vybrání z tabulky uživatelů asomezením na věk, tak vlastní hodnota 30. V tomto případě hrozí, že tato hodnota pomocí znaků a slov, vyskytujících se v řídicí části, její smysl naprosto změní. Problémem je, že toto míchání se děje prostým skládáním řetězců dohromady, jeden za druhým. Například: $query = SELECT * FROM uzivatele WHERE vek>.$vek. AND aktivni=1 ;. V tomto případě jsme spojili celkem tři řetězce do jednoho ($query). To je v praxi realizováno naprosto jednoduše alokováním další paměti, překopírováním obsahu jednoho řetězce a následně druhého. Řetězce samotné jsou relativně základním typem objektů většiny programovacích jazyků a jejich spojování (konkatenace) je tedy dosti systémová záležitost. Tím pádem nelze nijak smysluplně navěsit na tuto událost nějaké bezpečnostní prvky. Čistě teoreticky si můžeme takovou situaci v některých jazycích představit, ale velmi těžko tak, aby výsledek dobře sloužil našemu účelu, a už vůbec ne v PHP.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 3 Pokud chce programátor ošetřit nebezpečné znaky v proměnné $vek z našeho případu, musí na ni použít nějakou funkci, která zajistí neinterpretování nebezpečných znaků, v tomto případě apostrofu. To je ale docela šikanující úkon, protože se to musí dít při sestavování každého dotazu. Není tedy divu, že mnoho programátorů, kteří se vývojem neživí třeba jen krátce, to často opomíjejí. Jinou variantou je snad jen použití magic quotes, nicméně převažující nevýhody tohoto řešení jsme již probírali. Jak tedy postupovat? Je třeba navrhnout jiný systém pro tvorbu vlastního dotazu. Většina řešení volí syntax známé rodiny funkcí printf()/sprintf(), populární především v jazycích C/C++, ale nacházejících se ostatně i v PHP. Řeší totiž přesně to, co potřebujeme - oddělení formátovacího řetězce od vlastních hodnot. Ukažme si definici funkce a velmi jednoduchý příklad z PHP dokumentace: string sprintf ( string format [, mixed args [, mixed...]]) printf( There are %d monkeys in the %s, $num, $location); Jak vidíme, první argument se jmenuje format a je povinný. Následuje řada variabilního počtu argumentů. Ve vlastním volání vidíme jakési znaky %d a %s. Jejich funkce je v určení místa a formátu hodnot, které po formátovacím parametru následují. Tedy na první %_ (kde _ je nějaké písmeno) se vloží první hodnota (tj. druhý parametr, $num), na druhý %_ druhá hodnota (tj. třetí parametr, $location). Podotkněme, že původní význam této funkce nemá s bezpečností absolutně nic společného. Důvod je, že Styl sprintf()

část 5, díl 3, kap. 3.2, str. 4 BEZPEČNÁ POČÍTAČOVÁ SÍŤ SafeSQL v kompilovaných jazycích v žádném případě nelze spojovat řetězce s čísly takto jednoduše, jak jsme zvyklí ze skriptovacích jazyků - kompilátor by okamžitě nahlásil chybu. Pro výpis různých hlášení obsahujících proměnné jiných typů je třeba vymyslet nějaký (tento) formátovač. Nyní je již patrně jasné, jak budeme při zabezpečování SQL dotazů postupovat. Totiž úplně stejně. SQL dotaz vytvoříme z formátovacího řetězce a jednotlivých hodnot. Jejich vkládání ale bude zařizovat nějaká funkce, která správně a přesně aplikuje bezpečnostní prvky. Jediné, co se bude lišit, jsou různé způsoby implementace (PHP umožňuje všeho dosáhnout několika technikami...) a pár drobných výjimek a optimalizací pro lepší používání. Začněme knihovnou SafeSQL. Ta je pod licencí Lesser GPL volně k dispozici na adrese http://www.phpinsider.com/php/code/safesql/. Důvodem, proč stojí za to se jí zabývat, je její relativně profesionální zpracování, na rozdíl třeba od uživatelských komentářů k původní funkci myslq_query() - http://cz2.php.net/- mysql_query. Tam se komentující programátoři silně vyřádili - takřka každý druhý příspěvek řeší tento problém, nicméně velmi povrchně. SafeSQL podporuje formátovače %s, %i, %f, %c, %l, %q, %S, %I, %F, %C, %L, %Q, většina z nich kopíruje význam z sprintf() (k tomu se ještě vrátíme). Knihovna je celá objektová (o přínosu nejsem v tomto případě přesvědčen). Jaké jsou formátovací možnosti konkrétněji? Předně je zde %s, značící řetězec, typicky uvnitř apostrofů. Základní celé číslo, integer, značí %i, typicky

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 5 naopak mimo apostrofy. Desetinné číslo (float) se vyznačí pomocí %f. Dále tu jsou podstatnější zjednodušení. Číselná množina se vloží pomocí %c, zde si uveďme příklad: $foobar = array(1,2,34,55); $safe_q = $safesql->query( select * from foo where bar in (%c), array($foobar) ); Výsledkem bude dotaz: select * from foo where bar in (1,2,34,55). Hodnoty reprezentované identifikátorem se zavádějí pomocí %l: $foobar = array( one, two, three, four ); $safe_q = $safesql->query( insert into foo (myset) values (%l);, array($foobar) );...výsledek bude: insert into foo (myset) values (one,two,three,four). A nakonec hodnoty reprezentované řetězcem řeší %q: $foobar = array( a, b, c, d, e ); $safe_q = $safesql->query( select * from foo where bar in (%q), array($foobar) ); S výsledkem: select * from foo where bar in ( a, b, c, d, e ).

část 5, díl 3, kap. 3.2, str. 6 BEZPEČNÁ POČÍTAČOVÁ SÍŤ Ukázkové řešení V úvodu krátké dokumentace autor popisuje příklad, kdy se z tabulky míst vybírá Fred s place, sloupec id musí být v množině a, b a c s and d s a location má být $location, pokud tato proměnná není prázdný řetězec. PHP kód, zajišťující bezpečné vykonání tohoto dotazu, je relativně složitý. Je třeba ošetřit všechny položky pole, všechny proměnné a vedle vyřešit zahrnutí omezení na Location, pokud $location!=. Kód vypadá následovně, přičemž vlastní dotaz je poslední část. $sec_name = Fred s place ; $section_ids = array( a, b, c s and d s ); $location = Lincoln s best ; if(!empty($location)) { $location_clause = and Location =. ;addslashes($location). ; { else } $location_clause = ; } set_type($section_ids, array ); foreach($section_ids as key=$key val=$val) { $section_ids[$key] = addslashes($val); } $query_string = select * from sections where SectionName =. addslashes($sec_name). and id in (. implode(,,slash_array- ($section_ids)). ) and timestamp >=. time(). $location_clause; Jak je vidět, relativně komplikovaná věc a náchylnost na chybu určitě nebude malá. Nyní si vyřešme tento příklad pomocí SafeSQL. Zformování dotazu bude mít taktéž čtyři části, konkrétně nastavení proměnných (1),

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 7 vytvoření objektu (2), zapsání formátovacího řetězce (3) a vlastní vygenerování dotazu (4). Ale podívejme se na výsledek: //1 $sec_name = Fred s place ; $section_ids = array( a, b, c s and d s ); $location = Lincoln s best ; //2 $safesql =& new SafeSQL_MySQL; //3 $query_string = <<< EOQ select * from sections where SectionName = %s and id in (%q) and timestamp >= %i [ and Location = %S ] EOQ; //4 $safe_q = $safesql->query( $query_string, array( $sec_name, $section_ids, time(), $location ) ); I neprogramátorovi musí být jasné, že tato konstrukce je minimálně napsaná daleko úhledněji. Všechna možná vnořená volní se vytratila, kostra SQL dotazu je jasná a čitelná. Jediný rozdíl oproti standardnímu SQL je část v hranatých závorkách, která bude vypuštěna, pokud obsah

část 5, díl 3, kap. 3.2, str. 8 BEZPEČNÁ POČÍTAČOVÁ SÍŤ na místě značky %S bude nulový. Toto je v SafeSQL implementováno jaksi navíc. Použití je závislé na variantě s velkými písmeny (%S, %I, %F, %C, %L, %Q). Také je vidět, že vlastní hodnoty se předávají v druhém parametru pomocí pole, zde by možná bylo estetičtější definovat pole literálem. Jaký je výsledný SQL řetězec? select * from sections where SectionName = Fred\ s place and id in ( a, b, c\ s and d\ s ) and timestamp >= 987654321 and Location = Lincoln\ s best Jak vidíme, všechny apostrofy se správně vyescapovaly pomocí zpětného lomítka, a to ve všech parametrech. Jak již bylo řečeno, knihovnu SafeSQL jsme vybrali díry relativně robustnímu řešení daného úkonu, to je patrné i ze samotné velikosti souboru, více než 9 kb. Nicméně stejně jako při řešení XSS bychom se při řešení bezpečnosti neměli jen tak spoléhat na cizí kód. Ukažme si tedy, jak se k problému postavili někteří jiní programátoři, jejichž funkce můžete najít v příspěvcích na stránce php dokumentace ohledně funkce mysql_select (http://cz.php.net/mysql_query). Nápady budou hodnotné především pro ty programátory, kteří se rozhodnou si podobnou funkci naprogramovat sami. Díky relativně přísným nárokům na kvalitu příspěvků (jejich hodnota je ručně ověřována) se takto dá najít mnoho užitečných funkcí, které nám ušetří čas. Nicméně robustnost příspěvků samozřejmě nemůže být s kompletní knihovnou srovnávána.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ Autor tohoto skriptu měl jistě dobré úmysly, ale jeho řešení postrádá nadhled a v mnoha případech nebude fungovat. Největší problém je, že tato varianta počítá s vynecháním důležitých apostrofů v řídicí části dotazu s tím, že je na svá místa doplní sama. To je ale dost problematické, protože není jasné, v jakých případech se tak má dít a v jakých ne. Autorova podmínka nemusí být dostatečná. Přesněji, příklad předpokládá, že v poli předávaných hodnot budou opravdu jen hodčást 5, díl 3, kap. 3.2, str. 9 Klasické řešení se nabízí pomocí použití regulérních výrazů. Jedna z ukázek implementuje funkci, která generuje SQL dotaz ze zápisu: mysql_query_params( SELECT * FROM my_table WHERE col1=$1 AND ;col2=$2, array( 42, It s ok ) ) Opět je vidět řešení polem. Samotné nahrazování probíhá vybráním značky $číslo pomocí regulérního výrazu a jeho následné vložení pomocí callback funkce mysql_query_params callback. \return mysql_query( preg_replace_callback( \$([0-9]+)/, mysql_query_params callback, $query ) ) Samotná callback funkce dostane jako parametr klasické pole o výsledcích hledání regulérním výrazem (viz dokumentace k PHP), takže stačí již jen sáhnout na příslušné místo do druhého parametru mysql_query_params. Před tím je ale ještě provedeno zabezpečení hodnot pomocí cyklu: foreach( $parameters as $k=>$v ) $parameters[$k] = ( is_int( $v )? $v : ( NULL===$v? ; NULL :.mysql_real_escape_string( $v ). ) )

část 5, díl 3, kap. 3.2, str. 10 BEZPEČNÁ POČÍTAČOVÁ SÍŤ noty, nikoliv výrazy, jako například now() či cokoliv, co počítá s hodnotami ve sloupci. Pokud bychom například chtěli vypsat všechny články do dnešního data, podmínka by vypadala takto: datum<now(). Jenže při použití mysql_query_params( SELECT... datum<$1, array( now() ); dostaneme výsledek SELECT... datum< now() Apostrofy způsobí nefunkčnost. Bohužel nelze rozhodnout, kdy se mají a kdy nemají použít. Z tohoto důvodu je jejich automatické doplňování minimálně diskutabilní. Nicméně kladem je teoretický (!) poznatek, že pro toto vlastně funkce není určena, řeší se bezpečnost hodnot a veškeré výrazy mají být uvedeny v řídicí části. Za předpokladu programátorova srozumění s tímto faktem je možné použití takto naprogramované funkce doporučit, nicméně samozřejmě nelze zdaleka vyloučit rozčarování v prvním momentu, kdy bude třeba do hodnot výraz dostat (a nebude možné měnit řídicí část). Nabízí se taktéž možnost odlišit výrazy od hodnot v zápisu, tj. v definici příslušného pole při volání funkce. Přidávání apostrofů by tak mohlo být vynecháno v případě, že by obsah byl například uzavřen do hranatých závorek. Mimochodem v příkladu se také testují hodnoty na integer a v případě shody se apostrofa vynechává. Toto je s největší pravděpodobností snaha vyhovět dikci, že vykonání dotazu bude pomalejší, pokud se bude porovnávat číselná hodnota se zápisem v apostrofech. To

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 11 je uvedeno v řadě knih o MySQL, ale novější verze obsahují optimalizační postupy, které tuto vadu odstraňují. Ale zpět k problému - z principiálního hlediska se zahrnutí apostrofů v řídicí části dotazu jeví jako výhodnější už ze sémantického hlediska. Jiný autor řeší tento problém použitím různých znaků pro vyznačení míst hodnot. Podíváme-li se na komentář ve skriptu, zjistíme toto: /* Wildcard Rules * SCALAR (?) => original string quoted * OPAQUE (&) => string from file quoted * MISC (~) => original string (left as-is ) */ Tento autor ještě přidal možnost číst hodnoty přímo ze souboru, což je naprostý nesmysl, respektive nesystematičnost a chyba v návrhu. Ostatní přispěvatelé nabízí řešení buď nezajímavá, nebo shodná s prvním. Problémem všech řešení je jistá omezenost, nikdy nebudeme schopni postihnout všechny případy jako při jednoduchém spojování řetězce. Často se u složitějších dotazů vytváří různé části na různých místech a nemusí mít pevně danou délku. Kdybychom měli postihnout i toto, museli bychom navrhnout nějakou obecnou strukturu-pole, kde by například první položka znamenala řídicí řetězec a všechny další hodnoty. V případě, že by hodnota byla také pole, generování dotazu by se spustilo rekurzivně. Dále by bylo možné implementovat pojmenování vyznačených míst (obdobně jako při pojmenování částí regulérního výrazu).

část 5, díl 3, kap. 3.2, str. 12 BEZPEČNÁ POČÍTAČOVÁ SÍŤ PDO Je třeba ale poznamenat, že není dobré se dostat do nějakého nestandardního tweakování jazyka, zvláště ne pro tento účel. Takto zásadní operace by měly zůstat čitelné pro co nejvíce programátorů, je tedy dobré se držet toho, co je buď běžné, nebo během krátké chvilky intuitivně pochopitelné. Zvláště když pro tyto účely má PHP v blízké budoucnosti nabízet standardizované postupy. Špatná standardizace práce s databázemi byla po dlouhou dobu doménou PHP. Jazyk obsahoval podporu pro práci s databázemi jako MySQL, což vedlo programátory k psaní špatně přenosných aplikací, často svázaných na jeden konkrétní databázový stroj. Navíc v poslední době i samotné změny v MySQL způsobily, že některé aplikace nebyly přenosné ani v rámci různých databázových rozhranní MySQL, tj. mysql_* vs. mysqli_. Většina programátorů si dřív či později de facto z donucení napsala nějakou vlastní mezivrstvu pro práci s databází, což je sice krok logický, ale vůči standardizaci naprosto opačný. Je faktem, že ani v podmínce českých hostingových společností v některých případech nelze použít aplikaci bez alespoň nějaké databázové vrstvy, protože se najdou takoví poskytovatelé, kteří omezují použití na rozhranní mysqli_, standardní mysql_ nefunguje. PDO by mělo všechny tyto nešvary vyřešit. Pojetí práce s databázemi je úplně jiné, než na jaké jsou programátoři zvyklí, což je patrné z následujících rozdílů - PDO je psané v C, nikoliv PHP. Tedy nejedná se o žádný framework ve smyslu knihovny v PHP. - PDO nepracuje se standardními databázovými funkcemi, ale pro jednotlivé databázové stroje umožňuje použití takzvaných ovladačů.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 13 - PDO přináší práci s databázemi objektově orientovaný koncept. V současnosti je PDO zahrnuto v PHP 5.1, nicméně implementace je stále označována jako experimentální (ono je totiž v experimentálním stavu i několik ovladačů, konkrétně Oracle, Microsoft SQL Server, Firebird a další, stabilní jsou samozřejmě ovladače pro webové klasiky MySQL a PostgreSQL). Úspěch PDO zatím nelze předpovědět, protože přechod na tak zásadní technologii si nutně vyžádá nějaký čas a tlak z minulosti, tj. zachovat maximum věcí kompatibilních, je vždy silný. Z bezpečnostního hlediska PDO přináší především změnu ve způsobu, jak je (resp. by měl být) konstruován SQL dotaz. Nejprve je dotaz připraven a jsou v něm vyznačena místa, kde budou uživatelské hodnoty. Následně jsou s těmito místy spárovány proměnné a nakonec je dotaz vykonán. Tento způsob je revoluční i v jiných než bezpečnostních ohledech. Ukažme si příklad z dokumentace k PDO. <?php $stmt = $dbh->prepare( INSERT INTO RE- GISTRY (name, value) VALUES (:name, :value) ); $stmt->bindparam( :name, $name); $stmt->bindparam( :value, $value); // insert one row $name = one ; $value = 1; $stmt->execute(); // insert another row with different values $name = two ; $value = 2; $stmt->execute();?>

část 5, díl 3, kap. 3.2, str. 14 BEZPEČNÁ POČÍTAČOVÁ SÍŤ Jak je vidět, tzv. paceholdery jsou pojmenované syntaxí :jméno. Následně s nimi programátor spároval proměnné $name a $value. Teď přijde změna - toto párování není dosazením, to se děje až při volání metody execute(). Tím pádem každé execute() vykoná SQL dotaz s aktuálními hodnotami. Vlastní hodnoty lze ale specifikovat i jako parametr již zmíněné metody execute(). Dokumentace uvádí následující příklad, ve kterém jsou placeholdery vyznačeny otazníky (tyto dvě metody nelze kombinovat). <?php $stmt = $dbh->prepare( SELECT * FROM REGISTRY where name =???); if ($stmt->execute(array($_get[ name ]))){ while ($row = ;$stmt->fetch()){ print_r($row) } }?> Tento koncept je podobný tomu, jaký jsme si sami vytvářeli v předešlé kapitole, pouze se kostra SQL dotazu a data nezapisují na jednom místě. Jak již bylo řečeno, knihovnu SafeSQL jsme vybrali díky relativně robustnímu řešení daného úkonu, to je patrné i ze samotné velikosti souboru, více než 9 kb. Nicméně stejně jako při řešení XSS bychom se při řešení bezpečnosti neměli jen tak spoléhat na cizí kód. Ukažme si tedy, jak se k problému postavili někteří jiní programátoři, jejichž funkce můžete najít v příspěvcích na stránce php dokumentace ohledně funkce mysql_select (http://cz.php.net/mysql_query). Nápady budou hodnotné především pro ty programátory, kteří se rozhodnou si podobnou funkci naprogramovat sami.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 15 Díky relativně přísným nárokům na kvalitu příspěvků (jejich hodnota je ručně ověřována) se takto dá najít mnoho užitečných funkcí, které nám ušetří čas. Nicméně robustnost příspěvků samozřejmě nemůže být s kompletní knihovnou srovnávána. Klasické řešení se nabízí pomocí použití regulérních výrazů. Jedna z ukázek implementuje funkci, která generuje SQL dotaz ze zápisu: mysql_query_params( SELECT * FROM my_table WHERE col1=$1 AND ;col2=$2, array( 42, It s ok ) ) Opět je vidět řešení polem. Samotné nahrazování probíhá vybráním značky $číslo pomocí regulérního výrazu a jeho následné vložení pomocí callback funkce mysql_query_params callback. \return mysql_query( preg_replace_callback( /$([0-9]+)/, ; mysql_query_params callback, $query ) ) Samotná callback funkce dostane jako parametr klasické pole o výsledek hledání regulérním výrazem (viz dokumentace k PHP), takže stačí již jen sáhnout na příslušné místo do druhého parametru mysql_query_params. Před tím je ale ještě provedeno zabezpečení hodnot pomocí cyklu: foreach( $parameters as $k=>$v ) $parameters[$k] = ( is_int( $v )? $v : ( NULL===$v? NULL :.mysql_real_escape_string( $v ). ) ); Autor tohoto skriptu měl jistě dobré úmysly, ale jeho řešení postrádá nadhled a v mnoha případech nebude fungovat. Největší problém je, že tato varianta počítá

část 5, díl 3, kap. 3.2, str. 16 BEZPEČNÁ POČÍTAČOVÁ SÍŤ s vynecháním důležitých apostrofů v řídicí části dotazu s tím, že je na svá místa doplní sama. To je ale dost problematické, protože není jasné, v jakých případech se tak má dít a v jakých ne. Autorova podmínka nemusí být dostatečná. Přesněji příklad předpokládá, že v poli předávaných hodnot budou opravdu jen hodnoty, nikoliv výrazy, jako například now() či cokoliv, co počítá s hodnotami ve sloupci. Pokud bychom například chtěli vypsat všechny články do dnešního data, podmínka by vypadala takto: datum<now(). Jenže při použití mysql_query_params( SELECT... datum<$1, array( now() ); dostaneme výsledek SELECT... datum< now() Apostrofy způsobí nefunkčnost. Bohužel, nelze rozhodnout, kdy se mají a kdy nemají použít. Z tohoto důvodu je jejich automatické doplňování minimálně diskutabilní. Nicméně, kladem je teoretický (!) poznatek, že funkce pro to vlastně není určena, řeší se bezpečnost hodnot a veškeré výrazy mají být uvedeny v řídicí části. Za předpokladu programátorova srozumění s tímto faktem je možné použití takto naprogramované funkce doporučit, nicméně samozřejmě nelze zdaleka vyloučit rozčarování v prvním momentu, kdy bude třeba do hodnot výraz dostat (a nebude možné měnit řídicí část). Nabízí se taktéž možnost odlišit výrazy od hodnot v zápisu, tj. v definici příslušného pole při volání funkce. Přidávání apostrofů by tak mohlo být vynecháno v případě, že by obsah byl například uzavřen do hranatých závorek.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ Problémem všech řešení je jistá omezenost, nikdy nebudeme schopni postihnout všechny případy jako při jednoduchém spojování řetězce. Často se u složitějších dotazů vytvářejí různé části na různých místech a nemusí mít pevně danou délku. Kdybychom měli postihnout i to, museli bychom navrhnout nějakou obecnou strukturu-pole, kde by například první položka znamenala řídicí řetězec a všechny další hodčást 5, díl 3, kap. 3.2, str. 17 Mimochodem v příkladu se také testují hodnoty na integer a v případě shody apostrofy vynechávají. To je s největší pravděpodobností snaha vyhovět dikci, že vykonání dotazu bude pomalejší, pokud se bude porovnávat číselná hodnota se zápisem v apostrofech. To je uvedeno v řadě knih o MySQL, ale novější verze obsahují optimalizační postupy, které tuto vadu odstraňují. Ale zpět k problému - z principiálního hlediska se zahrnutí apostrofů v řídicí části dotazu jeví jako výhodnější, už ze sémantického hlediska. Jiný autor řeší tento problém použitím různých znaků pro vyznačení míst hodnot. Podíváme-li se na komentář ve skriptu, zjistíme toto: /* Wildcard Rules * SCALAR (?) => original string quoted * OPAQUE (&) => string from file quoted * MISC (~) => original string (left as-is ) */ Tento autor ještě přidal možnost číst hodnoty přímo ze souboru, což je naprostý nesmysl, respektive nesystematičnost a chyba v návrhu. Ostatní přispěvatelé nabízejí řešení buď nezajímavá, nebo shodná s prvním.

část 5, díl 3, kap. 3.2, str. 18 BEZPEČNÁ POČÍTAČOVÁ SÍŤ noty. V případě, že by hodnota byla také pole, generování dotazu by se spustilo rekurzivně. Dále by bylo možné implementovat pojmenování vyznačených míst (obdobně jako při pojmenování částí regulárního výrazu). Je třeba ale poznamenat, že není dobré se dostat do nějakého nestandardního tweakování jazyka, zvláště ne pro tento účel. Takto zásadní operace by měly zůstat čitelné pro co nejvíce programátorů, je tedy dobré se držet toho, co je buď běžné, nebo během krátké chvilky intuitivně pochopitelné. Zvláště když pro tyto účely má PHP v blízké budoucnosti nabízet standardizované postupy. PDO Špatná standardizace práce s databázemi byla po dlouhou dobu doménou PHP. Jazyk obsahoval podporu pro práci s databázemi jako MySQL, což vedlo programátory k psaní špatně přenosných aplikací, často svázaných na jeden konkrétní databázový stroj. Navíc v poslední době i samotné změny v MySQL způsobily, že některé aplikace nebyly přenosné ani v rámci různých databázových rozhranní MySQL, tj. mysql_* vs. mysqli_. Většina programátorů si dříve či později de facto z donucení napsala nějakou vlastní mezivrstvu pro práci s databází, což je sice krok logický, ale vůči standardizaci naprosto opačný. Je faktem, že ani v podmínce českých hostingových společností v některých případech nelze použít aplikaci bez alespoň nějaké databázové vrstvy, protože se najdou takoví poskytovatelé, kteří omezují použití na rozhranní mysqli_, standardní mysql_ nefunguje.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.2, str. 19 PDO by mělo všechny tyto nešvary vyřešit. Pojetí práce s databázemi je úplně jiné, než na jaké jsou programátoři zvyklí, což je patrné z následujících rozdílů: - PDO je psané v C, nikoliv PHP; tedy nejedná se o žádný framework ve smyslu knihovny v PHP; - PDO nepracuje se standardními databázovými funkcemi, ale pro jednotlivé databázové stroje umožňuje použití takzvaných ovladačů; - PDO přináší práci s databázemi objektově orientovaný koncept. V současnosti je PDO zahrnuto v PHP 5.1, nicméně implementace je stále označována jako experimentální (ono je totiž v experimentálním stavu i několik ovladačů, konkrétně Oracle, Microsoft SQL Server, Firebird a další, stabilní jsou samozřejmě pro webové klasiky MySQL a PostgreSQL). Úspěch PDO zatím nelze předpovědět, protože přechod na tak zásadní technologii si nutně vyžádá nějaký čas a tlak z minulosti, tj. zachovat maximum věcí kompatibilních, je vždy silný. Z bezpečnostního hlediska PDO přináší především změnu ve způsobu, jak je (resp. by měl být) konstruován SQL dotaz. Nejprve je dotaz připraven a jsou v něm vyznačena místa, kde budou uživatelské hodnoty. Následně jsou s těmito místy spárovány proměnné a nakonec je dotaz vykonán. Tento způsob je revoluční i v jiných než bezpečnostních ohledech. Ukažme si příklad z dokumentace k PDO. <?php$stmt = $dbh->prepare( INSERT INTO REGISTRY (name, value) VALUES (:name, :value) ); $stmt->bindparam( :name, $name); $stmt->bindparam( :value, $value);

část 5, díl 3, kap. 3.2, str. 20 BEZPEČNÁ POČÍTAČOVÁ SÍŤ // insert one row $name = one ; $value = 1; $stmt->execute(); // insert another row with different values $name = two ; $value = 2; $stmt->execute();?> Jak je vidět, tzv. paceholdery jsou pojmenované syntaxí :jméno. Následně programátor s nimi spároval proměnné $name a $value. Teď přijde změna - toto párování není dosazení, to se děje až při volání metody execute(). Tím pádem každé execute() vykoná SQL dotaz s aktuálními hodnotami. Vlastní hodnoty lze ale specifikovat i jako parametr již zmíněné metody execute(). Dokumentace uvádí následující příklad, ve kterém jsou paceholdery vyznačeny otazníky (tyto dvě metody nelze kombinovat). <?php $stmt = $dbh->prepare( SELECT * FROM REGISTRY where name = {? ); if ($stmt->execute(array($_get[ name ]))) { while ($row = $stmt->fetch()) { print_r($row); } }?> Tento koncept je podobný tomu, jaký jsme si sami vytvářeli v předešlé kapitole, pouze se kostra SQL dotazu a data nezapisují na jednom místě.

BEZPEČNÁ POČÍTAČOVÁ SÍŤ část 5, díl 3, kap. 3.3, str. 1 5/3.3.3 CROSS-SITE REQUEST FORGERY V prostředí webových aplikací je Cross Site Scripting a SQL injection jistým fenoménem. Takřka v každé příručce (především začínajícího) webového programátora najdete popis těchto chyb a možností jejich zneužití. Patrně díky jisté podobnosti se ale publikace málokdy zmiňují o podobné, dalo by se říci sesterské chybě Cross-Site Request Forgery (CSRF). Rozdíly jsou ovšem znatelné, v technice jak na straně útočníka, tak při obraně webové aplikace. O tom, jak zneužití chyby vypadá, svým způsobem vypovídá název, který přeložen do českého jazyka odpovídá přibližně názvu podvržení dotazu jinou stránkou. Princip útoku tedy spočívá v donucení uživatele webové aplikace zavolat klasickou validní URL, typicky nějaké akce administračního rozhranní, která ovšem vyvolá nechtěnou reakci. Může jít o úpravu nějakého obsahu, změnu nastavení či smazání nějaké položky. Cílem tedy nebývá zmocnit se spojení (i když