SQL-инъекции
Кстати а при защите от SQL-инъекций достаточно будет только этой функции?
И хотелось бы знать с какой версии она поддерживается.
Кстати а при защите от SQL-инъекций достаточно будет только этой функции?
И хотелось бы знать с какой версии она поддерживается.[/QUOTE]
При прямых руках да. Но очень-очень-очень рекомендую проверять все переменные которые так или иначе используются в SQL запросах.
Читать faq по безопасности в этом разделе
securitylab.ru
Читать много и по нескольку раз чтобы понять.
mysql_escape_string() недостаточно для предотвращения инъекций
подскажите, вот что:
передают переменную $data
Если её обрабатывать так:
$sql="insert into table values ('".$data."')";
mysql_query($sql);
То будь осторожен, sql-хак возможен...
что-то меняет если данные писать так:
mysql_query("insert into table values('$data')");
???
Пытался olo'lo, olo\'lo... всё равно пишет :(
<input type="text" name="user"> <input type="submit" name="but" value="go!">
</form>
<?
if($_POST["user"])
{
echo "POST прошел";
$user=$_POST["user"];
if(mysql_query("insert into users (user) values ('$user')")) echo "<br> команда Мускуля прошла";
}
?>
Просьба (может глупая):
Пожалуйста, напишите мне какую-то инъекцию под этот код (форма, переменные и т.д. даны)... Ну например, чтобы инъекция трункатила таблицу...
Уважаемый Спай, по поводу моего поста чуть выше есть что сказать???...
CREATE TABLE `user`(
`user` VARCHAR(40) NOT NULL
)
*/
$user = "olol'o";
$rs = mysql_query("insert into user(user) values ('$user')");
if( $rs === false ) {
printf("#%d - %s", mysql_errno(), mysql_error());
} else {
echo("Ok");
}
Ругается как
Я полагаю и стоит у вас Денвер какой-нить и включен флаг magic_quotes.
Посчет TRUNCATE в INSERT - мне пока идей не пришло. Зато можно спокойно напосоздавать любых пользователей с любыми правами (и паролями, я полагаю).
А если $user равно 1'), (VERSION()), ('1 - то создаем за раз трех пользователей, имеем доступ к SQL-командам.
Уважаемый Спай, по поводу моего поста чуть выше есть что сказать???...
Конечно есть - читайте
http://webew.ru/articles/2078.webew
Да. Денвер стоит...
2 Спай: Угу.. почитал...
т.е. РЕАЛЬНЫМ спасением (читай - защитой) SQL-запросов будет:
1. Использование проверки переменных регулярками
2. Использование mysql_real_escape_string()
?
Я пока не знаю, как именно работает mysql_real_escape_string(), но вопрос таков:
А что, если нужно передавать данные как есть???
Ну, чтобы в базе можно было хранить html-код и т.п. ???
Передавать как есть, только обрабатывать mysql_real_escape_string
Ну потому что вас издалека видно :) Потому что если бы хоть раз ставили сами бы, то не возникало бы вопросов, почему строка уже экранирована, потому что знали бы надстройки. Так, при включенном magic_quotes все данные, приходящие от клиента автоматически экранируются.
И вообще [QUOTE="php.net"]This feature has been DEPRECATED as of PHP 5.3.0. Relying on this feature is highly discouraged.[/QUOTE]
Да и напишите функцию проверки, общую для всех.
У меня вся проверка данных выполняется в виде:
value_check( $value, array("type"=>"numeric", "min" => 100, "max" => "100K") );
value_check( $value, array("type"=>"email", "empty" => true) );
Ибо плодить в коде кучу регулярок и проверок ненаглядно, а тут сразу все видно. Проверка, так-то, хорошая.
а про вэлью_чек пошел читать
switch($type) {
case "email": $pattern='#...emailpattern...#'; break;
case "text": $pattern='#...symbolpatern...#'; break;
case "login": $pattern='#...loginpattern...#'; break;
default: $pattern='#...symbolpatern...#';
}
$valid=preg_match($pattern, $str);
return $valid;
}
if(value_check($login, "login")==true && value_check($email, "email")==true)
{
//Всякая белиберда
}
Правильно???
// устанавливаем проверочные параметры и дополняем пропущенными
$parameters = array_merge(
// значения параметров проверки "по-умолчанию"
array(
"empty" => null, // ничего не указано в $value
"equals" => null, // равенство чему-либо (равенство проверяется через ===
"minlength" => null, // минимальная длина строки
"maxlength" => null, // максимальная длина строки
"min" => null, // min значение числа
"max" => null, // max значение числа
"enum" => null, // допустимые значения (допустимое значение - массив типа array(enum1,enum2,...)
"mask" => null, // регулярное выражение, к которому должно подходить $value
"type" => null, // тип (заранее установленные типы: url, seo_url, email, numeric, bool
"charset" => "UTF-8" // кодировка $value (если передана строка)
),
// указанные значения параметров проверки
$parameters
);
// преобразование чисел с суффиксами в числа (преобразование суффиксов K,M,k,m)
foreach( array("minlength","maxlength", "min", "max") as $item ) {
if( is_null( $parameters[$item]) ) continue;
// выдергиваем последний символ и сверяем с позволенными суффиксами
switch( substr((string)$parameters[$item],-1) ) {
case "K":
$parameters[$item] = (int)substr($parameters[$item],0,-1)*1024; break;
case "k":
$parameters[$item] = (int)substr($parameters[$item],0,-1)*1000; break;
case "M":
$parameters[$item] = (int)substr($parameters[$item],0,-1)*1024*1024; break;
case "m":
$parameters[$item] = (int)substr($parameters[$item],0,-1)*1000*1000; break;
default:
break;
}
}
if( !is_null($parameters["empty"]) && $parameters["empty"] == true && $value == "" ) {
return true;
}
if( !is_null($parameters["equals"]) && $value != $parameters["equals"] ) {
return false;
}
if( !is_null($parameters["minlength"]) &&
mb_strlen($value,$parameters["charset"]) < $parameters["minlength"]
) {
return false;
}
if( !is_null($parameters["maxlength"]) &&
mb_strlen($value,$parameters["charset"]) > $parameters["maxlength"]
) {
return false;
}
if( !is_null($parameters["min"]) && (int)$value < (int)$parameters["min"] ) {
return false;
}
if( !is_null($parameters["max"]) && (int)$value > (int)$parameters["max"] ) {
return false;
}
if( !is_null($parameters["enum"]) &&
( !is_array($parameters["enum"]) || !in_array($value, $parameters["enum"], true) )
) {
return false;
}
if( !is_null($parameters["mask"]) && !preg_match($parameters["mask"], $value) ) {
return false;
}
if( !is_null($parameters["type"]) ) {
$no_errors = 1;
switch($parameters["type"]) {
case "url":
$no_errors &= preg_match("#^((http|https|ftp):\/\/)?(www\.)?[a-z0-9-_]+(\.[a-z]{2,6})+?(/?.*)$#i",$value);
break;
case "email":
$no_errors &= preg_match("/^(([a-z0-9]|[!#$%\*\/\?\|^\{\}`~&'\+=-_])+\.)*([a-z0-9]|[!#$%\*\/\?\|^\{\}`~&'\+=-_])+@([a-z0-9-_]+\.)+([a-z0-9-]){2,6}$/s",$value);
break;
case "numeric":
$no_errors &= is_numeric($value);
break;
case "bool":
$no_errors &= is_bool($value);
break;
default:
$no_errors = 0;
break;
}
if( $no_errors == 0 ) return false;
}
return true;
}
Использовать, например, как тут:
value_check(0, array("enum"=>array(1,2,3,4,5)) ); // false
value_check("", array("empty"=>true, "type"=>"email")); // true
value_check("example@example.com", array("type"=>"email")); // true
value_check("ololo", array("equals"=>"ololo")); //true
value_check("qwer(qw", array("maxlength"=>10, "mask"=> "/^qwe.*/i")); // true
value_check("abc", array("type"=>"bool")); // false
Советы с удовольствие выслушаю, ибо использую эту функцию чуть более, чем везде в своих наработках.
[COLOR="Silver"]За email и url рег.выражения не ругать, ибо мне их хватает.[/COLOR]
Да и что-то от оригинальной темы мы отошли.
Пофлудим немного о твоей функции :)
1. Вместо is_null() лично я использую === null, т.к. работает несколько быстрее, ибо не является функцией, а эффект одинаковый.
2. В substr() обязательно приведение к (string)? Думается мне, PHP это делает автоматически, т.к. ожидает на входе строку.
3. Какой смысл в двух проверках, если можно обойтись одной строгой?
// =>
if( $parameters["empty"] === true && $value == "" ) {
4. Здесь же кстати снова приведения типов, типа (int)$value - думаю, пхп может сравнивать по <, > только числа, значит, конвертит в них автоматом, а у тебя получаются лишние инструкции.
5. default: break; в switch'е для числовых значений явно лишний. Вообще, этот switch я бы переписал с таким трюком:
switch( substr($parameters[$item],-1) ) {
case "M": $multiplier *= 1024;
case "K": $multiplier *= 1024; break;
case "m": $multiplier *= 1000;
case "k": $multiplier *= 1000; break;
}
$parameters[$item] = substr($parameters[$item], 0, -1) * $multiplier;
Главное не пропусти break'и. Кстати, if (is_null()) перед switch'ем можно убрать, если добавить в него case null: continue;
6. in_array() скорее всего возвращает false, если $haystack не массив, так что у тебя снова лишние инструкции.
7. Последний switch я бы переделал немного иначе, убрав условие перед ним и добавив "case null: return true;" - у тебя ж после switch'а всё равно ничего нет, стало бы меньше на 2 return, переменную и пару скобок.
8. Кстати, зачем ты там везде используешь &=?
9. Регулярка для url выглядит странно - ты экранируешь "/" внутри неё, но разделитель-то у тебя не /, а # - так что это не нужно.
9.1. Кстати, в доменных именах, насколько я понимаю, подчёркивание не допускается, а у тебя оно есть. Плюс у тебя там избыточность: (/?.*)$ - совершенно не к чему, ибо все части необязательны (? и *), а затем идёт конец строки.
9.2. Насчёт (\.[a-z]{2,6})+[COLOR="Red"]?[/COLOR] я бы ещё раз подумал, по-моему он тут не к чему.
9.3. Зачем тебе в регулярках скобки, ты же не собираешь их в $matches? :) Если для наглядности, то я бы использовал флаг x и пробелы.
9.4. Регулярка для email какая-то странная (! # * / и т.д.).
Ладно, остановимся. Это совершенно личные впечатления, но может пригодятся :) Функция интересная, возьму на заметку соорудить для себя такую же при случае.
Большая часть как раз по-оформлению, а я уж привык порой написать лишнюю конструкцию, чтобы нагляднее мне было.
1) Вместо is_null() лично я использую === null
Хмм, а я как-то и не подумал. Реально, при 100.000 итерациях is_null выполняется в среднем ~0.041с, а === за ~0.015.
2) В substr() обязательно приведение к (string)?
Да, ты прав, php сам делает приведение к стринг, это, скорее, моя привычка всегда явно преобразовывать типы, хоть это и не суть важно для языка.
3) Какой смысл в двух проверках, если можно обойтись одной строгой?
Тут, так-то, всё верно.
4) Здесь же кстати снова приведения типов, типа (int)$value
См. пункт 2
5) Вообще, этот switch я бы переписал с таким трюком
Да, неплохо, надо будет переделать.
6) in_array() скорее всего возвращает false, если $haystack не массив, так что у тебя снова лишние инструкции
Warning: in_array() expects parameter 2 to be array, string given (для примера)
7) Последний switch я бы переделал немного иначе, убрав условие перед ним и добавив "case null: return true;"
Ок. Опять же is_null в if я добавил для наглядности, а то у меня везде выше is_null, потому визуально блок проверки сразу видно.
8) Кстати, зачем ты там везде используешь &=?
Чтобы не вводить внутри case условия if(!cond) return false; Потому для наглядности сделал битовое И, а в конце уже делают вывод.
Хотя с учетом замечаний выше + блок последний, я думаю, имеет смысл $no_error &= сразу заменить на return и всё. Что я, скорее всего, и сделаю.
9) Регулярка для url выглядит странно - ты экранируешь "/" внутри неё, но разделитель-то у тебя не /
:D косяк. Видать, когда-то скопипастил, потом сменил разделители, а внутри забыл поправить.
Над пунктами 9.1-9.4 на досуге пошаманю=)
Ок, спс, сделаю кое-какие поправки.
Если кому-то надо будет - выложу, один фиг полезная функция.
9.3) Согласен, захватывающие скобки надо заменить на "(?: ... )".
За юзабельность функции в целом не скажу, т. к. когда в свое время делал для себя, пришел к выводу, что особо время не экономит. А может, просто лень одолела. ^)
Большая часть как раз по-оформлению, а я уж привык порой написать лишнюю конструкцию, чтобы нагляднее мне было.
Ну, как я уже сказал, это дело каждого. Лично я придерживаюсь мнения, что "меньше кода - более читабельно", без экстрима, конечно.
Реально, при 100.000 итерациях is_null выполняется в среднем ~0.041с, а === за ~0.015.
Это известный приём оптимизации, типа как замена $i++ на ++$i - вместо 4х опкодов будет 3. Конечно, ощутимая разница будет видна на огромных потоках данных, но почему бы не писать сразу так, как быстрее, ведь разницы в коде нет, а "платить меньше"?
Ещё один трюк из этой же серии, уже на гране экстрима, им я почти не пользуюсь:
if (!isset($str[10])) { }
Т.к. isset() языковая конструкция, а не функция вроде strlen(), то нет расходов на вызов функции, например, создание её стёка, поиск в таблице функций и т.д.
Можешь погуглить про оптимизацию PHP-скриптов, я как-то находил интересную статью (список из ~40 пунктов), эта похожа на неё, но не та. Если интересно, выложу свои записи по оптимизации, которые я взял из той статьи.
И по-моему с (typecast)'ами код становится менее читабельным.
Warning: in_array() expects parameter 2 to be array, string given (для примера)
Да, верно. Это кстати ещё одна интересная штука. Знаешь про то, что код с @ выполняется во много раз медленнее, поэтому рекомендуется @ избегать, а вместо него включать error_reporting на максимум?
Я как раз придерживаюсь этого правила: на локальной машине error_reporting - максимальный, а на реальном сервере отключен. Тогда получается, что многие проверки, которые ты забиваешь @'ми нужны только для предотвращения вывода этих самых сообщений, когда юзер ввёл что-то неверно. Но у тебя-то на локальной машине ведь таких ситуаций почти не бывает и даже наоборот: рискуешь пропустить какое-то важное сообщение, когда код забит @.
У меня такое бывало не раз, например, когда это вызов какого-то метода @$class->Method(), а $class либо не объект, либо просто не имеет Method() - тогда скрипт вылетает, не сказав ни слова (не отправляя вывода и заголовков), и ищи потом место ошибки...
Поэтому использую @ тогда, когда без него код будет более громозкий. Кстати, проверки типа if (is_array($a)) { extract($a); } работают быстрее, чем @extract($a). in_array() и прочие так же.
Ок. Опять же is_null в if я добавил для наглядности, а то у меня везде выше is_null, потому визуально блок проверки сразу видно.
Ну, тебе же код читать, поэтому смотри сам. Как я уже сказал, для себя придерживаюсь мнения "меньше кода лучше видно".
8) Кстати, зачем ты там везде используешь &=?
Чтобы не вводить внутри case условия if(!cond) return false;
Так может и так, но у тебя ж там после каждого case стоит break, да и не похоже, чтоб $no_error зависел более, чем от одного условия.
А так код функции получается разбитым на два: в первой части (до последнего switch) ты сразу делаешь return false/true, а во второй используешь флаг, и только потом выходишь из функции. Тогда уж либо везде используй флаг + & и | (что кстати я обычно и делаю), либо везде return.
Если кому-то надо будет - выложу, один фиг полезная функция.
Выкладывай, может что ещё найдём интересное :)
9.3) Согласен, захватывающие скобки надо заменить на "(?: ... )".
Это не то, что я имел в виду (кстати, интересно, насколько быстрее работают регулярки с (?: ), чем с ()?). Там не везде скобки вообще используются, например: [COLOR="Blue"](/?.*)$#i",$value);[/COLOR] - здесь никакого модификатора после скобок нет.
За юзабельность функции в целом не скажу, т. к. когда в свое время делал для себя, пришел к выводу, что особо время не экономит.
Я сейчас использую небольшие функции типа
isset($var) or $var = 0;
$var = max($lowest, min($highest, $var));
}
вместо одной большой. Раньше, вроде бы, использовал вариант как у UAS. Пока не решил, что лучше.
Вопрос непосредственно по теме. Принято ли использовать mysqli_stmt_bind_param(), и если нет, то почему?
Скажем, под Oracle код, не использующий параметры, считается непрофессиональным. И в учебниках первой заповедью идёт именно использование параметров. В первую очередь это делается для разгрузки сервера: подстановка параметров - процедура намного более простая, чем разбор SQL и построение плана заново.
Поскольку значения параметров передаются отдельно от текста запроса и подставляются уже самим сервером, SQL-инъекция невозможна.
Ну так-то и я сам всегда поступаю)
Последний раз, где я использовал, так в классе для БД перед вызовом, например, @mysql_query, т.к. потом ниже все равно идет проверка результата с выбросом исключений, а @ нужен, дабы не вылезали нотисы, ворнинги или что он там показывает.
Ну посчет 8 пункта - он точно изменится)
Freeman
Я думаю, принято использовать, по крайней мере когда видел код mysqli, то там это использовали + очень много видел подобного, когда Java баловался. Так что используйте =)
Последний раз, где я использовал, так в классе для БД перед вызовом, например, @mysql_query
mysql_query() выдаёт варнинги? :eek: Сколько использую, ни одного не видел, если конечно был коннект к БД и всё штатно.
Ну, идея понятна :)
Принято ли использовать mysqli_stmt_bind_param(), и если нет, то почему?
Я не думаю, что в пхп есть "принятое" и нет. Конкретно по подготовленным запросам - я их почти не встречал в чужом коде (может, конечно, мало его читал) кроме какого-то одного популярного движка, да и сам не использую. Отчасти потому, что до сих пор юзаю MySQL, а не MySQLi (shame on me, ага), а отчасти потому, что при выводе одной страницы редко нужно делать несколько похожих запросов, которые можно подставить под шаблон - может быть полезно для форумов и тому подобных вещей.
А подготавливать выражение ради одного вызова слишком много мороки.
А, ну это конечно.
Суть не/выода ошибок, которую я пытался описать как раз в том, что во время разработки количество нкорректных запросов минимально - и даже если возникает что-то нештатное это либо баг, который надо пофиксить, либо ожидаемая ошибка как следствие некорректного ввода от юзера (= от программёра, имитирующего такой ввод). В обоих случаях вывод ошибок приветствуется и в обоих же случаях отключение вывода ошибок на реально работающем сервере только на руку и позволяет не задумываться о предупреждениях во время разработки.
По крайней мере я использую такой подход.