BF Summer sales
Pouze tento týden sleva až 80 % na HTML & CSS a JavaScript
80 % bodů zdarma na online výuku díky naší Letní akci!

Obrana proti útoku Mass assignment v PHP

V předchozí lekci, Útok Clickjacking a jak se před ním bránit, jsme se seznámili s útokem Clickjacking a také jsme si uvedli způsoby, jak před tímto útokem svou aplikaci chránit.

Mass assignment je útok, který se z angličtiny překládá jako hromadné přiřazení. Představme si následující situaci:

Máme na webové stránce registrované uživatele a chceme jim poskytnout možnost změny údajů. V databázi máme tabulku s uživateli:

Tabulka s uživateli v databázi

S tímto jedním testovacím uživatelem:

Uživatelé v PHP

Vytvoříme jednoduchý formulář (proměnné se vytáhnou z databáze).

<h2>Editace údajů</h2>
<form method="post">
    <table>
        <tr><th>Jméno</th></tr>
        <tr><td><input type="text" name="firstname" value="<?= htmlspecialchars($firstname) ?>" /></td></tr>

        <tr><th>Příjmení</th></tr>
        <tr><td><input type="text" name="lastname" value="<?= htmlspecialchars($lastname) ?>" /></td></tr>

        <tr><th>Přezdívka</th></tr>
        <tr><td><input type="text" name="nick" value="<?= htmlspecialchars($nick) ?>" /></td></tr>

        <tr><td><input type="submit" value="Změnit údaje" /></td></tr>
    </table>
</form>

Vypadá asi takto:

Editační formulář v PHP

Předpokládejme, že pracujeme s nějakým databázovým wrapperem (ono to bez něj v dnešní době již ani moc nejde), který obsahuje metodu pro update:

class Database extends PDO
{
    // hromada kódu

    /**
     * @param string
     * @param array
     * @param string|NULL
     * @return PDOStatement
     */
    public function update($table, $data, $where = NULL)
    {
        $sql = "UPDATE {$table} SET ";

        $counter = 0;
        $dataCount = count($data);
        foreach ($data as $column => $value) {
            $value = $this->quote($value);
            $sql .= "{$column} = {$value}";

            if ($counter < $dataCount - 1) {
                $sql .= ", ";
            }

            $counter++;
        }

        if ($where !== NULL) {
            $sql .= " WHERE {$where}";
        }

        return $this->query($sql);
    }
}

Pokud například zavoláme

$db = new Database("přihlašovací údaje");
$db->update("user", array(
    "firstname" => "jméno",
    "lastname" => "příjmení",
    "nick" => "přezdívka"
), "id = 1");
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

, vygeneruje nám to tento SQL dotaz:

UPDATE user SET firstname = 'jméno', lastname = 'příjmení', nick = 'přezdívka' WHERE id = 1

Nyní, protože jsme velmi líní, zpracujeme formulář tímto způsobem:

<?php

$db = new Database("přihlašovací údaje");
$userId = (int) $_SESSION["userId"];

$errors = array();
if ($_POST) {
    if (empty($_POST["firstname"])) {
        $errors[] = "Nebylo vyplněno jméno.";
    }
    if (empty($_POST["lastname"])) {
        $errors[] = "Nebylo vyplněno příjmení.";
    }
    if (empty($_POST["nick"])) {
        $errors[] = "Nebyla vyplněna přezdívka.";
    }

    if (empty($errors)) {
        $db->update("user", $_POST, "id = {$userId}");

        // Uložení zprávy o editaci, přesměrování
    }
}

Upravíme údaje v textových polích a formulář odešleme.

Odeslání editačního formuláře v PHP

Metoda Database::update() sestaví SQL dotaz následovně:

UPDATE user SET firstname = 'Jméno', lastname = 'Příjmení', nick = 'nick' WHERE id = 1

Takové volání metody je velmi snadné, stačí vyplnit 2-3 krátké parametry. Jenže zápis druhého parametru je velmi nebezpečný. Proměnná $_POST pochází od uživatele a můžeme jí data podstrčit a to hned několika způsoby.

Vraťme se zpět na editační formulář. Ukážeme si jeden primitivní způsob, jak data podstrčit, a to přes úpravu HTML formuláře. Podstrčený input vytvoříme jako textové pole, aby byl pro ukázku vidět:

Editace HTML formuláře

Pokud následně formulář odešleme, metoda nám najednou vygeneruje takový SQL dotaz:

