ID-uri numerice curate in PHP

Adesea m-am lovit de problema sanitizarii variabilelor care provin din input, si nu zic asta facand referire la PHP, ci la programare in general. Din moment ce am avut boala sa studiez catusi de cat bazele securitatii, mi-a ramas in cap un principiu foarte bine formulat: “all user input is evil!”. Adevarat grait. Deci am inceput sa gandesc aplicatiile dupa metoda user proof. In primul rand cand testez ceva nou, nu testez dupa cum ar trebui sa fie utilizata aplicatia in mod corect, ci cum NU ar trebui sa fie folosita, si ce sa se intample in acele cazuri. In primul rand pentru ca o aplicatie nu este o chestie democratica unde userul face ce vrea el si adesea face prost. Deasemenea intervine si cealata extrema. Unde nu este prostie – este viclenie – deci se poate trezi cate un h4x0r de asta cu mai mult timp liber la dispozitie sa te caute la oo (a se citi: securizare).

Problema ID-urilor numerice in PHP desi este una destul de banala, rezolvarea este multipla. De-alungul timpului am testat mai multe chestii, dar doar recent am ajuns la o forma finala care sa ma multumeasca deplin. De ce ID-uri numerice? Pentru ca este foarte usor de lucrat cu ele cand se interactioneaza cu o baza de date. Orice tabela care trebuie sa stocheze in mod unic niste chestii, are o cheie primara. Pana acum cea mai desteapta chestie in materie de chei primare este cheia intreaga care se seteaza cu auto-increment – ceea ce asigura complet unicitatea datelor. Recomandarea ar fi ca auto incrementul sa inceapa de la 0 (chestia asta o sa se lege de ce o sa zic mai jos). Bonus ar fi faptul ca fiecare tabela de acest gen ar trebui sa aiba cheia primara indexata pentru a reduce considerabil timpul de cautare al ei.

Adesea aceste chei primare din baza de date sunt trimise prin intermediul requesturilor de tip GET, deci se vor regasi in componenta URL-ului. Problema se pune atunci cand pe langa acel ID numeric, utilizatorul (in mod intentionat) introduce si altceva pe langa acel ID. Treaba poate sa se imputa de la XSS (Cross Site Scripting) – atunci cand acea cheie se regaseste in pagina, sau la atacuri de tip SQL injection (mai grav).

Desi exista functii native in suportul de MySQL pentru PHP, acestea sunt destul de triste la ID-uri numerice, deci am preferat propria metoda:

function clean_id($id)
{
$id=preg_replace("@[^0-9]@", "", $id);
if (empty($id))
	$id=0;
return $id;
}

Chestia asta de mai sus face vreo doua chestii … prima este aceea ca da strip la tot inputul care nu e numeric. A doua verifica ce a mai ramas, iar daca are valoarea 0 (orice 0 din PHP), atunci returneaza un 0 numeric. Este important acest zero numeric … ziceam ceva mai sus de el … acest zero numeric are grija ca interogarea sa nu dea de o chestie gen <<WHERE id=”>> si sa bubuie o eroare de SQL (care in anumite cazuri … ar putea la randul ei sa divulge alte chestii).

Actualizare:

Datorita observatiei lui AgLiAn am ajuns la concluzia ca functia mea initiala nu rezolva o problema: interger overflow, chestie ce mai departe se traduce in interpretarea ca float de catre PHP – si de unde pot aparea alte probleme. Si pentru ca am mentionat pe undeva mai sus de auto-increment (si acest obicei nu o sa se transforme peste noapte pana la proba contrara ce demonstreaza un obicei prost), functia clean_id(); devine:

function clean_id($id)
{
return abs(intval($id));
}

2 thoughts on “ID-uri numerice curate in PHP

  1. SaltwaterC Post author

    Ba era … dar intval(‘-5’)=-5, nu 5 … Sa zicem ca nu apar probleme de sintaxa SQL in cazul in care in interogare ajunge un ID negativ in cazul in care variabila nu e pusa intre ghilimele, dar tot nu ma impac cu ideea: ar fi trebuit sa ajunga un numar ‘natural’ acolo, nu ‘intreg’ (asta conform matematicii … un unsigned int ar fi echivalentul).

    Totusi observatia este buna … Un integer overflow ce iese din clean_id(); este interpretat ca float de catre PHP, iar preg_replace(); interpreteaza input-ul ca string, deci functia ar da mai bine putin modificata. O sa vezi forma in articol. Multumesc pentru observatie.

Leave a Reply

Your email address will not be published. Required fields are marked *