Programujeme pro Drupal: bezpečně
S tím jak roste povědomí o Drupalu v ČR i ve světě, roste také počet lidí, kteří Drupal nepoužívají jen jako CMS, ale také jako webový framework. Píší vlastní PHP kód a případně i moduly. Zvlášť v PHP vše není jen tak a je třeba dodržovat některá základní pravidla, bez kterých bude výsledkem jen napadnutelná aplikace. Jak na to? Vše okořeníme soutěží o tričko.
Mimochodem, výsledkem napadnutelné aplikace je z 90 % uživatel, který nakonec viní Drupal, i když ten za nic nemůže. To není moc spravedlivé. Z práce v Drupal security týmu se s tím setkávám denně.
Každé pravidlo ohodnotím na škále nebezpečnosti, tak jak se později hodnotí zveřejňované chyby.
Pravidlo č. 1 – Don't hack core
Drupal je silný, právě kvůli svému API, rozhraní, přes které mohu dělat vše co si vzpomenu a nedotknout se tak jádra. Přijdu na webtrh, čtu „Systém běží na upraveném Drupalu“ a hned se mi točí panenky. Nikdy neupravujte jádro. Nikdy. Tečka. Nikdy. Nikdy. Napište si to na tabuli nad monitorem: Nikdy.Škála nebezpečnosti: Kritická.
Pravidlo č. 2 – Práce s databází
Tak jako v každém CMS je možné v Drupalu psát SQL dotazy a pracovat s databází. Pokud dodržíte základní pravidla, automaticky Vás Drupal ochrání proti SQL Injection útokům.
Škála nebezpečnosti: Kritická.
K načtení: http://api.drupal.org/…p/database/6
SQL dotaz
A jedem:
// http://www.example.com/vypis_uzel/350
$nid = arg(1);
// ...
mysql_query("SELECT * FROM node WHERE nid = " . $nid ." OR type = " . $type . " OR title LIKE '%" . $title ."%'");
Znáte to?
Tak takhle NE! V jednom malém dotazu jsme udělali dvě chyby: Dotaz bude samozřejmě fungovat jen v MySQL a zároveň jsme vygenerovali triviální SQL Injection.
Takhle je to správně:
$query = db_query("SELECT * FROM {node} n WHERE n.nid = %d OR n.type = '%s' OR n.title LIKE '%%%s%%'", $nid, $type, $title);
Všimněte si několika zajímavých věcí:
- db_query je správná funkce – Drupal zajistí použití správného ovladače databáze (MySQL, PostgreSQL, v Drupalu 7 i Oracle nebo SQLite).
- Jméno tabulky jsme obalili znaky {}, díky tomu Drupal doplní případný prefix, modul bude univerzálnější.
- Místo integeru jsme použili %d, místo stringu ‚%s‘ a pokud potřebujeme vložit procento, použijeme %%. Konkrétní parametry patří do druhého argumentu funkce. Drupal automaticky zajistí ochranu proti SQL Injection.
- Můžete použít i %f pro float a ‚%b‘ pro blob.
Soutěž!
Uvedený dotaz:$query = db_query("SELECT * FROM {node} n WHERE n.nid = %d OR n.type = '%s' OR n.title LIKE '%%%s%%'", $nid, $string, $string);
je správně, přesto však obsahuje jednu bezpečnostní chybu, úplně nesouvisející s SQL Injection. Najdete ji? Pokud ano, napište nám kontaktním formulářem (ne do komentářů!). Z došlých odpovědí vylosujeme dva výherce, kteří získají tričko „Drupal 6 cheat shirt“.
Kdo neví, měl by se přihlásit k odebírání komentářů k tomuto článku, aby se dozvěděl, jak může bezpečnost své aplikace ještě vylepšit.
Pravidlo č. 3 – Vypisování textu
Kromě zapisování dat či dotazů do databáze téměř vždy potřebujeme data ještě vypsat. To také není jednoduché, můžeme lehce docílit Cross Site Scripting (XSS) chyby.
K načtení: http://api.drupal.org/…eck_markup/6 http://api.drupal.org/…heck_plain/6
Takhle ne:
while($node = db_fetch_object($query)) {
print $node->title;
print $node->body;
}
drupal_set_title($row->title);
Takhle ano:
foreach($node = db_fetch_object($query)) {
print check_plain($node->title);
print check_markup($node->body, $node->format);
}
drupal_set_title(check_plain($row->title));
Co jsme zjistili:
- check_plain() zajistí text bez HTML tagů (escapuje je), použijeme vždy když vypisujeme text od uživatele. Vždy.
- check_markup() není častý, ale hodí se pro vypisování políček, kde může být HTML kód. Poté ho vypíše a udělá filtr na základě druhého parametru.
- Ač to většina lidí neví, drupal_set_title není bezpečné a VY musíte zajistit escapování textu, který funkci předáváte. Možná se to v Drupalu 7 změní, ale to nikdo zatím neví.
Bezpečný text i s překlady
Často je třeba vypsat uživateli nějakou hlášku, společně s jeho jménem či titulkem uzlu. To vždy musíme dělat přes funkci t() a psát v angličtině, Drupal zajistí překlad.K načtení: http://api.drupal.org/…function/t/6
Takto je to správně:
print t('Your node: @title will be published on: !day', array('@title' => $node->title, '!day' => date('l', $node->created)));
Všimněte si:
- Celý text jsme obalili funkcí t() pro překlad. Titulek uzlu nevkládáme přímo dovnitř – vznikaly by samostatné stringy pro každý titulek. Opět jsme použili nahrazovací vzorky.
- Rozdíl mezi vzorkem @title a !day je naprosto zásadní. První s @ zajistí, že titulek uzlu bude zabezpečen funkcí check_plain. Druhý s vykričníkem to neudělá, text vloží tak jak je. Který je bezpečný pro vstup uživatele?
Pravidlo č. 4 – Formuláře
Tvorba vlastních formulářů není jednoduchá, nicméně ukážeme si alespoň základ. Podívejte se na Drupal Forms API příručku: http://api.drupal.org/…rence.html/6 kde naleznete všechny potřebné informace. V zásadě platí: Vždy používejte pro formuláře Forms API, negenerujte je ručně. Nebudete tak vystaveni chybám jako Cross Site Request Forgery.
Jak poznat, že formuláře tvoříte správně? Jestliže do kódu ručně vkládáte input type=… či používáte $_POST, pravděpodobně to děláte špatně.

Hint pro soutěž: Chyba
Hint pro soutěž: Chyba souvisí s právy.
Přiznám se, že jsem velice
Přiznám se, že jsem velice zvědav, v čem ta bezpečnostní chyba vězí.
Po debate s Havranom uz asi
Po debate s Havranom uz asi viem co ma jakub na mysli, ale ak je to naozaj ono tak je to riadna blbost.
Asi mi nezbyva nic jineho,
Asi mi nezbyva nic jineho, nez odpovedet: WTF?
Pockam, kym prezradis
Pockam, kym prezradis riesenie, potom to okomentujem :) Ak si si nevsimol tak citujem seba: „ale ak je to naozaj ono tak“..
Pokračování?
Moc pěknej článek, bude pokračování?
Moc pěkný článek.
Moc pěkné, více takových článků bych uvítal, třebas by mě to přimělo modifikovat si své stránky. Api ma drupal vymazlené.
kdy bude zverejnena spravna
kdy bude zverejnena spravna odpoved? :-P
taky se přimlouvám za
taky se přimlouvám za vysvětlení té hádanky :-)
Vysledky a spravna odpoved:
Vysledky a spravna odpoved: http://www.drupal.cz/…programovani
Poslat nový komentář