UPDATE user SET firstname = 'Martin', lastname = 'Konečný', nick = 'pavelco1998', admin = '1' WHERE id = 1

Když se následně podíváme do tabulky 'user', uvidíme upravený údaj ve sloupci 'admin'.

Mass assignment v PHP

Tento příklad útoku by šel provést i tak, že jednoduše přepíšeme atribut některého z textových polí (např. z name="firstname" na name="admin") a změnili hodnotu na 1.

Jak útoku předcházet?

Možností je hned několik.

1. Vypsání hodnot ručně

// zpracování formuláře

$data = array(
    "firstname" => $_POST["firstname"],
    "lastname" => $_POST["lastname"],
    "nick" => $_POST["nick"]
);
$db->update("user", $data, "id = {$userId}");

2. Vytvořit si seznam povolených hodnot

$allowed = array("firstname", "lastname", "nick");

Díky tomuto poli lze i zkrátit naši kontrolu vyplněných hodnot:

$errors = array();
if ($_POST) {
    foreach ($allowed as $postKey) {
        if (empty($_POST[$postKey])) {
            $errors[] = "Nebyla vyplněna hodnota pole '{$postKey}'";
        }
    }
}

Pro samotný update využijeme dvou funkcí:

  • array_flip() - přehodí v poli klíče a hodnoty
  • array_intersec­t_key() - vybere pouze ty hodnoty, jejichž klíče jsou stejné v obou polích
$data = array_intersect_key($_POST, array_flip($allowed));
$db->update("user", $data, "id = {$userId}");

3. Využít formulářovou knihovnu, která automaticky vrací jen správné hodnoty

Pro ukázku využijeme Davidovu třídu Form:

$form = new Form("userUpdate");
$form->addTextBox("firstname", "Jméno", TRUE);
$form->addTextBox("lastname", "Příjmení", TRUE);
$form->addTextBox("nick", "Přezdívka", TRUE);
$form->addButton("update", "Změnit údaje");

// hodnoty se opět vyberou z databáze
$form->setData(array(
    "firstname" => $firstname,
    "lastname" => $lastname,
    "nick" => $nick
));

if ($form->isPostBack())
{
    try {
        $db->update("user", $form->getData(), "id = {$userId}");
    } catch (UserException $e) {
        echo "<span style='color: red;'>" . nl2br($e->getMessage()) . "</span>";
    }
}

Nyní se nemůže stát, že by se v tabulce upravily hodnoty jiných sloupců.

Závěrem

Nikdy, ale opravdu nikdy nevěřte uživateli, který navštíví vaše stránky. Vždy dbejte na ochranu vaší aplikace, protože nikdy nevíte, jaký problém z toho může vzniknout. HTML si každý může ve svém prohlížeči upravit, proto by pro tento příklad nefungovala ani kontrola v JavaScriptu - jednoduše by si ho mohl uživatel ze stránky odstranit nebo vypnout.


 

Předchozí článek
Útok Clickjacking a jak se před ním bránit
Všechny články v sekci
Bezpečnost webových aplikací v PHP
Článek pro vás napsal Martin Konečný (pavelco1998)
Avatar
Jak se ti líbí článek?
12 hlasů
Autor se o IT moc nezajímá, raději by se věnoval speciálním jednotkám jako jsou SEALs nebo SAS. Když už to ale musí být něco z IT, tak tvorba web. aplikací v PHP. Také vyvýjí novou českou prohlížečovou RPG hru a provozuje osobní web http://www.mkonecny.cz
Aktivity (2)

 

 

Komentáře

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:11.3.2014 17:37

Díky za první článek o bezpečnosti v PHP. Ten PDO wrapper je nějaký divný, protože nepoužívá prepared statements a escapuje si to sám, místo aby to nechal na databázi. Kvalitě článku to samozřejmě neubírá, je to jen příklad.

Odpovědět
11.3.2014 17:37
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Odpovídá na David Čápka
Martin Konečný (pavelco1998):11.3.2014 18:43

Prepared statements by samozřejmě byly vhodnější, ale je to prakticky stejný příklad, jako když někdo používá toto:

$pdo->query("
  UPDATE `tabulka`
  SET `sloupec` = 'hodnota'
  WHERE `id` = " . $_GET["id"] . "
");

A že jsem takový kód neviděl jen zřídka - i přes to, že PDO parametrizované dotazy podporuje.

Odpovědět
11.3.2014 18:43
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
jpicha
Člen
Avatar
jpicha:14.3.2014 18:11

Techická sem začátečník s php a na vstupní hodnoty mam toho třeba.

$modul['title'] = htmlspecialchars(@$_POST['title'], ENT_QUOTES);
if(empty($modul['title'])){
    $message["error"][] = "Title modulu musíte zadat.";
}elseif(!onlyLetterNumberSpace($modul['title'])){
    $message["error"][] = "Title modulu obsahuje neplatné znaky.";
}elseif(mb_strlen($modul['title']) < "4"){
    $message["error"][] = "Title modulu musí obsahovat minimálně 4 znaky.";
    $arr['title'] = $modul['title'];
}elseif(mb_strlen($modul['title']) > "32"){
    $message["error"][] = "Title modulu musí obsahovat maximálně 32 znaků.";
    $arr['title'] = $modul['title'];
}else{
    $arr['title'] = $modul['title'];
}

Když mam povolený jenom určitý znaky, který se můžou poslat jinak to vyhodí hlášku a neuloží se tak mě todle nehrozí nebo tam mam ještě něco špatně.
Děkuji za info

Editováno 14.3.2014 18:12
 
Odpovědět
14.3.2014 18:11
Avatar
jpicha
Člen
Avatar
Odpovídá na jpicha
jpicha:14.3.2014 18:29

Ještě funkce onlyLetterNum­berSpace

function onlyLetterNumberSpace($text){
    $text = preg_match("/^[0-9a-zA-Z^áčďéěíňóřšťůúýž^ÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ^\s]+$/", $text);
    return $text;
}
 
Odpovědět
14.3.2014 18:29
Avatar
Odpovídá na jpicha
Martin Konečný (pavelco1998):14.3.2014 18:43

Jelikož jsi začátečník, tak pochybuji, že máš nějaký databázový wrapper, ve kterém bys funkci předal pole a ona sestavila SQL dotaz.
Když tak pošli tu část, kde pracuješ s databází, ať ti mohu odpovědět přesně, ale myslím, že ti tento druh útoku nehrozí.

Odpovědět
14.3.2014 18:43
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
jpicha
Člen
Avatar
jpicha:14.3.2014 18:55

Na to mam dibi, když me to projde těma validačníma podmínkama tak se mě to uloží to pole $arr přes tendle příkaz

dibi::query('INSERT INTO [:cms:neco]', $arr);

do mysql

 
Odpovědět
14.3.2014 18:55
Avatar
Odpovídá na jpicha
Martin Konečný (pavelco1998):14.3.2014 21:10

Tvůj kód vypadá v pořádku. Pokud si vytvoříš samostatné pole, do kterého ručně napíšeš klíče a hodnoty, pak je to OK. Pro tento útok bys to musel mít v takovémto tvaru:

dibi::query("INSERT INTO [:cms:neco]", $_POST);

A nebo že bys sloučil obě pole:

$arr["title"] = "hodnota";
$arr = array_merge($arr, $_POST);
dibi::query("INSERT INTO [:cms:neco]", $arr)
Odpovědět
14.3.2014 21:10
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
sweetboi
Člen
Avatar
sweetboi:17.3.2014 8:18

takze, kdo nepouziva DB wrapper je porad zacatecnik ? :-) Jak utocnik zjisti, ze v DB je sloupec "Admin" a nasledne mu podstrci hodnotu "1" ?

 
Odpovědět
17.3.2014 8:18
Avatar
Odpovídá na sweetboi
Martin Konečný (pavelco1998):17.3.2014 8:54

O začátečníkovi nebyla v článku zmínka.

Útočník to nezjistí, pokud mu to sám neřekneš. :-) Ale na tohle se v žádném případě nespoléhej, protože i kdyby se to stalo čistou náhodou, pořád by si útočník změnil práva na admina.
Jak lze podstrčit tu jedničku, je napsáno ve článku.

Odpovědět
17.3.2014 8:54
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
sweetboi
Člen
Avatar
sweetboi:17.3.2014 9:07

Prominte, ale to co tu popisujete je hloupost jak mraky :-)

  1. jen ignorant vytvori hidden pole Admin
  2. jen ignorant bude skladat sql dotaz z prichozich POSTu
  3. jen ignorant nebude chranit vstupy proti SQL injection

pokud toto ignorant nebude delat, nema utocnik sanci updatovat jakekoliv jine pole, nez ty ktere poslete do SQL prikazu.

