ugrás a tartalomhoz

Validációs tervezési hibák

inf · 2014. Május. 14. (Sze), 02.03
Szép napot!

Kíváncsi vagyok, hogy mit gondoltok a php validációról. Jó, ténylegesen nincs ilyesmi, de én nagyon furcsának találtam az alapvető hozzáállást a validáláshoz az összes kódban, amit eddig láttam. Gyakorlatilag már a kiindulásnál elbuknak, mert eleve rossz stratégiát választanak.

Alapból valahogy így szokás nekifutni php-ben a validálásnak:

// lib/form/ContactForm.class.php
class ContactForm extends sfForm
{
  protected static $subjects = array('Subject A', 'Subject B', 'Subject C');
 
  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'subject' => new sfWidgetFormSelect(array('choices' => self::$subjects)),
      'message' => new sfWidgetFormTextarea(),
    ));
    $this->widgetSchema->setNameFormat('contact[%s]');
 
    $this->setValidators(array(
      'name'    => new sfValidatorString(array('required' => false)),
      'email'   => new sfValidatorEmail(),
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      'message' => new sfValidatorString(array('min_length' => 4)),
    ));
  }
}
Symfony dokumentációból másolt kód - Form-Validation

Ez még a szerencsésebb eset. A szerencsétlenebb az, amikor külön controller-ben van a validáció és külön view-ban az űrlap létrehozása, és lapozgatni kell egyikről a másikra, hogy lássuk mi micsoda.

Miért rossz ez (legalábbis szerintem)?

Ha security-ről van szó általában mindenki a whitelist-es megoldásokat biztonságosabbnak tartja, mint a blacklist-eseket, mert könnyű kifelejteni, embereket, akiknek tiltani akarjuk az alkalmazás használatát. Ugyanígy input esetében ha nem kötelező megadni validációt minden egyes input-ra, akkor könnyű kifelejteni azt, és az alkalmazás ugyanúgy működni fog nélküle...

Szóval szerintem alapvetően téves az az elképzelés, hogy az input-okat blacklist-es megközelítéssel kellene kezelni. Helyette minden egyes input-ot regisztrálni kell a controller-ben, mint engedélyezettet, és ha a listán nem szereplő input is bejön, akkor a rendszer automatikusan dobhat egy BadRequestException-t. (Nem tudom létezik e olyan keretrendszer, ami ezt a megközelítést vallja, eddig nem találkoztam vele.)

Ti mit gondoltok erről?
 
1

Model

firith · 2014. Május. 14. (Sze), 08.19
Szerintem a validációt a modelben érdemes eltárolni és a kontrollerből meghívni. Az új symfonyban ez így is van validation pl annotációként
2

Jó kérdés

Pepita · 2014. Május. 14. (Sze), 08.25
Olyanról nem tudok, hogy egy fw tudná:
ha a listán nem szereplő input is bejön, akkor a rendszer automatikusan dobhat egy BadRequestException-t

De azt viszonylag könnyen ellenőrizheted, hogy hány darab adat jött. A többi már a további validálás dolga.
Egyébként nem vagyok híve a 4xx dobálásnak, mert pl. előfordulhat, hogy megjelenik itt egy cikk, belinkelik valahol, majd törlik a cikket. Ekkor barátságosabb a cikk helyére odaírni, hogy "nincs megjeleníthető tartalom" (esetleg plusz magyarázattal), mint dobni egy 404-et.

A legtöbb esetben a whitelist-es ellenőrzés a megfelelő, egy-egy nagyon egyszerű esetben alkalmazható a blacklist-es is (pl. ha az adat nem integer), de nem követendő példa, épp azért, mert az összes nemet sokkal nehezebb felsorolni (és ahogy mondod, kihagyni könnyű belőle), mint az összes igent.
3

Whitelist

sandornemeth · 2014. Május. 14. (Sze), 09.44
Én ugyan Java-zok, de alapvetően a validáció (gyakorlatilag bármilyen rendszerben) szerintem mindig is whitelist-es volt, pont emiatt a megközelítés miatt (azaz azt mondom meg, hogy a validálandó inputnak mik a helyes esetei, minden más pedig hiba). Ugyanúgy, ahogy a példádban szereplő validáció is whitelistes (hiszen megmondja, hogy milyen formátumban várja az adatokat, illetve azoknak a rendszer szempontjából milyen követelményeknek kell megfelelniük):

