Хорошая практика и безопасность запросов к базе данных PHP / SQL
-
05-07-2019 - |
Вопрос
Итак, я немного опытный php-разработчик и "занимаюсь этим чертовым делом" с 2007 года;тем не менее, я все еще относительно осторожен, когда дело доходит до защиты моих приложений.В том смысле, что я на самом деле не знаю всего, что мог бы и должен знать.
Я подобрал Защита веб-приложений PHP и я читаю свой путь через это, проверяя все на своем пути.У меня есть несколько вопросов к общей группе SO, которые касаются запросов к базе данных (в основном под управлением mysql):
При создании приложений, которые помещают данные в базу данных, достаточно ли mysql_real_escape_string и общей проверки (is_numeric и т.д.) Входных данных?Как насчет других типов атак, отличных от sql-инъекции?
Может ли кто-нибудь объяснить хранимые процедуры и подготовленные инструкции с немного большей информацией, чем - вы создаете их и выполняете вызовы к ним.Я хотел бы знать, как они работают, какая проверка происходит за кулисами.
Я работаю в среде, привязанной к php4, и php5 на данный момент не подходит.Был ли кто-нибудь еще в таком положении раньше, что вы сделали для защиты своих приложений, пока все крутые ребята используют этот замечательный новый интерфейс mysqli?
Каковы некоторые общие рекомендации, которые люди сочли полезными, с акцентом на создание инфраструктуры, способной выдерживать обновления и возможные миграции (например, перенос php4 на php5).
Примечание:проведя поиск, я не смог найти ничего похожего на это, что могло бы повлиять на безопасность php-mysql.
Решение
Мои рекомендации:
- откажитесь от mysqli в пользу PDO (с драйвером mysql)
- используйте PDO параметризованные подготовленные инструкции
Затем вы можете сделать что-то вроде:
$pdo_obj = new PDO( 'mysql:server=localhost; dbname=mydatabase',
$dbusername, $dbpassword );
$sql = 'SELECT column FROM table WHERE condition=:condition';
$params = array( ':condition' => 1 );
$statement = $pdo_obj->prepare( $sql,
array( PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY ) );
$statement->execute( $params );
$result = $statement->fetchAll( PDO::FETCH_ASSOC );
Плюсы:
- Больше никаких экранирований вручную, поскольку PDO делает все это за вас!
- Относительно легко внезапно переключить серверные части базы данных.
Минусы:
- я не могу придумать ни одного.
Другие советы
Ответ Хавьера со ссылкой на owasp - хорошее начало.
Есть еще несколько вещей, которые вы можете сделать больше:
<Ол>Что касается атак с использованием SQL-инъекций, вы можете написать функцию, которая будет удалять общие операторы SQL из входных данных, например " DROP " или " УДАЛИТЬ * ГДЕ " ;, например:
* $ sqlarray = array ("DROP", "или 1 = 1", "union select", "SELECT * FROM", "select host", "create table", "" ОТ пользователей "," пользователи ГДЕ "); *
Затем напишите функцию, которая будет проверять ваш ввод по этому массиву. Убедитесь, что какие-либо вещи внутри $ sqlarray не будут обычным вводом ваших пользователей. (Не забудьте использовать strtolower, спасибо, lou).
Я не уверен, работает ли memcache с PHP 4, но вы можете установить некоторую защиту от спама с помощью memcache, разрешив только определенный удаленный IP-доступ к странице process.php X раз за период Y .
Привилегии важны. Если вам нужны только привилегии на вставку (скажем, обработка заказа), вам следует войти в базу данных на странице процесса заказа с пользователем, у которого есть только вставка, и, возможно, выбрать привилегии. Это означает, что даже если SQL-инъекция прошла, они могли выполнять только запросы INSERT / SELECT, а не удалять или реструктурировать.
Поместите важные файлы обработки php в такой каталог, как / include. Затем запретите всем IP-адресам доступ к этому каталогу / include. Р>
Поместите соленую MD5 с агентом пользователя + remoteip + вашу соль в сеанс пользователя и проверяйте при каждой загрузке страницы, что правильный MD5 находится в их cookie.
Запретить определенные заголовки ( http://www.owasp.org/index.php / Testing_for_HTTP_Methods_and_XST ). Запретить PUT (если вам не нужны загрузки файлов) / TRACE / CONNECT / DELETE заголовки.
Я обычно не работаю с PHP, поэтому не могу дать совет, специально предназначенный для ваших требований, но я предлагаю вам взглянуть на страницу OWASP, в частности, на отчет о 10 самых уязвимых местах: http://www.owasp.org/index.php/Top_10_2007
На этой странице для каждой уязвимости вы получаете список действий, которые можно предпринять, чтобы избежать проблемы на разных платформах (.Net, Java, PHP и т. д.)
Что касается подготовленных операторов, они работают, сообщая ядру базы данных, сколько параметров и каких типов ожидать во время конкретного запроса, используя эту информацию, механизм может понять, какие символы являются частью фактического параметра, а не что-то, что должно быть проанализирован как SQL как '(апостроф) как часть данных вместо' как разделитель строк. Извините, я не могу предоставить больше информации, ориентированной на PHP, но надеюсь, что это поможет. Р>
AFAIK, PHP / mySQL обычно не имеет параметризованных запросов.
Использование sprintf ()
с mysql_real_escape_string ()
должно работать очень хорошо. Если вы используете соответствующие строки формата для sprintf ()
(например, «% d» для целых чисел), вы должны быть в полной безопасности.
Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать mysql_real_escape_string
по предоставленным пользователем данным? Р>
если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например, ctype_digit
или is_numeric
или sprintf
(используя % d
или % u
для принудительного ввода числа)
Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<?php if (isset(
Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['product_name']) && isset(Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['product_description']) && isset(Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['user_id'])) { // Connect $link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password'); if(!is_resource($link)) { echo "Failed to connect to the server\n"; // ... log the error properly } else { // Reverse magic_quotes_gpc/magic_quotes_sybase effects on those vars if ON. if(get_magic_quotes_gpc()) { $product_name = stripslashes(Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['product_name']); $product_description = stripslashes(Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['product_description']); } else { $product_name =Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['product_name']; $product_description =Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['product_description']; } // Make a safe query $query = sprintf("INSERT INTO products (`name`, `description`, `user_id`) VALUES ('%s', '%s', %d)", mysql_real_escape_string($product_name, $link), mysql_real_escape_string($product_description, $link),Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>Я могу ошибаться, но этого должно быть недостаточно, чтобы использовать
mysql_real_escape_string
по предоставленным пользователем данным? Р>если только они не являются числами, в этом случае вы должны убедиться, что они на самом деле являются числами, используя, например,
ctype_digit
илиis_numeric
илиsprintf
(используя% d
или% u
для принудительного ввода числа)Также неплохо было бы иметь отдельного пользователя mysql для ваших php-скриптов, которые могут только SELECT, INSERT, UPDATE и DELETE ...
<Ч>Пример с php.net
POST['user_id']); mysql_query($query, $link); if (mysql_affected_rows($link) > 0) { echo "Product inserted\n"; } } } else { echo "Fill the form properly\n"; }Пример № 3 «Лучшая практика» запроса р>
Использование mysql_real_escape_string () вокруг каждой переменной предотвращает SQL-инъекцию. Этот пример демонстрирует «наилучшую практику» метод запроса базы данных, независимый от настройки Magic Quotes. Р>
Теперь запрос будет выполняться правильно, и атаки с использованием SQL-инъекций не будут работать.
<*>
Используйте хранимые процедуры для любого действия, связанного с записью в БД, и используйте параметры связывания для всех операций выбора.