Editováno 17.3.2014 9:09
 
Odpovědět
17.3.2014 9:07
Avatar
Odpovídá na sweetboi
Martin Konečný (pavelco1998):17.3.2014 9:11

Ale přesně o tom ten článek je. Viděl jsem spoustu DB dotazů, které byly náchylné na SQL inject. Určitě může být někdo, kdo to dělá tímhle způsobem. Cílem bylo ukázat, že takový útok existuje, jak ho lze provést a jak se mu bránit. Není nikde řečeno, že se taková chyba dělá běžně.

Odpovědět
17.3.2014 9:11
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na sweetboi
David Čápka:17.3.2014 10:14

Nojo, arogance a hloupost. Vůbec netušíš, které bije. Hidden pole do formuláře přidá útočník, ne autor stránky. Mass assignment útok byl proveden proti známým webům v ROR. Že je v db sloupec admin si můžeš být jistý na 90%, stejně jako že je v ní tabulka users a podobně. Jsou to nejčastější pojmenování.

Editováno 17.3.2014 10:14
Odpovědět
17.3.2014 10:14
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
sweetboi
Člen
Avatar
sweetboi:17.3.2014 10:27

no jestli je to tak, tak ty "zname" weby si to zaslouzily a kazdy, kdo pojmenovava inputy stejne jako pole v DB a ten, kdo sklada SQL z prichozich POSTu a i ten, ktery si IDcka posila v hiddenu :-) Btw. ja sloupec admin nepouzivam :-)

 
Odpovědět
17.3.2014 10:27
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na sweetboi
David Čápka:17.3.2014 10:43

Jak je vidět, tak nemáš žádné zkušenosti s vývojem webových aplikací a v životě jsi nedělal s žádným frameworkem a nikdy jsi neviděl reálný projekt. Opravit ty nesmysly co jsi napsal je asi nad mé síly. Běž si prosím prostudovat alespoň základy PHP.

Odpovědět
17.3.2014 10:43
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
michalkasparec
Redaktor
Avatar
michalkasparec:17.3.2014 10:58

Nejvíc se mi líbí lidi, co očividně snědli rozum světa a bez jakéhokoli důkazu nebo ukázky toho co udělali, tak jen pomlouvají začínající programátory a odrazují je od učení...

 
Odpovědět
17.3.2014 10:58
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na michalkasparec
David Čápka:17.3.2014 11:04

Michale, pokud je to narážka na mě, tak si nejdříve přečti co sem ten člověk psal a jakým způsobem to psal. Raději odradím jednoho začátečníka (a ještě arogantního) než abych tu nechal diskuzi, ze které by si mohlo plno začátečníků vyvodit nějaké nepravdivé závěry.

Odpovědět
17.3.2014 11:04
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
michalkasparec
Redaktor
Avatar
Odpovídá na David Čápka
michalkasparec:18.3.2014 7:39

Promiň zapomněl jsem dát reakci. To určitě není na tebe, ale na toho (s prominutím) vola, co psal před tebou. Celá ta reakce byla na toho sweetboi.
Omlouvám se za nepřesnost. Proti tobě bych neřekl jediné křivé slovo. Tebe se na to moc vážím.

 
Odpovědět
18.3.2014 7:39
Avatar
asanos
Člen
Avatar
asanos:29.3.2014 21:17

1. Máš v plánu pokračovat dalšími články v téhle sekci?
2. Nezdá se ti to jako "návod" pro útočníka?
3. Nechtěl by jsi napsat zmínku o OWASP Top Ten a dál pokračovat například podle něj, nebo je to velké sousto?

  • Vím, že například o XSS by se dalo mluvit hodiny a hodiny. A jeden článek by teda vše podstatné asi nevystihl.
  • Máš teda už nějaké představy, kam to spěje?

Předem děkuji za odpověďi.

Odpovědět
29.3.2014 21:17
Na světě je 10 typů lidí. Ti, kteří rozumí binárce a ti co nerozumí.
Avatar
Odpovídá na asanos
Michal Žůrek - misaz:29.3.2014 21:20

návod pro útočníka = návod pro obránce. Aby ses dokázal ubránit musíš vědět jak se útočí, stejně tak pokud chceš točit musíš vědět jak se brání.

 
Odpovědět
29.3.2014 21:20
Avatar
asanos
Člen
Avatar
Odpovídá na Michal Žůrek - misaz
asanos:29.3.2014 21:25

