PHP

Безопасные запросы

Вот я и вернулся, смотреть больше не могу на напитки, крепче кефира. Во время празднования окончания учебы и проводов одногруппников в армию, вспомнилась пара интересных моментов, о которых я Вам сегодня поведаю.

Первый из них - безопасный sql запрос. Используя его, можно забыть о фильтрации данных перед записью в БД.
#PHP
<?php
/* автор Nc_Soft, дополнил Fashion */
function sql(){ // создаем функцию
static $s=0; // создаем счетчик sql - запросов
if(!func_num_args()){return $s;}else{ // если функции ничего не передано, возвращаем кол - во sql - запросов
$a=func_get_args(); // поучаем массив аргументов, переданных функции
$s++; // считаем sql - запросы
$q=array_shift($a); // извлекаем первый элемент массива, т.к. фильтровать его не нужно(он содержит синтаксис)
$a=array_map('fsql',$a); // применяем фильтр ко всем элементам массива
array_unshift($a,$q); // добавляем извлеченный синтаксис обратно
$q=call_user_func_array('sprintf',$a); // подставляем вместо меток значения массива по порядку
$qs=mysql_query($q); // сам запрос к базе
if(mysql_errno()){
trigger_error('<u>'.mysql_errno().', sql#'.$s.'</u>:<br/><i>'.nl2br($q).'</i><br/><br/>'.mysql_error(),E_USER_ERROR); // если запрос содержит ошибку, то выводим ее на экран в качестве предупреждения вместе с sql запросом
}
return $qs; // возвращаем результат
}
}

/* функция - фильтр для sql - запросов */
function fsql($in){
return mysql_real_escape_string(xss($in)); // функцию xss возьмите из записей, созданных ранее
}

Итак. Что мы видим? У нас есть функция безопасного sql - запроса к БД и с фильтрацией от xss уязвимости до записи в БД.

Как этим пользоваться?
#PHP
<?php
$sql=sql("SELECT * FROM table WHERE login='%s' and pass='%s' LIMIT 1",$_GET['login'],$_GET['pass']);
// как видим, первый аргумент функции - сам запрос, а последующие - значения, которые подставляются на место %s
// ОБЯЗАТЕЛЬНО ставьте апострофы по краям %s, т.к. их отсутствие тоже является уязвимостью и без них вас никакая защита не спасет.
//Пример такого уязвимого запроса:
mysql_query("SELECT * FROM table WHERE login=".mysql_real_escape_string($_GET['login'])."");
// Вроде бы все отфильтровано, но это является дырой. Подставляем в $_GET['login'] сл. строку:
1 UNION SELECT * FROM table WHERE id=1 DESC LIMIT 1
// Получаем:
mysql_query("SELECT * FROM table WHERE login=1 UNION SELECT * FROM table WHERE id=1 DESC LIMIT 1");




echo sql(); // выдаст кол-во сделанных запросов

В первой функции мы затронули некую функцию - trigger_error(). Что она делает? Она генерирует сообщение об ошибке:
Notice: Undefined variable: abcd in S:\home\localhost\www\index.php on line 20
Что означает: использована незарегистрированная переменная.
Или:
Notice: Undefined offset: 5 in S:\home\localhost\www\index.php on line 21
Что означает: используется неизвестный ключ массива
А таких ошибок много и читать их не очень приятно. Погуглив немного, я нашел некоторую инфу на этот счет, дополнил ее, и выкладываю ее вам на обозрение:
#PHP
<?php
set_error_handler('myerr'); // сообщаем серверу о использовании нашей собственной функции - обработчик ошибок. Пишем эту строку в самом верху каждого файла или инклудим в самом верху

function myerr($type,$message,$file,$line){ // создаем функцию - обработчик ошибок
static $titles=array(
E_WARNING=>'Предупреждение',
E_NOTICE=>'Уведомление',
E_USER_ERROR=>'Ошибка, определенная пользователем',
E_USER_WARNING=>'Предупреждение, определенное пользователем',
E_USER_NOTICE=>'Уведомление, определенное пользователем',
E_STRICT=>'Проблема совместимости в коде',
E_RECOVERABLE_ERROR=>'Поправимая ошибка'
); // создаем массив с типами ошибок
$file=basename($file); // удаляем путь к файлу, в котором возникла ошибка(можно это не использовать, если у вас используется куча папок и в них есть файлы с одинаковыми именами)

if($_SERVER['REMOTE_ADDR']=='127.0.0.1'){ // если работаем на локально машине, то выводми предупреждение на экран
echo '<div style="margin: 2px; padding: 3px; border: 1px solid #ff00ff;"><h3>'.$titles[$type].'</h3><p>'.$message.'<br/>Файл: <b>'.$file.'</b>, линия <b>'.$line.'</b></p></div>';
}else{ // если работаем на сервере, то пишем запись о ошибке в лог
ob_clean(); // очищаем буфер, если он есть, а если нет, то просто удалите эту строку
$f=fopen('log.log','a+'); // открываем файл для записи ошибки
flock($f,LOCK_EX); // устанавливаем запрет на запись, которая может возникнуть в тот же момент
$fwrite=data(time()).' | '.$titles[$type].' | '.$file.' | '.basename($_SERVER['PHP_SELF']).' | '.$line." |\r\n".$message)."\r\n--------------------------\r\n\r\n";
/* генерируем сообщение о ошибке:
Дата | тип ошибки | файл, в котором возникла ошибка | файл, который был запущен | номер строки с ошибой | сам текст с описанием ошибки
*/
fwrite($f,strip_tags($fwrite)); // записываем, и перед записью удаляем теги если есть, т.к. они будут мешать при чтении ошибки
flock($f,LOCK_UN); // отпираем файл
fclose($f); // закрываем файл
exit('Извините, но на сервере возникла неизвестная ошибка!'); // прекращаем выполнение php файла и выводим текст о недоступности скрипта
}

}

Незабудьте выставить права на папку, в которой будет находиться лог с ошибками. И периодически проверяйте содержимое этого файла(после того,как проверили и исправили ошибки, можно его просто удалять, скрипт сам его создаст).

Ах да, чуть не забыл. Если у Вас есть файл .htaccess, добавьте в него сл. строки, а если нету, то создайте его:
#TEXT
Для работы на локальной машине:
php_value display_errors 1
php_value error_reporting 2047
php_flag log_errors Off


#TEXT
Для работы на сервере:
php_value display_errors 0
php_value error_reporting 0
php_flag log_errors Off

1 - включение/выключение вывода ошибок на экран
2 - включении генерации всевожможных ошибок
3 - выключение записи ошибок в лог apache


Если Вы поставите себе такой обработчик и увидите большой лог с ошибками - увы, но ваш код нужно будет переписывать.
Добавлено: 21 Августа 2013 02:53:12 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...