ujovlado.dev

Ako Yii framework rieši Mass Assignment

03.04.2012

To, že niekto “hackol” Github ste už asi počuli. Nebol to ani tak problém serveru Github ale skôr frameworku Ruby on Rails. O probléme Mass Assignment sa potom písalo všelikde na internete, niečo spomenul David Grudl, niečo Michal Špaček a podobne.

Yii framework logo

Tento problém sa týka samozrejme viacerých jazykov (alebo skôr programátorov) a bezpečnostnú dieru vyrobíte veľmi ľahko. Je preto lepšie spraviť to zaužívaným spôsobom alebo použiť framework, ktorý to má vyriešené. Jedným z nich je Yii.

V podstate je celý problém s Mass Assignment spôsobený tým, že nezadefinujete tzv. “whitelist atribútov”, ktoré môže používateľ vašej stránky poslať POST requestom a zmeniť údaje v databáze alebo v modeli.

Deravý kód

<?php

// nejaký model obsahujúci atribúty, ktoré sú vlastne stĺpcami v databáze
$attributes = array(
    'id',
    'username',
    'isActive',
    'email',
    'about',
);

// dáta, ktoré pôjdu do databázy
$data = array();

// $_POST['User'] nech naplnené odoslaným formulárom
// a nech sa jedná o editáciu profilu používateľa

// naplnenie 
foreach ($attributes as $attribute) {
    // tu nastáva chyba
    if (isset($_POST['User'][$attribute]))
        $data[$attribute] = $_POST['User'][$attribute];
}

// zloženie SQL
$sql = "UPDATE `User` SET ";
foreach ($data as $key => $value) {
    $sql .= "`" . $key . "` = '" . mysql_real_escape_string($value) . "'";
}
$sql .= " WHERE `id` = " .  intval($_GET['id']) . " LIMIT 1;";

// uloženie
// ...

Takýto kód je zlý a umožňuje podstrčiť (a tým pádom zmeniť) aj také užívateľské údaje, ktoré sme meniť nechceli. V tomto prípade ID užívateľa a stav, či je aktívny alebo nie.

Ako to robí Yii

Yii definuje tzv. safe attributes. Tie si môžete vymenovať manuálne + je to urobené tak, že akonáhle existuje na nejaký atribút validátor, tak je safe.

<?php

class User extends CModel // alebo napr. CActiveRecord
{
    // validačné pravidlá
    public function rules()
    {
        return array(
            array('username, email', 'required'),
            array('email', 'email'),
            array('username', 'length', 'min' => 3, 'max' => 12),
            array('about', 'safe'),
        );
    }
}

V controlleri sa potom v akcii volá:

A čo sa stane keď postrčím $_POST['User']['isActive']? Nič. Pretože isActive nie je safe attribute. Ak by ste chceli nastaviť i tie atribúty, čo nie sú safe, musíte to spraviť manuálne cez funkciu setAttributes s druhým parametrom FALSE.

<?php
// hard-core nastavenie všetkých atribútov - NEROBIŤ!

// public void setAttributes(array $values, boolean $safeOnly=true)
$model->setAttributes($_POST['User'], false);

Príklad

Akcia v controlleri:

<?php

$user = new User;
$user->id = 1;
$user->email = 'test@ujovlado.sk';
$user->isActive = 1;
$user->username = 'ujovlado';
$user->about = 'Lorem ipsum dolor sit amet, consectetur ...';

// $post je náhrada za $_POST['User']
$post = array(
    'isActive' => 0,
    'id' => 45,
    'email' => 't@ujovlado.sk',
    'username' => 'test',
    'about' => 'Lorem ipsum ...'
);

// mass assignment
$user->attributes = $post;

var_dump($user->attributes);

Výstup:

array
  'id' => int 1
  'username' => string 'test' (length=4)
  'isActive' => int 1
  'email' => string 't@ujovlado.sk' (length=13)
  'about' => string 'Lorem ipsum ...' (length=15)

Povolené sme mali zmeniť username a email kvôli validátorom a about atribút kvôli manuálnemu nastaveniu, že je safe. A tak sa i stalo.

Záver

Mass Assignment patrí medzi časti, v ktorých programátori často robia chyby a jedinou spoľahlivou ochranoum proti neželanému zmeneniu údajov je ukladať len tie údaje, ktoré potrebujeme. Čiže white-listing alebo manuálne priraďovanie hodnôt z $_POST jednotlivým atribútom.

Poznámky