Jj, tohle si také myslím. Sám jsem si říkal, že bych něco napsal. Ale zase je tady problém, že se najde určitě někdo, kdo toho zneužije.

Odpovědět
29.3.2014 21:25
Na světě je 10 typů lidí. Ti, kteří rozumí binárce a ti co nerozumí.
Avatar
Odpovídá na asanos
Michal Žůrek - misaz:29.3.2014 21:56

to se najde vždycky, nemusíš se bát.

 
Odpovědět
29.3.2014 21:56
Avatar
Odpovídá na asanos
Martin Konečný (pavelco1998):30.3.2014 14:32
  1. Ano, mám v plánu ještě nějaké články připsat, jen to z určitých důvodů nemohu udělat teď.
  2. Zneužít se toho dá - a právě kvůli tomu tato sekce vůbec nemusela vzniknout. Podle mého názoru je ale lepší ten útok ukázat, aby web vývojáři věděli, co je může postihnout a jak tomu mají předcházet.
  3. O OWASP jsem bohužel neslyšel.
Odpovědět
30.3.2014 14:32
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
kuba_kubikula:9.5.2014 0:47

Ahoj,
trošku tápu.
Je aplikace ochráněna proti tomuto útoku když předané hodnoty filtruji
$data1 = filter_input(IN­PUT_POST,'data')
a používám předpřipravenou funkci
$dbh->prepare('SELECT * FROM fruit WHERE ID = ? AND colour = ?');
$dbh->execute(arra­y($data1,$data2);

Děkuji, Jakub

 
Odpovědět
9.5.2014 0:47
Avatar
Odpovídá na kuba_kubikula
Michal Žůrek - misaz:9.5.2014 6:24

mělo by to být OK.

 
Odpovědět
9.5.2014 6:24
Avatar
BulDozer Diwinorum:11.4.2015 18:48

Ahoj, ahoj.... ja by som sa spytal....

1. je nejaka moznost zistit nazov db alebo nazvy tabuliek alebo stlpcov ak sa tieto nazvy vyskytuju len v mysql dopytoch?

a

2. je v niecom lepsie pouzivat prikaz mysqli_ od mysql_ ?
3. ak uz pouzijem id ako cislo tak to predsa staci overit prikazom is_numeric....alebo sa mylim?

totiz to, moc mi nesadlo OOP a robim radsej proceduralne a priznavam, ze moje programovanie nie je nic extra :D ,kazdopadne v mojich projektoch potrebujem osetrit citlive data

vopred dakujem za rady

Editováno 11.4.2015 18:49
 
Odpovědět
11.4.2015 18:48
Avatar
Odpovídá na BulDozer Diwinorum
Martin Konečný (pavelco1998):11.4.2015 18:56

zdar,

  1. neznám jiný způsob, kterým by to šlo zjistit, než zobrazením názvu v chybovém hlášení (to by mělo být na ostrém serveru zakázáno).
  2. mysqli neznám, ale má umožňovat např. parametrizované dotazy. Spíš se podívej na PDO (tady je o tom hodně tutoriálů).
  3. is_numeric() moc nepoužívám - když už chci mít v dotazu číslo, tak to přetypuji, je to jistější. Pokud ale jako parametry posíláš uživatelské vstupy ručně, pak je to OK - tento článek mluví spíše o tom, že do DB dotazu vložíš rovnou celé pole (třeba $_POST), aniž by sis zkontroloval, jestli obsahuje pouze to, co očekáváš.
Odpovědět
11.4.2015 18:56
Aktuálně připravuji browser RPG, FB stránka - https://www.facebook.com/AlteiraCZ
Avatar
Odpovídá na Martin Konečný (pavelco1998)
BulDozer Diwinorum:11.4.2015 19:10

tak to by ma ani vo sme nenapadlo :D :D dakujem teda za ukludnenie mojej paranoje :D :D :D ... pozrem teda aj na to OOP, uz niektore kniznice v podstate aj vyuzivam.... hlavne html do pdf alebo posielanie mailov ... a velmi sa mi pacili aj clanky, co napisal David Čápka o vlastnom frameworku... skoda len, ze mi ich neukazalo vsetky do konca.... kazdopadne dik aj za to... tento web je paradny, prehladny a ako jeden z mala aj zrozumitelny

Editováno 11.4.2015 19:11
 
Odpovědět
11.4.2015 19:10
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 27 zpráv z 27.