// lib/form/ContactForm.class.php
class ContactForm extends sfForm
{
  protected static $subjects = array('Subject A', 'Subject B', 'Subject C');
 
  public function configure()
  {
    $this->setWidgets(array(
      'name'    => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'subject' => new sfWidgetFormSelect(array('choices' => self::$subjects)),
      'message' => new sfWidgetFormTextarea(),
    ));
    $this->widgetSchema->setNameFormat('contact[%s]');
 
    $this->setValidators(array(
      // a név mező nem kötelező, és nem érdekel a formátuma
      'name'    => new sfValidatorString(array('required' => false)),
      // az email mezőben email-t várok
      'email'   => new sfValidatorEmail(),
      // a subject mező tartalma az alábbi értékek közül kell valamelyik legyen
      'subject' => new sfValidatorChoice(array('choices' => array_keys(self::$subjects))),
      // a message mezőnek legalább 4 karakter hosszúnak kell lennie
      'message' => new sfValidatorString(array('min_length' => 4)),
    ));
  }
}
Az összes többi lehetőséget kizárja, szerintem ennél whitelistebb megoldás nem nagyon létezik.

Ugyanakkor azokat az adatokat, amiket nem vár az alkalmazásod, azokat pedig eldobja (nem csinál velük semmit, igy elvesznek). Ellenőrizheted a bejövő adatokat valahol a feldolgozási folyamat elején, de alapvetően nagyobb (és nem szükséges) terhelésnek teszed ki a szervert, a biztonsági előnyök nélkül (hiszen a nem várt adattal úgy sem foglalkozol).
5

Egyáltalán nem az, mert

inf · 2014. Május. 14. (Sze), 13.58
Egyáltalán nem az, mert regisztrálhatsz inputot úgy rajta, hogy nem adsz meg hozzá validátort.

Ha meg direktbe $_GET-et használsz valahol, akkor lehet, hogy egyáltalán fel sem tűnik, hogy nincs levalidálva. Szóval lehet még fokozni...

Valami ilyesmire gondoltam js-el kifejezve. (valamiért most nem volt kedvem php-t írni)

	inputs.allow({
		query: {
			"source": "queryString", //cookie, postData, header, etc...
			"name": "q",
			"type": input.types.searchQuery
		}
	});
	searchItem(inputs.query)
Mikor száll el hibával:
- ha egy input nem szerepel az allow listában, de mégis megadják
- ha egy input-ra nincs típus meghatározva
- ha invalid a bejövő input

Az allow részben lévő beállításokat hozzá lehetne csapni a controller action-ökhöz annotáció formájában.
Az űrlapot lehetne úgy generálni, hogy ezeket az annotációkat is felhasználja. Így már a view példányosításánál elszállna, ha valamelyik input mező nincs engedélyezve az action annotációjában.

Szerintem ez lenne kielégítő biztonsági szempontból. Ami problémás, hogy nem egyszerű ilyen annotációkat tervezni, programkóddal sokkal könnyebb bekonfigolni valamit, mint annotációval vagy config fájlal...
4

Symfony

firith · 2014. Május. 14. (Sze), 10.04
Helyette minden egyes input-ot regisztrálni kell a controller-ben, mint engedélyezettet, és ha a listán nem szereplő input is bejön, akkor a rendszer automatikusan dobhat egy BadRequestException-t. (Nem tudom létezik e olyan keretrendszer, ami ezt a megközelítést vallja, eddig nem találkoztam vele.)


Pont az általad felhozott symfony 1.x framework tudott ilyet. A validátornak be lehetett állítani két opciót:
allow_extra_fields: Ha olyan input érkezik amihez nem tartozik validátor akkor hibát dob (hogy pontosan milyet arra már nem emlékszem sajnos)
filter_extra_fields: Kiszűri a formon nem megtalálható mezőket a postból és törli őket
6

Köszi, ezek hasznos dolgok,

inf · 2014. Május. 14. (Sze), 13.59
Köszi, ezek hasznos dolgok, ha bekapcsolja őket az ember.