- Рекомендации Защиты (MySQL и SQL Web-интерфейс)
- Защита от SQL-инъекций в PHP
- Что такое SQL-инъекция
- Что может сделать злоумышленник?
- Экранирование кавычек
- Неэффективные способы защиты от SQL-инъекций
- 1. Функция htmlspecialchars()
- 2. Фильтрация по чёрному списку символов
- 3. Функция stripslashes()
- 4. Функция addslashes()
- Эффективные способы защиты
- 1. Функция mysql(i)_real_escape_string
- 2. Приведение к числу
- 3. Подготовленные запросы
- 4. Готовые библиотеки
Рекомендации Защиты (MySQL и SQL Web-интерфейс)
Любой использующий mysql (или любой другой sql сервер) на компьютере, связанном с internet должен прочитать эту консультацию, чтобы избежать наиболее часто встречающихся проблем защиты.
Однако необходимо подчеркнуть важность полной защиты сервера (не просто mysql сервера) от всех типов применяемых нападений. В данной статье, к сожалению, не возможно охватить все аспекты проблем безопасности, но самые важные проблемы рассмотрены достаточно полно.
mysql использует защиту, основанную на Списках Управления Доступа (acl) для всех подключений, запросов, и других операций, которые пользователь может пытаться исполнять. Имеется также некоторая поддержка для ssl-зашифрованных подключений между клиентами mysql и серверами. Многие из концепций, обсуждаемых в этой статье, не являются специфическими для mysql и могут быть применены ко всем приложениям.
Когда mysql запущен, постарайтесь следовать этим рекомендациям:
Никому не давайте доступ (кроме администратора mysql) к таблице user в базе данных mysql. Зашифрованный пароль — реальный пароль в mysql. Если Вы знаете пароль, перечисленный в таблице user для данного пользователя, вы можете легко войти как этот пользователь, если у вас есть доступ к компьютеру, перечисленному для этой учетной записи.
Изучите систему привилегии доступа mysql. Команды grant и revoke используются для управления доступом к mysql. Не предоставляйте больших привилегий, чем это необходимо. Никогда не предоставляйте привилегии всем компьютерам в сети. Команды контрольных проверок:
1. Пробуйте mysql -u root. Если вы сможете соединиться с сервером без запроса пароля, значит у вас проблемы. Любой сможет соединиться с вашим mysql сервером как mysql root пользователь с полными привилегиями. Тщательно читайте команды инсталляции mysql, обращая внимание на опции установки пароля.
2. Используйте команды show grants и check, чтобы видеть, кто имеет доступ к какой. Удалите лишние привилегии, используя команду revoke.
Не храните пароли в виде открытого текста в вашей базе данных. Когда ваш компьютер взломан, вторгшийся может получить полный список
паролей и использовать их. Вместо этого используйте md5 алгоритм или любой другой на основе односторонней хеш-функции.
Не выбирайте пароли из словарей. Имеются специальные программы, чтобы подбирать их. Даже пароли подобно » xfish98 » являются очень плохими. Намного лучше — » «duag98″, который содержит то же самое слово » рыба » но напечатанное на одну клавишу влево на клавиатуре. Другой метод состоит в том, чтобы использовать пароли типа «УМББР», который состоит из первых слов в предложении » У Мэри был большой ребенок». Такие пароли просто запомнить и напечатать, но трудно подобрать злоумышленнику.
Используйте firewall. Он защитит вас, по крайней мере, от 50% эксплуатирующихся уязвимостей в любом программном обеспечении. mysql использует 3306 порт по умолчанию. Этот порт должен быть доступен только от доверенных компьютеров. Самый простой способ проверить, является ли ваш порт mysql открытым, состоит в том, чтобы пробовать следующую команду от некоторой отдаленной машины, где server_host — имя хоста вашего mysql сервера: telnet server_host 3306
Не доверяйте никаким данным, введенными пользователями. Они могут обмануть ваш код, вводя специальные символы в web форме или url. Убедитесь, что ваше приложение остается безопасным, если пользователь вводит что-то подобно: drop database mysql;. Это — критический пример, но множество утечек защиты и потеря данных могут происходить из-за хакеров, использующих подобные методы. Также, не забудьте проверять числовые данные. Обычная ошибка должна защитить только строки. Иногда люди думают, что, если база данных содержит только публично доступные данные, она не должна защищаться. Это неправильно. По крайней мере, dos атака может быть выполнена против таких баз данных. Самый простой способ защиты от такого типа нападения состоит в том, чтобы использовать апострофы вокруг числовых констант:
select * from table where mysql автоматически преобразовывает эту строку к числу и убирает все нечисловые символы в запросе. Проверяем:
Все web приложения:
1. Попытка вводить ‘ ‘ ‘ ‘”’ в ваших web формах. Если Вы получаете любой вид ошибки mysql, сразу же исследуйте эту проблему.
2. Попытка изменить url, добавляя %22 (‘”’), %23(‘#’), %27‘’’.
3. Попытка изменить типы данных в динамических url от числовых символов к символам, приведенных в предыдущих примерах. Ваше приложение должно быть безопасно против этого и подобных нападений.
4. Попытка вводить символы, пробелы, и специальные символы вместо чисел в числовых полях. Ваше приложение должно удалить их перед их принятием mysql, или ваше приложение должно выдать ошибку.
5. Проверьте размеры данных перед принятием их mysql.
6. Ваше приложение должно соединяться с базой данных, используя пользователя, отличного от того, которого вы используете для административных целей. Не давайте вашим приложением больше прав, чем те, в которых они реально нуждаются.
Проверьте функцию addslashes(). После php 4.0.3, доступна функция mysql_escape_string (), которая является основанной на функции с таким же именем в mysql c api.
Пользователи mysql c api:
Проверьте api запрос mysql_escape_string ().
Проверьте переходы и кавычки модификаторов в потоках запросов.
Пользователи perl dbi:
Проверьте метод quote() или используйте структурный нуль (placeholders).
Пользователи java jdbc:
Используйте объект preparedstatement и placeholders.
Не передавайте незашифрованные данные по internet. Эти данные доступны для каждого, кто имеет время и способность перехватить их и использовать для собственных целей. Вместо этого, используйте зашифрованный протокол типа ssl или ssh. mysql поддерживает внутренний ssl версии 3.23.9. ssh может использоваться для создания зашифрованных туннелей.
Научитесь использовать tcpdump. В большинстве случаев, вы можете проверить, действительно ли потоки данных mysql незашифрованны, используя следующую команду:
Источник
Защита от SQL-инъекций в PHP
Что такое SQL-инъекция
SQL инъекция — это подстановка в SQL-запрос таких данных, которые меняют структуру этого запроса. Злоумышленник может использовать уязвимость для выполнения произвольного SQL-кода.
Представим типичную задачу — вывод статей на сайте. При переходе по адресу /index.php?id=15 должна быть отображена статья, идентификатор которой в базе данных равен числу 15.
Как начинающие разработчики обычно пишут запрос к базе данных:
Разработчик ожидает, что в $_GET[‘id’] будет число и конечный запрос станет таким:
Но вместо этого злоумышленник может передать строку -1 OR 1=1 :
При запуске этого запроса будут выбраны все записи вместо одной, поскольку записей с отрицательными идентификаторами скорее всего нет в базе, а условие 1=1 всегда истинно.
Но суть в другом. После фрагмента 1=1 злоумышленник может дополнить запрос любым произвольным SQL-кодом.
Что может сделать злоумышленник?
Это зависит от конкретного запроса, а также способа его запуска.
Если запрос выполняется не через функцию mysqli_multi_query() , которая поддерживает мультизапросы (несколько запросов через точку с запятой), тогда у злоумышленника нет возможности выполнить совсем произвольный запрос вроде такого:
Так сделать не получится, поскольку выполнение нескольких запросов по-умолчанию не поддерживается.
Но кое-что плохое злоумышленник сделать может. Например, с помощью UNION можно получить любые данные из любых таблиц.
Представим, что у нас есть таблица articles с 4 полями: id | title | content | created_at , а также таблица users с 3 полями: id | login | password .
Поскольку UNION позволяет объединять данные из таблиц только с одинаковым количеством столбцов, злоумышленник может указать 2 необходимых ему столбца, а остальные 2 заполнить любыми значениями, например единицами:
В итоге вместо title и content на страницу будут выведены login и password одного из пользователей. И это только один из десятков возможных вариантов взлома.
Экранирование кавычек
Прежде чем перейти к существующим способам защиты, хочу отдельно объяснить, что такое вообще экранирование и зачем оно нужно.
Возьмём такой пример:
С этим запросом всё в порядке, он выполнится как мы и ожидаем:
Но что если в переменной $name будет одинарная кавычка?
Тогда SQL-запрос станет таким:
Попытка выполнить этот запрос приведёт к ошибке синтаксиса. Чтобы её не было, вторую кавычку нужно экранировать, т.е. добавить к ней обратный слеш.
Способы экранирования и их надёжность разберём чуть ниже, а сейчас для простоты возьмём addslashes() :
Что будет в итоге:
Готово, запрос выполнится даже при наличии кавычек.
Экранировать можно не только кавычки. Разные функции умеют экранировать разные символы, об этом мы подробно поговорим чуть позже.
А теперь важный момент. Некоторые разработчики считают, экранирования достаточно для полной защиты от SQL-инъекций.
Хорошо, ещё раз посмотрим на самый первый пример с SQL-инъекцией:
В этом запросе нет никаких кавычек. Но уязвимость есть. Отсюда делаем вывод, что экранирование не гарантирует защиту от SQL-инъекций.
Неэффективные способы защиты от SQL-инъекций
Очевидно, самый худший вариант — не иметь никакой защиты от SQL инъекций и передавать данные, полученные от пользователя, напрямую в SQL-запрос.
Никогда так не делай! Любые данные перед подстановкой в SQL-запрос должны проходить фильтрацию и/или валидацию.
1. Функция htmlspecialchars()
Время от времени встречаю статьи, где авторы используют функцию htmlspecialchars() для экранирования данных:
Это опасно! Штука в том, что функция htmlspecialchars() пропускает без экранирования опасные символы: \ (слеш), \0 (nul-байт) и \b (backspace).
Вот полный пример кода, демонстрирующего уязвимость:
В итоге SQL-запрос будет таким:
С помощью / экранируется кавычка, идущая сразу после $login. `login` = ‘$login’ по факту превращается в `login` = ‘\’ AND `password` = ‘ . После этого любой код, который мы напишем, будет выполнен, в нашем случае это просто OR 1=1 . В конце добавляем # (комментарий), чтобы скрыть последнюю кавычку.
2. Фильтрация по чёрному списку символов
По каким-то непонятным мне причинам ещё существуют разработчики, использующие чёрные списки символов:
Все символы, входящие в чёрный список, удаляются из строки перед вставкой в базу.
Я не хочу сказать, что этот подход не будет работать, но его применение под большим вопросом:
- Зачем вообще составлять какие-то списки, если есть более простые и надёжные способы защиты?
- Нужно знать все потенциально опасные символы.
- Что делать если нужно разрешить пользователям использовать какие-либо символы из списка?
Кроме этого, я считаю фильтрацию в SQL-запросах плохой идеей. Если в строке есть недопустимые символы — лучше сообщить о них пользователю и попросить исправить, а не просто обрезать часть контента.
К примеру, пользователь хочет использовать логин
, а после регистрации оказывается, что его ник превратился в MegaPihar9000 .
Я считаю, лучше уточнить у пользователя, нравится ли ему такой отфильтрованный логин или он хотел бы что-то поменять. Короче, я за валидацию по белому списку вместо фильтрации по чёрному.
3. Функция stripslashes()
Редко, но встречается код, использующий stripslashes() перед записью в базу. Поскольку новички до сих пор копируют этот код в свои проекты, объясню, зачем эта функция нужна.
Раньше в PHP была такая штука как волшебные кавычки (Документация). Если эта директива была включена, то все данные, содержащиеся в $_GET, $_POST и $_COOKIE автоматически экранировались.
Сделано это было для защиты новичков, которые подставляли данные напрямую в SQL-запросы. На практике это было не самое удачное решение:
- Не очень удобно, когда все данные по-умолчанию экранируются, ведь зачастую они нужны в исходном виде.
- В идеале экранирование должно учитывать кодировку соединения с базой данных, о чём мы поговорим чуть позже. Из-за этого разработчикам приходилось убирать экранирование функцией stripslashes() и затем опять экранировать данные более подходящими функциями, в случае MySQL это была mysql_real_escape_string() .
Вот почему функцию stripslashes() можно встретить в старых учебниках. Чтобы отменить экранирование символов и получить исходную строку.
Начиная с PHP 5.4 функционал волшебных кавычек удалён, поэтому использовать stripslashes() перед записью в базу нет никакого смысла.
4. Функция addslashes()
В некоторых книгах ещё можно встретить рекомендации экранировать данные функцией addslashes() .
Эта функция надёжней, чем htmlspecialchars() , поскольку экранирует и обратный слеш, и nul-байт. Однако эта функция хуже, чем mysql_real_escape_string , поскольку не учитывает кодировку текущего соединения с базой.
Поэтому даже в документации прямо написано, что эту функцию не нужно использовать для защиты от SQL инъекций.
Эффективные способы защиты
1. Функция mysql(i)_real_escape_string
Работает эта функция примерно по тому же принципу, что и addslashes() , только учитывает текущую кодировку соединения с базой данных.
Есть две важные детали, которые вы должны знать, когда используете эту функцию.
Первая — вы всегда должны подставлять экранированные данные в кавычки. Если этого не делать, толку от экранирования не будет:
Вторая опасность подстерегает тех, кто использует некоторые специфические кодировки вроде GBK. В этом случае вам обязательно нужно указывать кодировку при установке соединения с базой.
Почитать о проблеме можно тут (блог разработчика, обнаружившего ошибку), здесь и подробней с примерами там.
2. Приведение к числу
Простой и эффективный способ защиты для числовых полей — приведение данных к числу. Пример:
Кавычки здесь не обязательны, поскольку в запрос в любом случае подставится число.
Есть один нюанс. Как я писал выше, мне не очень нравится идея фильтрации данных и здесь она может выйти боком с точки зрения SEO.
Допустим, есть интернет-магазин, где URL адреса страниц товаров выглядят как /product/15 , где 15 — идентификатор товара.
Если алгоритм поиска статьи заключается в том, что мы берём вторую часть URL и приводим её к числу, вроде такого:
Тогда можно писать какие угодно символы после числа 15 (только один следующий символ должен быть не цифровым), например /product/15abcde13824_ahaha_lol и система всё равно будет отображать статью c >
3. Подготовленные запросы
Один из лучших способов защиты от SQL инъекций. Суть в том, что SQL запрос сначала «подготавливается», а затем в него отдельно передаются данные.
Такой подход гарантирует отсутствие SQL-инъекций в момент подстановки данных, поскольку запрос уже «подготовлен» и не может быть изменён.
Но, как обычно, всё портят детали.
Первая деталь. Чуть выше я указывал ссылку на обсуждение уязвимости mysql_real_escape_string.
Если ты героически прочитал его до конца (нет), там есть интересное утверждение — что PDO с подготовленными запросами также может иметь уязвимость, связанную с кодировками.
Чтобы её избежать, нужно либо отключить эмуляцию подготовленных запросов, либо использовать только надёжные кодировки (например UTF-8), либо обязательно указывать кодировку соединения (через $mysqli->set_charset($charset) или DSN для PDO, но не через SQL-запрос SET NAMES).
Вторая деталь. Нужно понимать, что защита от SQL-инъекций будет действовать только в том случае, если мы не подставляем никаких данных напрямую в запрос. Если разработчик решит сделать так:
Тогда его не спасут никакие подготовленные запросы.
И третья деталь. В подготовленные запросы нельзя подставлять названия столбцов и таблиц.
Прекрасно. И что теперь делать?
Один из распространённых вариантов — белые списки. Простой пример:
Если полей много и не хочется всех их вбивать ручками — можно просто достать их всех из базы ( SHOW COLUMNS FROM `products`) .
Другой логичный вариант — валидировать названия столбцов, разрешая, к примеру, только буквы, цифры и подчёркивания.
В общем, опять надо что-то вручную допиливать, придумывать собственные функции генерации запросов. Не комильфо. Рекомендую поступить иначе.
4. Готовые библиотеки
Разработчики популярных библиотек наверняка гораздо умней и опытней нас. Они давно всё продумали и протестировали на десятках тысяч программистов. Так почему нет?
Для простых проектов вполне хватит Medoo или RedBeanPHP, для средних рекомендую (и всегда использую) Eloquent, ну а для крупных проектов лучше всего подойдёт мощная и суровая Doctrine.
Источник