Ako Yii framework rieši Mass Assignment

PHPYiiBezpečnosť

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.

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