C, PHP, VB, .NET

Дневникът на Филип Петров


* Валидиране на типовете данни при SQL

Публикувано на 30 ноември 2008 в раздел ОСУП.

Дотук разгледахме най-често срещаният проблем, позволяващ вмъкване на нежелан SQL код - лошо филтриране на входните данни. Отбелязахме, че е по-добре да даваме множество от позволени стойности на входните данни, а не просто да забраняваме непозволените символи. За да обясним защо ще демонстрираме чрез пример, в който няма да използваме кавички при изпълнение на заявката. Нека например изпращаме следната заявка:

	$sql = "SELECT * FROM stats WHERE id=$id"

В случая полето с име "id" е от тип int и поради тази причина не е нужно ограждането на стойността в кавички. Ако подадем например числото "5" ще се генерира следната заявка към базата от данни:

	SELECT * FROM stats WHERE id=5;

Ако обаче подадем "23 OR 1=1" ще се получи:

	SELECT * FROM stats WHERE id=23 OR 1=1;

Тази заявка очевидно е винаги валидна и ще изкара всички данни от базата от данни. В нея не използвахме нито един непозволен символ. Възможностите за пробив са много, в зависимост от вида на заявката. При заявка от тип UPDATE или DELETE например може да се достигне до фатални за базата от данни резултати.

Как да се защитим? Отговорът е ясен - валидирайте не само подадените символи, но и самите типове данни:

	if(is_numeric($id){
		$sql = "SELECT * FROM stats WHERE id=$id"
		...
	}
	else{
		echo "Are you trying to do SQL injection?";
		exit();
	}

Възможно е и използване на вградените функции на сървъра за база от данни, но на практика е по-добре да валидирате в собственото приложение (а защо не и по двата начина?).

Един начин категорично да предотвратим SQL Injection е да използваме параметризирани заявки, т.е. такива, при които първо компилираме скрипта, а после вмъкваме параметрите. По този начин се гарантира, че няма как да бъде променена логиката на заявката (и да се вмъкне OR условие или подобна промяна на изходния програмен код). Друга подобна възможна практика е да използваме на съхранени процедури, вместо да подаваме на директни заявки. В крайна сметка ако трябва да градираме защитата от по-опасна към по-добра практика (от гледна точка на възможност за допускане на неволни грешки), класацията ще изглежда така:

  1. Използване на стандартни заявки, като се прави винаги escape на кавички и допълнително внимание към променливи, които не са текстови низове;
  2. Валидиране на всички параметри чрез регулярни изрази;
  3. Валидиране на всички параметри чрез регулярни изрази и допълнителен escape на всички кавички (за всеки случай, ако случайно допуснем грешка в регулярния израз);
  4. Използване на параметризирани заявки или съхранени процедури;
  5. Използване на параметризирани заявки или съхранени процедури, и валидиране на всички параметри чрез регулярни изрази (така няма да изпълняваме въобще заявки, които няма смисъл да се изпълняват).

Отново ще припомним и едно друго, вече споменато основно правило за базите от данни - давайте толкова привилегии на потребителя, колкото са му нужни! Грешен подход е да имаме един потребител за базата от данни, който се използва за всички видове заявки.

 



3 коментара


  1. Когато се очаква дадена променлива да е число, я минавам през (int). Примерно $id = (int)$_GET['id] и тя се превръща съответно в число. Това е малко по-бързо отколкото да се ползват готови функции от рода на intval() и is_nummeric. Ако пък се очаква да са някакви символи ги минавам през mysql_real_escape_string(). Евентуално, ако ще се добавя в базата данни някаква информация, е хубаво да се минава през функции за обезвреждане на html таговете от рода на strip_tags. Аз ползвам точно нея и ми изглежда сигурна.

  2. О, не - това, което правиш не е добре. Пробвай например "echo (int)"1234abcd";". Резултатът е "1234". Това няма да счупи SQL заявката, да. Но има един основен въпрос: "след като знаем, че дадена променлива е с невалидни данни, то защо въобще трябва да се изпълнява SQL заявка?". И тук спорът приключва в полза на "is_numeric" :)

Добави коментар

Адресът на електронната поща няма да се публикува


*