Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bezpečnost webových aplikací

Bezpečnost webových aplikací

Popis několika základních útoků na webové aplikace, metody obrany proti těmto útokům, s ukázkami v PHP a Pythonu. Trocha doporučení k ukládání hesel.

Michal Špaček

September 27, 2012
Tweet

Other Decks in Programming

Transcript

  1. Michal Špaček
    Nebezpečné PyVo, 27. 9. 2012, Brno
    Varování: obsahuje nejen PHP, ale i příklady pro Python
    www.michalspacek.cz @spazef0rze
    WEB

    View Slide

  2. Michal Špaček www.michalspacek.cz
    Útoky na web
    SQL Injection
    Cross-Site Scripting
    Full Path Disclosure
    Útoků na webové aplikace existují desítky. Local File Inclusion, Remote Code Execution a další.
    Povíme si o těchto třech, kde první dva jsou navíc velice závažné. Všechny tři mají jednu
    společnou věc – zabránění těmto útokům je jednoduché, jak facka.

    View Slide

  3. Michal Špaček www.michalspacek.cz
    Full Path Disclosure
    Vyzrazení plné cesty ke skriptu
    http://devblog.cz/2012/04/fpd-aneb-full-path-disclosure/
    Začneme od konce. Full Path Disclosure (FPD) je útok, kterým donutíte webovou aplikaci, aby
    vyzradila kde na disku serveru jsou uloženy skripty dané aplikace. Více o FPD si můžete přečíst v
    mém článku na uvedené adrese.

    View Slide

  4. Michal Špaček www.michalspacek.cz
    V PHP to může vypadat třeba takto. Aplikace nám toho ovšem vyzradí mnohem více, než jen to,
    kde na disku žije. Dozvíme se například operační systém serveru, zjistíme, že je povolené
    zobrazování chybových hlášek a z adresářové struktury odhadneme, že to nepoužívá Nette.

    View Slide

  5. Michal Špaček www.michalspacek.cz
    Příklad z Pythonu pak vypadá třeba takto. Z této chybové hlášky se dozvíte nejenom použitý
    operační systém, seznam funkcí, které byly volány, ale třeba i adresu Redis serveru, ke kterému
    se zrovna nepovedlo připojit.

    View Slide

  6. Michal Špaček www.michalspacek.cz
    Jednou z několika možností, jak v PHP tento útok vyvolat, je změna typu vstupních parametrů. Z
    klasického řetězce uděláme pole přidáním hranatých závorek za název parametru v URL (a před
    rovnítko). Některé funkce však umí pracovat jenom s řetězcem a náležitě nám to oznámí.

    View Slide

  7. Michal Špaček www.michalspacek.cz
    Další variantou FPD útoku je zobrazení výstupu phpinfo(). Na velké části webů psaných v PHP
    se takový výstup nachází na URL /info.php nebo /phpinfo.php. Tyto informace obsahují
    třeba i čísla verzí nebo kompletní nastavení PHP. Nikdy toto nedávejte na produkční servery.

    View Slide

  8. Michal Špaček www.michalspacek.cz
    Řešení?
    display_errors = Off
    Říkal jsem, že řešení jsou jednoduchá, že? Zakažte zobrazování chybových hlášek a hotovo.
    Hlášky si samozřejmě nesmíte vypisovat sami, třeba v nějakém error handleru. Zobrazování
    zakažte pokud možno už v .htaccess pomocí php_flag display_errors off.

    View Slide

  9. Michal Špaček www.michalspacek.cz
    log_errors = On
    Co naopak povolte, je ukládání chybových hlášek do nějakého souboru, abyste se vůbec
    dozvěděli, že k nějakým chybám dochází. To provedete také nejlépe v souboru .htaccess takto:
    php_flag log_errors on, chybové hlášky pak naleznete v error logu serveru.

    View Slide

  10. Michal Špaček www.michalspacek.cz
    DEBUG = False
    V Djangu pak nezapomeňte vypnout debug mód. Některé další frameworky, jako třeba Bottle
    nebo Flask ho mají standardně vypnutý. Pokud používáte nějaký jiný framework, vždy se
    přesvědčte, jak je nastaven.

    View Slide

  11. Michal Špaček www.michalspacek.cz
    Cross-Site Scripting (XSS)
    Útočník vloží na naši stránku
    vlastní JavaScript
    Zábavnějším útokem na webové aplikace a na jejich návštěvníky je Cross-Site Scripting. Útočník
    vložením vlastního JavaScriptu do stránky získává kontrolu nad prohlížečem návštěvníka. Může s
    ním dělat vše, co JavaScript dovoluje, inspirujte se třeba projektem BeEF http://beefproject.com/

    View Slide

  12. Michal Špaček www.michalspacek.cz
    Úspěšný útok Cross-Site Scripting může vypadat třeba takto. Toto je pouze Proof-of-Concept kód,
    načítající JavaScript z cizího serveru, zobrazení identifikátoru session nemá žádný praktický
    dopad. Všimněte si hodnoty parametru zb v URL adrese a taky toho, že toto není PHP stránka.

    View Slide

  13. Michal Špaček www.michalspacek.cz
    Všimněte si označeného textu. Tento HTML tag vložil útočník úpravou adresy stránky (viz
    předchozí obrázek) a webová aplikace jej zapomněla zabezpečit. Útočníkovi pak stačí takovou
    adresu (třeba zkrácenou pomocí TinyURL) poslat oběti, této variantě se říká Reflected XSS.
    Variantě, kdy útočník vloží HTML značky například do diskuzního příspěvku a aplikace jej neošetří
    říkáme Permanent XSS, protože je zkrátka permanentní, útočník nemusí upravovat odkaz a nic
    nemusí nikomu posílat, stačí jenom navšívit takovou napadenou stránku.

    View Slide

  14. Michal Špaček www.michalspacek.cz
    Pomocí vloženého JavaScriptu může útočník dělat spoustu věcí, zde je například ukázka
    odchytávání uživatelských jmen a hesel zadávaných do formuláře s id userform a jejich
    odesílání na server tak, aby uživatel nic nepoznal, formulář se následně normálně odešle.

    View Slide

  15. Michal Špaček www.michalspacek.cz
    Řešení?
    framework
    V Pythonu (a asi i v PHP) je nejlepší možné řešení použít nějaký framework na vypisování dat
    (nebo aspoň nějaký šablonovací systém). Pro jistotu se ale stejně podívejte, jak vlastně pracuje,
    jestli pro prevenci XSS není potřeba nějaké speciální nastavení.

    View Slide

  16. Michal Špaček www.michalspacek.cz
    Řešení?
    htmlspecialchars($string)
    Další jednoduché řešení pro PHP. Pro ochranu před XSS stačí veškerá data (uživatelská i
    aplikační), která do HTML vypisujeme, nejdříve prohnat skrz tuto funkci. Na výpis do JavaScriptu,
    který má jiné speciální znaky (trackovací kódy např.) použijte třeba json_encode().

    View Slide

  17. Michal Špaček www.michalspacek.cz
    htmlspecialchars($string, ENT_QUOTES)
    htmlspecialchars() standardně převádí na HTML entity pouze znaky &, ", < a >. Pokud
    HTML atributy uzavíráte do jednoduchých uvozovek, musíte přidat flag ENT_QUOTES.
    Nejjednodušší je ale použít nějaký šablonovací systém, který celé escapování řeší za vás.

    View Slide

  18. Michal Špaček www.michalspacek.cz
    String.encode('ascii', 'xmlcharrefreplace')
    Jestli chcete v Pythonu zažít středověk nebo si píšete nějaký vlastní šablonovací systém nebo
    framework (dělá to v Pythonu někdo?), tak použijte pro ošetření výstupu proti XSS tuhle metodu
    třídy String.

    View Slide

  19. Michal Špaček www.michalspacek.cz
    Nepoužívat
    strip_tags()
    proti XSS
    Nikdy nepoužívejte na ochranu před XSS funkci strip_tags(). Není na to určená a navíc
    neochrání před tím, když útočník nebude vkládat celé tagy, ale jenom nové atributy (třeba
    onmouseover). Nepoužívejte ji ani na povolení některých tagů, ponechává totiž tagy i atributy.

    View Slide

  20. Michal Špaček www.michalspacek.cz
    Nepoužívat
    lxml / BeautifulSoup
    proti XSS
    Stejně tak v Pythonu nepoužívejte na ochranu před XSS nástroje a třídy jako jsou lxml nebo
    BeautifulSoup. Ty slouží na parsování a filtrování XML nebo HTML a přesto že to dělají na
    jedničku, tak to není ten správný nástroj na ochranu proti XSS.

    View Slide

  21. Michal Špaček www.michalspacek.cz
    SQL Injection
    Útočník modifikuje SQL dotaz
    Dopad útoku SQL Injection může být poměrně značný. Pokud je úspěšný, tak má útočník pod
    kontrolou celou databázi webové aplikace a může si s ní dělat, co chce. Například získávat
    uživatelská jména a hesla nebo finanční výsledky majitele. A taky měnit uložená data, třeba ceny.

    View Slide

  22. Michal Špaček www.michalspacek.cz
    Útok spočívá v úpravě SQL dotazu, který aplikace odesílá do databázového serveru. Napsáním
    speciálních znaků do některého parametru v adrese (zde znacka) vyvoláme chybu a zkoumáním
    a hledáním zadaného řetezce (třetí řádek shora, vpravo) zjistíme, jak parametr upravit.

    View Slide

  23. Michal Špaček www.michalspacek.cz
    "… WHERE znacka = '%s'" % request.GET['znacka']
    Chyba vyvolaná na této stránce vznikla nejspíš tak, že parametr znacka byl rovnou vložen do
    SQL dotazu, mezi jednoduché uvozovky, bez nějakého ošetření. Původně to byl PHP skript, ale
    naznačen je kód v Pythonu, který je chybně napsán a umožňuje provést SQL Injection útok.

    View Slide

  24. Michal Špaček www.michalspacek.cz
    Zde vidíme podobnou chybu, jako na předchozím obrázku. Parametr id původně obsahoval
    nějaké číslo, ale uvedením řetezce se útočníkovi podařilo vyvolat tuto chybovou hlášku, kterou na
    sebe aplikace vyzradila, že útoku SQL Injection neodolá.

    View Slide

  25. Michal Špaček www.michalspacek.cz
    Kód ve skriptu vypadal nejspíš nějak takto. Parametr id je rovnou připojen na konec SQL dotazu,
    není uzavřen v jednoduchých uvozovkách a proto si databázový server myslí, že je to název
    sloupce. I bez zobrazeného SQL dotazu útočník dokáže odhadnout, co má do parametru vložit.
    '… WHERE id = %s' % request.GET['id']

    View Slide

  26. Michal Špaček www.michalspacek.cz
    Řešení?
    Prepared statements (PDO)
    V případě ochrany proti SQL Injection je řešení trochu náročnější. Místo skládání řetězců
    oddělíme odesílání SQL kódu od dat a nemůže se tedy stát, že modifikací dat změníme kód. V
    PHP toto elegantně řeší extenze PDO. Někdy se můžete setkat s termínem vázání proměnných.

    View Slide

  27. Michal Špaček www.michalspacek.cz
    Takto nějak vypadá práce s rozšířením PDO. Na řádku 2 vytvoříme spojení s databázovým
    serverem. Na řádku 3 je pak uveden SQL dotaz a místo hodnot jsou uvedena zástupná jména
    parametrů ve formátu :jmeno. Na řádku 4 řekneme databázovému serveru, aby si připravil
    vykonání dotazu. Metodou bindValue() propojíme zástupné jméno s hodnotou, všiměte si, že
    hodnotu nikde nijak speciálně neošetřujeme a že v dotazu nejsou kolem :id uvozovky. Na řádku
    6 konečně odešleme data na databázový server a tím efektivně vykonáme dotaz.

    View Slide

  28. Michal Špaček www.michalspacek.cz
    mysql_set_charset()
    mysql_real_escape_string()
    Rozšíření mysql v PHP vázání proměnných nijak elegantně nepodporuje. Pro ošetření hodnot v
    SQL dotazu pak musíme použít funkci mysql_real_escape_string() a nesmíme po
    připojení zapomenout zavolat mysql_set_charset(). Uvedené řešení ovšem nevyřeší
    problém, který je ukázán na stránce 25, tedy případ, kdy v SQL dotazu očekáváme číslo. Tyto
    funkce slouží k zabránění „útěku z řetezce“, ale pokud v dotazu očekáváme číslo, tak v žádném
    řetezci nejsme. Pro ochranu v tomto případě zvolíme přetypování na integer.

    View Slide

  29. Michal Špaček www.michalspacek.cz
    Python?
    oursql, MySQLdb,
    psycopg2, SQLAlchemy
    Pro Python existuje spousta oblíbených rozšíření pro práci nejen s MySQL (psycopg2 je pro
    PostgreSQL), které prepared statements řeší. Použít lze i ORM nástroje, u kterých je ale obrana
    proti SQL Injection spíše takovým vedlejším efektem.

    View Slide

  30. Michal Špaček www.michalspacek.cz
    oursql
    cursor.execute(
    "SELECT id FROM users
    WHERE user = ? AND password = ?",
    (user, password))
    V rozšíření oursql se dotazy spouštějí takto. Důležité jsou otazníky, kolem kterých nejsou žádné
    uvozovky, tzv. placeholdery a to, že se vlastní hodnoty do metody execute předávají jako její další
    parametr.

    View Slide

  31. Michal Špaček www.michalspacek.cz
    psycopg2
    cursor.execute(
    "SELECT id FROM users
    WHERE user = %s AND password = %s",
    (user, password))
    V psycopg2 pro PostgreSQL je to podobné, s tím rozdílem, že se zde používá %s jako
    placeholder. Jedná se opravdu jenom o %s a ne třeba o %d, jak by se mohlo zdát, že by mohlo
    fungovat. Vlastní hodnoty jsou do execute opět předány jako další parametr.

    View Slide

  32. Michal Špaček www.michalspacek.cz
    Nikdy!
    cursor.execute(
    "SELECT id FROM users
    WHERE user = '%s' AND password = '%s'" %
    (user, password))
    Mohlo by se stát, že se vývojář splete a místo poslání hodnot ve zvláštním parametru je vlepí
    přímo do dotazu samotného. Stačí taková drobnost, jako zaměnit čárku (,) se znakem procenta
    (%), naštěstí jsou na klávesnici docela daleko od sebe. Pozor na to!

    View Slide

  33. Michal Špaček www.michalspacek.cz
    ' OR 1 -- -
    SELECT id FROM users
    WHERE user = '' OR 1 -- -' AND password = '…'
    Kdyby totiž někdo zadal místo svého jména něco jako ' OR 1 -- -, tak se spustí tento dotaz,
    který vrátí všechny id z databáze, cokoliv OR 1 je totiž vždy pravda a podmínka na heslo je
    zakomentovaná. (V MySQL začíná komentář do konce řádku znaky pomlčka pomlčka mezera)

    View Slide

  34. Michal Špaček www.michalspacek.cz
    Použitelnost
    McDonald's & O'Reilly
    Nejedná se ale jenom o bezpečnost, ačkoliv kvůli té si asi tyto slajdy čtete primárně. Pokud by se
    do vaší aplikace chtěl zaregistrovat někdo z výše uvedených pánů, tak má smůlu, kvůli
    speciálním znakům ve jménech jim to nepůjde. Vy s vaší aplikací jste tak promarnili životní šanci.

    View Slide

  35. Michal Špaček www.michalspacek.cz
    Nepoužívat
    addslashes()
    proti SQLIA
    Vůbec nikdy nepoužívejte funkci addslashes() pro ošetření dat proti SQL Injection Attack.
    Tato funkce, ač zdánlivě dělá to samé, tak neslouží pro ochranu proti SQL Injection. V MySQL v
    některých asijských znakových sadách je i přes použití addslashes() možné SQLIA provést.

    View Slide

  36. Michal Špaček www.michalspacek.cz
    Hashování hesel
    Nepoužívat MD5
    Ani SHA-1
    Pokud v aplikaci ukládáte hesla uživatelů, nikdy je neukládejte jen tak, v čitelné podobě. Hashujte
    je před uložením do databáze. Ovšem není hashování, jako hashování. Pro nové aplikace dnes
    už nepoužívejte MD5 ani SHA-1 a to ani pokud hashujete vícenásobně a už vůbec ne bez saltu.

    View Slide

  37. Michal Špaček www.michalspacek.cz
    Jak MD5, tak SHA-1 jsou už překonané. Předpočítané hashe najdete na webu Googlem, takže
    nemusíte nic crackovat. A když nic nenajdete, tak oba algoritmy jsou tak rychlé, že cracknutí hesel
    je většinou otázka několika minut.

    View Slide

  38. Michal Špaček www.michalspacek.cz
    Řešení?
    Pokud budete hashovat hesla, tak používejte minimálně SHA-512 a rozhodně je nutné použít salt
    jako obranu proti Birthday attacks. Salt může být uložen v databázi ve speciálním sloupci, v
    čitelné podobě. Pro šifrování v Pythonu použijte třeba hashlib nebo rozšíření PyCrypto.
    SHA-512 a salt?
    hashlib, PyCrypto

    View Slide

  39. Michal Špaček www.michalspacek.cz
    Lepší řešení?
    Existuje ale lepší řešení a opět jednoduché, vícenásobné hashování i obranu proti hrubé síle díky
    relativní vlastní pomalosti vyřeší použití funkce crypt() s Blowfish hashing algoritmem, v
    Pythonu lze využít implementaci py-bcrypt. Tento hash doporučuje váš dealer!
    bcrypt!
    Blowfish hashing, py-bcrypt
    http://codahale.com/how-to-safely-store-a-password/

    View Slide

  40. That's it. That's me.
    Díky Martinovi Čarnogurskému za pomoc s příklady z Pythonu.
    Michal Špaček
    www.michalspacek.cz
    @spazef0rze
    Přijďte na školení
    http://www.michalspacek.cz/skoleni
    Tak a to je vše, přátelé. Sledujte mě na Twitteru https://twitter.com/spazef0rze, pokud se chcete
    dozvědět, co se děje v mém světě PHP, bezpečnosti a výkonnosti. Pokud se toho chcete
    dozvědět ještě více z uvedených oblastí, přijďte třeba na školení, která vedu. Díky a zas někdy!

    View Slide