Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Obrana proti útoku Mass assignment v PHP

V predchádzajúcej lekcii, Úvod do bezpečnosti webových aplikácií , sme sa dozvedeli, čo si pod pojmom webová aplikácia predstaviť, tiež sme si predstavili princíp fungovania trojvrstvovej architektúry a tiež aplikačnú vrstvu. Poznáme tiež Web application firewall, nástroj ktorý chráni aplikačnú vrstvu aplikácie.

Mass assignment je útok, ktorý sa do angličtiny prekladá ako hromadné priradenie. Predstavme si nasledujúcu situáciu:

Máme na webovej stránke registrovaných užívateľov a chceme im poskytnúť možnosť zmeny údajov. V databáze máme tabuľku s používateľmi:

Tabuľka s uživatlei v databáze - Bezpečnosť webových aplikácií v PHP

S týmto jedným testovacím užívateľom:

Používatelia v PHP - Bezpečnosť webových aplikácií v PHP

Vytvoríme jednoduchý formulár (premenné sa vytiahnu z databázy).

<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>

Vyzerá asi takto:

Editačné formulár v PHP - Bezpečnosť webových aplikácií v PHP

Predpokladajme, že pracujeme s nejakým databázovým Wrapper (ono to bez neho v dnešnej dobe už ani veľmi nejde), ktorý obsahuje metódu pre 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);
    }
}

Ak naprí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");

, Vygeneruje nám to tento SQL dotaz:

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

Teraz, pretože sme veľmi leniví, spracujeme formulár týmto spôsobom:

<?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 poliach a formulár odošleme.

Odoslanie editačného formulára v PHP - Bezpečnosť webových aplikácií v PHP

Metóda Database :: update () zostaví SQL dotaz nasledovne:

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

Také volanie metódy je veľmi jednoduché, stačí vyplniť 2-3 krátke parametre. Lenže zápis druhého parametra je veľmi nebezpečný. Premenná $ _POST pochádza od užívateľa a môžeme jej dáta podstrčiť a to hneď niekoľkými spôsobmi.

Vráťme sa späť na editačné formulár. Ukážeme si jeden primitívny spôsob, ako dáta podstrčiť, a to cez úpravu HTML formulára. Podstrčené input vytvoríme ako textové pole, aby bol pre ukážku vidieť:

Editácia HTML formuláre - Bezpečnosť webových aplikácií v PHP

Ak následne formulár odošleme, metóda nám zrazu vygeneruje taký SQL dotaz:

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

Keď sa následne pozrieme do tabuľky 'user', uvidíme upravený údaj v stĺpci 'admin'.

Mass assignment v PHP - Bezpečnosť webových aplikácií v PHP

Tento príklad útoku by išiel vykonať aj tak, že jednoducho prepíšeme atribút niektorého z textových polí (napr. Z name = "firstname" na name = "admin") a zmenili hodnotu na 1.

Ako útoku predchádzať?

Možností je hneď niekoľko.

1. Vypísanie hodnôt ručne

// zpracování formuláře

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

2. Vytvoriť si zoznam povolených hodnôt

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

Vďaka tomuto poli možno aj skrátiť našu kontrolu vyplnených hodnôt:

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

Pre samotný update využijeme dvoch funkcií:

  • array_flip () - prehodí v poli kľúče a hodnoty
  • array_intersec­t_key () - vyberie iba tie hodnoty, ktorých kľúče sú rovnaké v oboch poliach
$data = array_intersect_key($_POST, array_flip($allowed));
$db->update("user", $data, "id = {$userId}");

3. Využiť formulárové knižnicu, ktorá automaticky vracia len správne hodnoty

Pre ukážku využijeme Dávidovmu triedu 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>";
    }
}

Teraz sa nemôže stať, že by sa v tabuľke upravili hodnoty iných stĺpcov.

Záverom

Nikdy, ale naozaj nikdy neverte užívateľovi, ktorý navštívi vaše stránky. Vždy dbajte na ochranu vašej aplikácie, pretože nikdy neviete, aký problém z toho môže vzniknúť. HTML si každý môže vo svojom prehliadači upraviť, preto by pre tento príklad nefungovala ani kontrola v JavaScripte - jednoducho by si ho mohol užívateľ zo stránky odstrániť alebo vypnúť.

V ďalšej lekcii, Ako sa brániť proti SQL injection , si uvedieme spôsoby, ako pred útokom SQL injection svoju aplikáciu chrániť.


 

Predchádzajúci článok
Úvod do bezpečnosti webových aplikácií
Všetky články v sekcii
Bezpečnosť webových aplikácií v PHP
Preskočiť článok
(neodporúčame)
Ako sa brániť proti SQL injection
Článok pre vás napísal Martin Konečný (pavelco1998)
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
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