|
|
|
| Да как оказалось не все так просто
даже в простом коде для загрузке файла есть дырка :)
Код позже кину суть в том что вся информация поступающая на сервер должна проверяться на некоторые слова и символы.(в данном выше примера даже имя загружаемого файла)
Кузнецов М.В. и Семдянов И.В. напишите что еще нужно обязательно делать чтобы код не был дырявым.
Ведь когда будет создан какой нибудь проект то возможно придется его весь пересматривать в поисках дырочек и лазеек и вероятность их пропустить помоему больше чем если заранее продумать вопрос безапасности более усмотрительней! | |
|
|
|
|
|
|
|
для: Zebra
(27.09.2004 в 21:39)
| | Это целое исскуство... Часто Web-приложения, такие как форум или фотогалерея, позволяют посетителям публиковать собственные изображения или файлы. Это могут быть фотографии, музыкальные, текстовые или бинарные файлы. Передача пользовательских файлов на сервер уже несёт в себе потенциальную опасность, и при невнимательном кодировании может привести к серьёзным последствиям.
Следующий код позволяет продырявить сайт по самое нехочу:
<form enctype='multipart/form-data' action=handler.php method=post>
Файл : <input type=file name=filename><br>
<input type=submit value=Отправить>
</form>
|
<?php
// Файл handler.php
// Проверяем содержимое параметра filename, передаваемого обработчику,
// из формы в файле index.php
if(isset($_POST['filename'])) $filename = trim($_POST['filename']);
else $filename = "";
// Если обработчику передан пустое поле filename или содержащее пробелы
// игнорируем обращение, в противном случае выводим ссылку на файл
if (!empty($filename))
{
// Если копирование произведено удачно, выводим ссылку на файл.
if (copy($filename, $_FILES['filename']['name']))
echo "<a href=".$_FILES['filename']['name'].">Посмотреть</a>";
else
echo "Ошибка при передаче файла на сервер.";
}
else
{
// если переменная $filename пуста, просим повторить загрузку файла
echo "Имя файла не введено, повторите, пожалуйста, операцию.<br>";
echo "<a href=# onClick='history.back()'>Вернуться к отправке</a>";
exit();
}
?>
|
На первый взгляд, код в листинге приведённый выше производит впечатление достаточно проработанного и предусматривающего все возможные варианты развития ситуации. Тем не менее, данное Web-приложение, позволяет полностью уничтожить сайт, на котором оно расположено. Отправка при помощи данной формы файла, например, приведённого в листинге ниже, приводит к рекурсивному удалению файлов сайта.
<?php
// Вызываем функцию удаляющую файлы c сервера с параметром "..", чтобы
// подняться на один уровень выше: это позволит уничтожить больше файлов,
// так как наверняка Web-приложение находится в отдельной директории.
delfiles("..");
// Функция удаляющая каталоги и файлы сервера
function delfiles($catalog)
{
$dir = opendir($catalog);
while(($file = readdir($dir)))
{
// Если текущий объект является файлом – уничтожаем его
if(is_file($catalog."/".$file)) unlink($$catalog."/".$file);
// Если текущий объект является каталогом – рекурсивно вызываем
// функцию delfiles(), заботливо избегая каталогов "." и "..",
// соответствующих текущему и вышележащему каталогам.
else if (is_dir($catalog."/".$file) &&
($file != ".") &&
($file != "..")) delfiles($catalog."/".$file);
}
// Закрываем директорию
closedir($dir);
// Удаляем директорию
rmdir($catalog);
}
?>
|
PS Листинги и курсивый текст из будующей книги /*Кто-то тут анонсов просил :)))*/
PPS Поэтому я всегда призываю изучать UNIX (Linux в частности), так как глубокое знание серверных операционных систем позволит писать более защищённый код.
PPPS Этот форум, по крайней мере этим способом, ломать не следует - здесь это не сработает...
PPPPS Я пишусь через букву И: Симдянов :))) | |
|
|
|
|
|
|
|
для: cheops
(27.09.2004 в 22:01)
| | А как быть с бинарными файлами, если например нужно загружать на сервер jpg и нужно удостовериться что это именно jpg? | |
|
|
|
|
|
|
|
для: Observer
(12.11.2005 в 00:58)
| | В самом простейшем случае можно проверить тип загруженного файла
<?php
if($_FILES['filename']['name'] != 'image/pjpeg') exit("Допустимы только графические файлы");
?>
|
В большинстве случаев достаточно проверить содержит ли первые 5 символов $_FILES['filename']['name'] подстроку "image".
<?php
if(substr($_FILES['filename']['name'],0,5) == "image")
{
// Загружено изображение
}
?>
|
| |
|
|
|
|
|
|
|
для: cheops
(12.11.2005 в 01:06)
| | Как можно в бинарном файле искать подстроку?
А что касается первого варианта, то меняем расширение любого файла на .jpg и он становится image/jpeg | |
|
|
|
|
|
|
|
для: Observer
(12.11.2005 в 11:18)
| | >Как можно в бинарном файле искать подстроку?
Можно при помощи регулярного выражения, только бинарную последовательность придётся записать в шестнадцетиричном формате. | |
|
|
|
|
|
|
|
для: Zebra
(27.09.2004 в 21:39)
| | >Кузнецов М.В. и Симдянов И.В. напишите что еще нужно
>обязательно делать чтобы код не был дырявым.
В новой книге этому вопросу посвящена отдельная глава, которая так и называется - Безопасность создаваемых приложений. Хотя, признаюсь, с точки зрения того, что хотелось бы сказать, не все в этой главе уместилось, потому что по этому вопросу можно писать отдельную книгу. Да и тема, как правильно заметил Игорь, очень соприкасается со смежными областями, в основном с безопасностью работы с СУБД и корректной настройке серверов. М.б., мы напишем такую книгу, которая целиком бы была посвящена вопросам безопасности создаваемых приложений. А в той, про которую речь мы постарались сказать самое основное, затронув, тем не менее, и сервера с базами данных. | |
|
|
|
|
|
|
|
для: Zebra
(27.09.2004 в 21:39)
| | Автор: php.spiker.ru
Дата добавления: 10-11-2004
Привет всем web-программерам :) Вот наконец у меня дошли руки до Блокнота :o), в котором я и пишу сейчас эту статью. Статья, имхо, чрезвычайно важной тематики, что ясно по названию - "Безопасность в PHP". Именно: я постараюсь описать наиболее распространенные приемы защиты php-скриптов от злых хакИров. Имейте в виду, эта статья - не панацея от взлома. Просто применение этих приёмов несколько усложнит задачу взломщика. Настоятельно рекомендую всем использовать свои методы защиты, написанные с учетом специфики именно вашего сайта. Если хакер не сможет догадаться, как все "закручено" в вашем скрипте, и какие там стоят "заглушки", то желание ломать у него, скорее всего, быстро пропадет. Что нам и надо :) Итак, disclaimer окончен, перейдем непосредственно к безопасности.
Не секрет, что 99% (а то и все 100) всех взломов веб-приложений происходят в результате того, что хакер передает скрипту не те данные, которые тот ожидает получить. (Простейший пример - гостевая книга, где можно писать HTML-тегами. При написании скрипта неопытный программер даже не подумал о том, что пользователь может передать скрипту что-то еще, кроме plain text-a). Поэтому, если ты думаешь, что кто-то может попробовать пощупать твои скрипты на безопасность, встрой в скрипт проверку на соответствие получаемой инфы твоему типу данных. За примером далеко ходить не надо: имеется гостевая книга, сообщения которой выводятся не на одной странице, а разбиты на несколько. При этом, чтобы прочитать, скажем, четвертую страницу, вызывается скрипт view.php?page=4. Где переменная $page принимает целые значения. Целые? Ок, так и не позволим хакеру дать ей, значение, например, "?>": $page=intval($page) вернет целую часть действительной переменной, 0 - для строковой переменной, и $page, если $page - целая переменная. Теперь в скрипте можно написать: if($page==0) echo("Похакай мне тут!"); и хакер обломается :)
Теперь еще один важный момент. (Если вы еще не забыли, я по-прежнему рассказываю о PHP без MySQL, т.е. в моих примерах мы будем работать с текстовыми файлами). Допустим, мы имеем скрипт-шаблонизатор main.php, который будет вызываться с параметром page=abc (main.php?page=news, к примеру), и при запуске будет пытаться прочитать содержимое файла "text/abc/text.txt", дабы вставить его в страницу и показать пользователю. Теперь представим, что каталога "text/abc" не существует (хакер опять-таки задал неверное значение), что вернет нам пхп? Правильно, ужасные, нарушающие весь дизайн сайта строки "Warning: fopen("text/abc/text.txt","r") - No such file or directory in /home/user/main.php on line 20, Warning: Supplied argument is not a valid File-Handle resource in /home/user/main.php on line 21). Конечно, никаких особо вредных последствий для сайта при этом не будет, но... А что, если в нашем файле скрипт не читает инфу из файла, а пишет туда? Может получиться нехорошо :) К примеру, если хакер вызовет скрипт так: main.php?page=../, скрипт может занести инфу в совсем уж непотребное место :) Итак, как избежать такого эффекта? Посмотрим. Во-первых, даже если хакеру и удалось спровоцировать ошибку, он не должен знать о своем успехе. Для этого заменим строку $textfile=fopen("text/$page/text.txt",r); на следующую: $textfile=@fopen("text/$page/text.txt",r). Как видите, добавилась собака (@). Для чего это нужно? Очень просто - символ @ блокирует вывод всех сообщений парсера об ошибках, связанных с той функцией, перед которой стоит собака. Так что, даже если ошибка и произошла, хакер об этом не узнает. А теперь - как предотвратить такую ошибку. Это очень просто - перед чтением файла следует сделать проверку на его существование. Примерно вот так:
<?
if(file_exists("text/$page/text.txt"))
$ok=1;
else
$ok=0;
if($ok!=1) {
echo("hacker? wow...");
exit;
}
?>
|
как видишь,все просто :)
Так, с заменой параметров прямо в строке запроса разобрались. Взломщик потерпел полное фиаско и решил поисследовать твои формочки. И тут мы его обломим :) Предположим, что хакер решил поправить некоторые параметры, к примеру, формы гостевой книги, и посмотреть, что получится. Для этого ему надо сохранить HTML-файл с формой к себе на винт и отредактировать в блокноте, после чего отправить инфу нам. Отловить такого плохого мальчика проще простого :) Делается это через переменную HTTP_REFERER. Вот простенький код:
<?php
$ref=getenv('HTTP_REFERER');
if(!ereg("^http://адрес.нашего.сайта",$ref)){
echo("error wrong referer!");
exit;
}
?>
|
Добавь этот код в начало своей гостевухи, и юные хакИры будут несколько озадачены. 90% за то, что на этом месте они перестанут ломать твой код. Для остальной же, более продвинутой публики стоит сделать еще несколько сюрпризов. Как, например, запретить употребление HTML-тегов (только представь, что можно натворить при помощи JavaScript...): я делаю это такой функцией:
function nohtml($string) {
$html = array (
"&" => "&",
""" => """,
"<" => "<",
">" => ">",
""" => """
);
for(reset($html); $key=key($html); next($html)) {
$string = str_replace("$key","$html[$key]",$string);
|
Эти приёмы усложняют жизнь начинающим хакерам и упрощают тебе - не придется, чертыхаясь, вычищать теги из гостевой / чата / форума.
И последний вопрос, который я хотел сегодня рассмотреть - авторизация. Я чуть со смеху не помер, увидев на одном сайте авторизацию средствами JavaScript. Да-да, не смейтесь! Выскакивала строка запроса, введенное значение прямо в исходнике HTML (!) сравнивалось с верным пассвордом и, в зависимости от результата проверки, происходил редирект броузера либо на error.htm, либо на secured.htm. Автор такой мега-авторизации надеялся, что хакер не успеет после ввода пароля просмотреть исходник HTML, но мы-то знаем, что ВСЮ подноготную client-side приложений можно найти в кеше... Так что единственно надежным (и то не всегда) механизмом авторизации мы будем считать авторизацию со стороны сервера. Как же сделать такую проверку в PHP? Очень просто: в форму вставить объект <input type=password name=password>, а в PHP - код такого вида:
<?php
if($password!="qwerty")
{
echo("Wrong password!");
exit;
}
?>
|
Этот способ имеет ряд недостатков. Во-первых, если ты админ, иногда неохота каждый раз вводить длинный пароль. "Подумаешь, есть же кукисы!" - скажешь ты. "Ввел один раз пароль - а дальше он пускает тебя автоматом - эх красота!" Верно, но предположим, что ты используешь прокси. Тогда существует вероятность, что кто-нибудь, сидящий через тот же прокси, запросит твой админовский скрипт, и прокси выдаст ему тот документ, который был сформирован после авторизации! Это нам совсем не нужно. Надо либо делать каждый раз logout, что ведет к тому же геморрою, что и с формами, либо ставить сервак на защищенное соединение (протокол HTTPS ;)). Еще один вариант защиты - положить все админовские скрипты в каталог admin и настроить доступ к нему при помощи .htaccess. Тогда хакеру придется ломать уже веб-сервер, что куда сложнее, чем может показаться. НО доступ к .htaccess есть не всегда. Что же делать? Есть способ проще - RONDO ;) в начало каждого админовского скрипта поместим такой вот код:
<?php
if ( ($PHP_AUTH_USER != "admin") ($PHP_AUTH_PW != "coolpassword") ) {
Header("WWW-Authenticate: Basic realm="For admins"");
Header("HTTP/1.0 401 Unauthorized");
echo("<HTML><HEAD><TITLE>В доступе отказано!</TITLE>
</HEAD><BODY><center><font color=red><b>access denied!
</b></font></center><br><br><br></BODY></HTML>");
exit;
}
?>
|
Итак. Если две переменные $PHP_AUTH_USER и $PHP_AUTH_PW не соответствуют некоторым значениям, формируем окошко с запросом на авторизацию (код 401). Если пользователь ввел правильные логин и пароль, то на время этой сессии он получит доступ ко всем скриптам админа, в которых используется такая авторизация - браузер просто "запомнит" эти значения логина и пароля, и пока ты не закроешь это окно, будет автоматом подставлять их во все запросы. Если ты случайно закрыл окошко, не закончив админить сайт, то окошко выскочит еще раз, что неудобно. Но в IE, например, есть очень удобная галочка Save Password ;) Теперь для того, чтобы авторизоваться, достаточно один раз нажать OK. Удобно нам - неудобно хакИру :) Этот способ практически не имеет недостатков, единственное - он работоспособен не на всех версиях апача. Для того, чтобы все было OK, PHP должен быть установлен как модуль Apache. Ну вот на wallst.ru, к примеру, все работает :)
Надеюсь, теперь тебя не хакнут. Удачи! Если есть какие-то вопросы, пиши в комментах. | |
|
|
|
|
|
|
|
для: Zebra
(27.09.2004 в 21:39)
| | Вот ещё. Через эту функцию стоит прогонять каждую переменную, записываемую в MySQL базу при помощи фунцкии mysql_query();
<?
function mysqlparser($txtstr) {
$txtstr = str_replace("\\", "\\\\", $txtstr);
$txtstr = str_replace("\"", "\\\"", $txtstr);
$txtstr = str_replace("'", "\'", $txtstr);
return $txtstr;
}
?>
|
| |
|
|
|
|
|
|
|
для: DDK
(12.11.2005 в 01:05)
| | Для этих целей лучше испльзовать стандартную функцию mysql_escape_string(), которая выполняет ту же работу но быстрее... | |
|
|
|
|
|
|
|
для: cheops
(12.11.2005 в 01:08)
| | Коленочный вариант тоже не плох ;-)))) | |
|
|
|
|