php sql injection защита
Php sql injection защита
Многие веб-разработчики даже не догадываются, что SQL-запросы могут быть подделаны, и считают, что SQL-запросы всегда достоверны. На самом деле поддельные запросы могут обойти ограничения доступа, стандартную проверку авторизации, а некоторые виды запросов могут дать возможность выполнять команды операционной системы.
Благодаря отсутствию проверки пользовательского ввода и соединению с базой данных под учётной записью суперпользователя (или любого другого пользователя, наделённого соответствующими привилегиями), взломщик может создать ещё одного пользователя БД с правами суперпользователя.
Пример #1 Постраничный вывод результата. и создание суперпользователя в PostgreSQL
Пример #2 Листинг статей. и некоторых паролей (для любой базы данных)
Пример #3 От восстановления пароля. до получения дополнительных привилегий (для любой базы данных)
Пугающий пример того, как на сервере баз данных могут выполняться команды операционной системы.
Пример #4 Выполнение команд операционной системы на сервере (для базы MSSQL)
Некоторые приведённые в этой главе примеры касаются конкретной базы данных. Это не означает, что аналогичные атаки на другие программные продукты невозможны. Работоспособность вашей базы данных может быть нарушена каким-либо другим способом.
Способы защиты
Хотя по-прежнему очевидно, что взломщик должен обладать по крайней мере некоторыми знаниями о структуре базы данных чтобы провести успешную атаку, получить эту информацию зачастую очень просто. Например, если база данных является частью open-source или другого публично доступного программного пакета с инсталляцией по умолчанию, эта информация является полностью открытой и доступной. Эти данные также могут быть получены из закрытого проекта, даже если он закодирован, усложнён, или скомпилирован, и даже из вашего личного кода через отображение сообщений об ошибках. К другим методам относится использование распространённых (легко угадываемых) названий таблиц и столбцов. Например, форма логина, которая использует таблицу ‘users’ c названиями столбцов ‘id’, ‘username’ и ‘password’.
Большинство успешных атак основывается на коде, написанном без учёта соответствующих требований безопасности. Не доверяйте никаким вводимым данным, особенно если они поступают со стороны клиента, даже если это списки в форме, скрытые поля или куки. Первый приведённый пример показывают, как подобные запросы могут привести к катастрофе.
Пример #5 Более безопасная реализация постраничной навигации
Помимо всего вышесказанного, вы можете логировать запросы в вашем скрипте либо на уровне базы данных, если она это поддерживает. Очевидно, что логирование не может предотвратить нанесение ущерба, но может помочь при трассировке взломанного приложения. Лог-файл полезен не сам по себе, а информацией, которая в нем содержится. Причём, в большинстве случаев полезно логировать все возможные детали.
SQL инъекции. Проверка, взлом, защита
SQL инъекция — это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.
Как вычислить уязвимость, позволяющую внедрять SQL инъекции?
Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.
Пример ошибки, возникающей при проверке уязвимости
Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые — сворачивание условия WHERE к истиностному результату при любых значениях параметров.
2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.
Практика. Варианты взлома сайта с уязвимостью на SQL внедрения
Итак, у нас есть уже упоминавшийся сайт test.ru. В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от парметра public (если параметр содержит значение 1, то новость публикуется).
Список новостей, разрешённых к публикации
При обращении к странице test.ru/?detail=4, которая должна выводить четвёртую новость появляется ошибка – новость не найдена.
В нашем случае новость существует, но она запрещена к публикации.
Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь
В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости
Разбор примера изнутри
За получение детального описания новости отвечает блок кода:
$detail_id=$_GET[‘detail’];
$zapros=»SELECT * FROM `$table_news` WHERE `public`=’1′ AND `id`=$detail_id ORDER BY `position` DESC»;
Глядя на запрос, получаемый при обращении к странице через test.ru/?detail=4+OR+1
SELECT * FROM `news` WHERE `public`=’1′ AND `id`=4 OR 1 ORDER BY `position` DESC
становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.
Защита от SQL инъекций (SQL внедрений)
Защита от взлома сводится к базовому правилу «доверяй, но проверяй». Проверять нужно всё – числа, строки, даты, данные в специальных форматах.
Числа
Строки
$a=»пример текста с апострофом ‘ «;
echo addslashes($a); //будет выведено: пример текста с апострофом \’
Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это mysql_escape_string($str); и mysql_real_escape_string($str);.
Магические кавычки
Магические кавычки – эффект автоматической замены кавычки на обратный слэш (\) и кавычку при операциях ввода/вывода. В некоторых конфигурациях PHP этот параметр включён, а в некоторых нет. Для того, что бы избежать двойного экранизирования символов и заэкранизировать данные по-нормальному через mysql_real_escape_string($str);, необходимо убрать автоматические проставленные обратные слеши (если магические кавычки включены).
Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc(); (возвращает 1 – если магические кавычки включены, 0 – если отключены).
В закючении привожу код с полной экранизацией строк для записи в БД
if(get_magic_quotes_gpc()==1)
<
$element_title=stripslashes(trim($_POST[«element_title»]));
$element_text=stripslashes(trim($_POST[«element_text»]));
$element_date=stripslashes(trim($_POST[«element_date»]));
>
else
<
$element_title=trim($_POST[«element_title»]);
$element_text=trim($_POST[«element_text»]);
$element_date=trim($_POST[«element_date»]);
>
$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);
Статья была подготовлена на основе практических навыков по защите веб-систем. Теория дело хорошее, но практика важнее и главное она работает.
Защита от SQL инъекций
Внедрение SQL-кода (SQL инъекция) — один из распространённых способов взлома сайтов, работающих с базами данных. Способ основан на внедрении в запрос произвольного SQL-кода.
Внедрение SQL позволяет хакеру выполнить произвольный запрос к базе данных (прочитать содержимое любых таблиц, удалить, изменить или добавить данные).
Атака этого типа возможна, когда недостаточно фильтруются входные данные при использовании в SQL-запросах.
Принцип атаки внедрения SQL
Если на сервере передан параметр city_id, равный 10 ( /weather.php?city_id=10 ), то выполнится SQL-запрос:
Отсутствие должной обработки параметров SQL-запроса — это одна из самых серьёзных уязвимостей. Никогда не вставляйте данные от пользователя в SQL запросы «как есть»!
Приведение к целочисленному типу
В SQL-запросы часто подставляются целочисленные значения, полученные от пользователя. В примерах выше использовался идентификатор города, полученный из параметров запроса. Этот идентификатор можно принудительно привести к числу. Так мы исключим появление в нём опасных выражений. Если хакер передаст в этом параметре вместо числа SQL код, то результатом приведения будет ноль, и логика всего SQL-запроса не изменится.
PHP умеет присваивать переменной новый тип. Этот код принудительно назначит переменной целочисленный тип:
Экранирование значений
Что делать, если в SQL запрос требуется подставить строковое значение? Например, на сайте есть возможность поиска города по его названию. Форма поиска передаст поисковый запрос в GET-параметр, а мы используем его в SQL-запросе:
Смысл запроса поменялся, потому что кавычка из параметра запроса считается управляющим символом: MySQL определяет окончание значение по символу кавычки после него, поэтому сами значения кавычки содержать не должны.
Очевидно, приведение к числовому типу не подходит для строковых значений. Поэтому, чтобы обезопасить строковое значение, используют операцию экранирования.
Подготовленные выражения
Вид атак типа «SQL-инъекция» возможен, потому что значения (данные) для SQL-запроса передаются вместе с самим запросом. Так как данные не отделены от SQL-кода, они могут влиять на логику всего выражения. К счастью, MySQL предлагает способ передачи данных отдельно от кода. Такой способ называется подготовленными запросами.
Выполнение подготовленных запросов состоит из двух этапов: вначале формируется шаблон запроса — обычное SQL-выражение, но без действительных значений, а затем, отдельно, в MySQL передаются значения для этого шаблона.
Первый этап называется подготовкой, а второй — выражением. Подготовленный запрос можно выполнять несколько раз, передавая туда разные значения.
Этап подготовки
На этапе подготовки формируется SQL-запрос, где на месте значений будут находиться знаки вопроса — плейсхолдеры. Эти плейсхолдеры в дальнейшем будут заменены на реальные значения. Шаблон запроса отправляется на сервер MySQL для анализа и синтаксической проверки.
Пример:
Этот код сформирует подготовленное выражение для выполнения вашего запроса.
После выполнения запроса получить его результат в формате mysqli_result можно функцией mysqli_stmt_get_result() :
Значения привязанных к запросу переменных сервер экранирует автоматически. Привязанные переменные отправляются на сервер отдельно от запроса и не могут влиять на него. Сервер использует эти значения непосредственно в момент выполнения, уже после того, как был обработан шаблон выражения. Привязанные параметры не нуждаются в экранировании, так как они никогда не подставляются непосредственно в строку запроса.
Защита от SQL-инъекций в PHP
Что такое SQL-инъекция
Как начинающие разработчики обычно пишут запрос к базе данных:
При запуске этого запроса будут выбраны все записи вместо одной, поскольку записей с отрицательными идентификаторами скорее всего нет в базе, а условие 1=1 всегда истинно.
Но суть в другом. После фрагмента 1=1 злоумышленник может дополнить запрос любым произвольным SQL-кодом.
Что может сделать злоумышленник?
Это зависит от конкретного запроса, а также способа его запуска.
Так сделать не получится, поскольку выполнение нескольких запросов по-умолчанию не поддерживается.
Но кое-что плохое злоумышленник сделать может. Например, с помощью UNION можно получить любые данные из любых таблиц.
Поскольку UNION позволяет объединять данные из таблиц только с одинаковым количеством столбцов, злоумышленник может указать 2 необходимых ему столбца, а остальные 2 заполнить любыми значениями, например единицами:
В итоге вместо title и content на страницу будут выведены login и password одного из пользователей. И это только один из десятков возможных вариантов взлома.
Экранирование кавычек
Прежде чем перейти к существующим способам защиты, хочу отдельно объяснить, что такое вообще экранирование и зачем оно нужно.
Возьмём такой пример:
С этим запросом всё в порядке, он выполнится как мы и ожидаем:
Тогда SQL-запрос станет таким:
Попытка выполнить этот запрос приведёт к ошибке синтаксиса. Чтобы её не было, вторую кавычку нужно экранировать, т.е. добавить к ней обратный слеш.
Способы экранирования и их надёжность разберём чуть ниже, а сейчас для простоты возьмём addslashes() :
Готово, запрос выполнится даже при наличии кавычек.
Экранировать можно не только кавычки. Разные функции умеют экранировать разные символы, об этом мы подробно поговорим чуть позже.
А теперь важный момент. Некоторые разработчики считают, экранирования достаточно для полной защиты от SQL-инъекций.
Хорошо, ещё раз посмотрим на самый первый пример с SQL-инъекцией:
В этом запросе нет никаких кавычек. Но уязвимость есть. Отсюда делаем вывод, что экранирование не гарантирует защиту от SQL-инъекций.
Неэффективные способы защиты от SQL-инъекций
Никогда так не делай! Любые данные перед подстановкой в SQL-запрос должны проходить фильтрацию и/или валидацию.
1. Функция htmlspecialchars()
Время от времени встречаю статьи, где авторы используют функцию htmlspecialchars() для экранирования данных:
Это опасно! Штука в том, что функция htmlspecialchars() пропускает без экранирования опасные символы: \ (слеш), \0 (nul-байт) и \b (backspace).
Вот полный пример кода, демонстрирующего уязвимость:
В итоге SQL-запрос будет таким:
2. Фильтрация по чёрному списку символов
По каким-то непонятным мне причинам ещё существуют разработчики, использующие чёрные списки символов:
Все символы, входящие в чёрный список, удаляются из строки перед вставкой в базу.
Я не хочу сказать, что этот подход не будет работать, но его применение под большим вопросом:
К примеру, пользователь хочет использовать логин
Я считаю, лучше уточнить у пользователя, нравится ли ему такой отфильтрованный логин или он хотел бы что-то поменять. Короче, я за валидацию по белому списку вместо фильтрации по чёрному.
3. Функция stripslashes()
Редко, но встречается код, использующий stripslashes() перед записью в базу. Поскольку новички до сих пор копируют этот код в свои проекты, объясню, зачем эта функция нужна.
Сделано это было для защиты новичков, которые подставляли данные напрямую в SQL-запросы. На практике это было не самое удачное решение:
Вот почему функцию stripslashes() можно встретить в старых учебниках. Чтобы отменить экранирование символов и получить исходную строку.
Начиная с PHP 5.4 функционал волшебных кавычек удалён, поэтому использовать stripslashes() перед записью в базу нет никакого смысла.
4. Функция addslashes()
Поэтому даже в документации прямо написано, что эту функцию не нужно использовать для защиты от SQL инъекций.
Эффективные способы защиты
1. Функция mysql(i)_real_escape_string
Есть две важные детали, которые вы должны знать, когда используете эту функцию.
Вторая опасность подстерегает тех, кто использует некоторые специфические кодировки вроде GBK. В этом случае вам обязательно нужно указывать кодировку при установке соединения с базой.
Почитать о проблеме можно тут (блог разработчика, обнаружившего ошибку), здесь и подробней с примерами там.
2. Приведение к числу
Кавычки здесь не обязательны, поскольку в запрос в любом случае подставится число.
Есть один нюанс. Как я писал выше, мне не очень нравится идея фильтрации данных и здесь она может выйти боком с точки зрения SEO.
Если алгоритм поиска статьи заключается в том, что мы берём вторую часть URL и приводим её к числу, вроде такого:
Тогда можно писать какие угодно символы после числа 15 (только один следующий символ должен быть не цифровым), например /product/15abcde13824_ahaha_lol и система всё равно будет отображать статью c >
3. Подготовленные запросы
Один из лучших способов защиты от SQL инъекций. Суть в том, что SQL запрос сначала «подготавливается», а затем в него отдельно передаются данные.
Такой подход гарантирует отсутствие SQL-инъекций в момент подстановки данных, поскольку запрос уже «подготовлен» и не может быть изменён.
Но, как обычно, всё портят детали.
Первая деталь. Чуть выше я указывал ссылку на обсуждение уязвимости mysql_real_escape_string.
Вторая деталь. Нужно понимать, что защита от SQL-инъекций будет действовать только в том случае, если мы не подставляем никаких данных напрямую в запрос. Если разработчик решит сделать так:
Тогда его не спасут никакие подготовленные запросы.
И третья деталь. В подготовленные запросы нельзя подставлять названия столбцов и таблиц.
Прекрасно. И что теперь делать?
В общем, опять надо что-то вручную допиливать, придумывать собственные функции генерации запросов. Не комильфо. Рекомендую поступить иначе.
4. Готовые библиотеки
Разработчики популярных библиотек наверняка гораздо умней и опытней нас. Они давно всё продумали и протестировали на десятках тысяч программистов. Так почему нет?
Для простых проектов вполне хватит Medoo или RedBeanPHP, для средних рекомендую (и всегда использую) Eloquent, ну а для крупных проектов лучше всего подойдёт мощная и суровая Doctrine.
Устраняем уязвимости: как защитить сайт от SQL-инъекции
SQL-инъекция — одна из самых опасных уязвимостей, которая не дает разработчикам спать по ночам. Научитесь бороться с ней, чтобы защитить свой сайт.
Заводишь сайт, наполняешь его контентом, запускаешь рекламную кампанию — трафик стабильно растет, пользователи активно комментируют и делятся статьями. Всё хорошо до того момента, пока в один не предвещающий беды день на сайте не оказывается ни одной статьи. Заходишь в лог запросов и видишь, что кто-то сделал DROP.
Что такое SQL-инъекция
SQL-инъекция — это попытка изменить запрос к базе данных. Ввести ее можно через форму или ссылку, которая передает параметры методом GET. Представьте, что у вас на сайте есть форма регистрации, в которую пользователь вводит логин и пароль.
Они отправляются на сервер и вставляются в запрос такого вида:
Это удобно, если в форме очень много полей, но из-за этого появляется уязвимость: если пользователь захочет запустить SQL-инъекцию, то он может просто создать в форме новое поле с нужным ему именем и ввести любое значение. Например, можно создать поле admin и ввести значение 1.
Если сайт плохо защищен, то хакер может совершить много попыток, но в итоге все же получить доступ к правам администратора.
Как защитить сайт от SQL-инъекции
Прежде всего запомните, что безопасных систем не бывает, поэтому нужно постоянно искать уязвимости, которые могут быть у вашего сайта.
1. Используйте белые списки
В примере выше нас бы спасло, если бы существовал белый список — нужно указать допустимые ключи и значения.
2. Не пользуйтесь методом GET в формах
Передавать переменные этим способом очень опасно, потому что они оказываются на виду у пользователей. Если информация важная, то лучше используйте POST. Из ссылки злоумышленник может узнать не только имена переменных, но и какие значения должны быть в них — так он сможет выбрать оптимальную переменную для ввода инъекции.
Не советуем использовать переменные прямиком из супермассива — лучше поместить их в другую переменную, предварительно проверив данные.
3. Обрабатывайте переменные
Старайтесь экранировать кавычки, заменять служебные символы, удалять лишние пробелы и так далее. Давайте посмотрим, что будет, если этого не делать:
Используйте функции trim (убирает лишние пробелы), htmlspecialchars (заменяет треугольные скобки и другие спецсимволы), addslashes (экранирует кавычки и специальные символы) и другие.
В приведенной форме регистрации, например, злоумышленник мог бы использовать кавычки и запятые, чтобы добавить новый ключ и новое значение. То есть, введя в поле что-то вроде «pass’, ’something», он бы добавил какую-нибудь еще информацию, которая не проходит белый список.
Также вы можете проверять типы переменных — число, строка, файл и так далее.
4. Проверяйте, откуда пришли данные
Недостаточно просто обработать данные — нужно узнать, откуда они пришли. Отследить источник можно несколькими способами:
Конечно, это лишь незначительный бонус к защите, но зато он отсеивает часть неопытных взломщиков, которые бросят свои потуги после нескольких попыток.
5. Используйте PDO
С помощью PDO и плейсхолдеров можно значительно снизить риск инъекции, потому что данные и запрос отправляются отдельно. Сначала производится подключение к базе, потом подготавливается запрос, затем отдельно указываются переменные, и, наконец, запрос выполняется. Выглядит это так:
Данные отправляются уже в виде переменных. То есть если бы в переменной кто-то поставил лишнюю кавычку, сервер бы не подумал, что она — часть запроса. Поэтому попытка испортить запрос через переменную не сработала бы.
Этот способ, пожалуй, эффективнее остальных, поэтому переходите на PDO как можно скорее. Хотя его использование не означает, что другие меры безопасности уже не нужны — чем больше защитных механизмов вы установите, тем сохраннее будет информация.
Часто проверками пренебрегают самоучки, которые изучают темы «по верхушкам», — они могут решить задачу, но редко задумываются о том, чтобы устранить уязвимости. Поэтому мы предлагаем курс с системным обучением — вы научитесь создавать эффективные и надежные системы, которым не будут страшны инъекции и утечки данных.
Дополнительная защита сайта от взлома
Кроме того, чтобы обезопасить сам запрос и переменные, нужно сделать всё, чтобы хакер не смог найти уязвимости. Для этого устраните все возможные дыры, из-за которых может появиться утечка информации и вероятность несанкционированного доступа к правам администратора.
1. Запретите прямой доступ к служебным файлам
Все файлы на сайте можно открыть или скачать, если знать их адрес. Например, можно попытаться скачать файл header.php, чтобы найти там уязвимости (если скачивание будет удачным).
Поэтому лучше все подключаемые файлы переместить в отдельную директорию и запретить к ней прямой доступ. Например, можно создать папку includes и поместить туда:
Прямой доступ заблокируется, но подключать эти файлы через PHP все еще будет можно.
2. Не выкладывайте свой код на форумы
Порой нам всем бывает нужна помощь коллег, поэтому мы выкладываем фрагменты своего кода на форумы или Stackoverflow. Делать это рекомендуется только в очень крайних случаях и лишь тогда, когда вы убедитесь, что никто не сможет вычислить ваш сайт.
Ничто не должно намекать на то, какой тематики сайт, какой у него адрес, на каком он хостинге и так далее. Чем больше информации вы добровольно расскажете, тем выше риск быть взломанным.
3. Проверяйте копипасту
Всегда проверяйте код, который вы копируете, — в нем может быть какой-то незаметный эксплойт, который навредит вашему сайту или позволит автору кода получить к нему доступ. Старайтесь хотя бы читать то, что вы вставляете. Даже если там всего одна строчка, ее может быть достаточно, чтобы создать брешь в вашей защите.
В идеале лучше вручную переписывать код — так вы точно заметите все подозрительные команды.
4. Отключите вывод ошибок
Вывод ошибок — очень полезная функция на этапе разработки. Но если сайт уже выложен в сеть, то оповещение о багах лучше отключить, потому что злоумышленник может увидеть, какие у сайта проблемы, и попытаться использовать эти уязвимости в своих целях.
Кроме того, уберите вывод ошибок, который вы прописали в самом коде.
5. Ограничьте права пользователя базы данных
Обычно для подключения к базе данных создают учетную запись со всеми правами, чтобы можно было использовать все преимущества SQL, но лучше их ограничить. Для этого зайдите в phpmyadmin, добавьте нового пользователя и выдайте ему только часть прав:
Пока ни одна галочка не отмечена — поставьте ее на пункт «Данные». Так можно будет оперировать существующими данными, но нельзя будет создать новую таблицу или удалить старую. Так вы защититесь в том числе и от атаки с помощью команды DROP, которая может удалить все статьи или комментарии с сайта.
Если же менеджеру вашего сайта все же регулярно нужны привилегии администрирования и изменения структуры, создайте отдельного пользователя, которого будете использовать в админке. Но риск того, что доступ к ней кто-то получит, все равно существует. Поэтому стоит ограничить максимальное количество подключений и запросов.
Также лучше поставить пароль на root, чтобы взломщик не смог подключиться через него.
6. Установите последнюю версию языка
Старые версии языков (любых) часто не только менее функциональны, но и обладают букетом уязвимостей. Критические дыры стали известны хакерам много лет назад, поэтому они используют их, чтобы взломать сайт.
Да, в обновлении тоже могут быть баги, но не такие опасные. Тем более о них никто не будет знать, если после релиза прошло немного времени.
7. Используйте сложный пароль
Банально, но простой пароль можно подобрать за несколько секунд. Особенно если он содержит персональные данные:
Усугубляет положение то, что эту информацию мы часто выкладываем в свободный доступ.
Заключение
Безопасных систем не существует — вы можете только снизить вероятность взлома. Для этого нужно самому быть немного хакером:
Пытайтесь взломать собственный сайт, чтобы узнать, какие у него есть уязвимости. Сделайте это, пока кто-то не опередил вас.
Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.