Как я могу предотвратить внедрение SQL в PHP?

Если пользовательский ввод вставляется без изменения в SQL-запрос, приложение становится уязвимым для внедрения SQL , как в следующий пример:

 $unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Это потому, что пользователь может ввести что-то вроде value'); DROP TABLE table;--, и запрос станет:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Что можно сделать, чтобы этого не случилось?

2780 голосов | спросил 44 revs, 36 users 14%
Andrew G. Johns
1 Jam1000000amThu, 01 Jan 1970 03:00:00 +030070 1970, 03:00:00

27 ответов


0

Используйте подготовленные операторы и параметризованные запросы. Это операторы SQL, которые отправляются и анализируются сервером базы данных отдельно от любых параметров. Таким образом, злоумышленник не сможет внедрить вредоносный SQL.

Для этого у вас есть два варианта:

  1. Использование PDO (для любого поддерживаемого драйвера базы данных):

     $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. Использование MySQLi (для MySQL):

     $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

Если вы подключаетесь к базе данных, отличной от MySQL, существует вторая опция для драйвера, к которой вы можете обратиться (например, pg_prepare() и pg_execute() для PostgreSQL). PDO - это универсальный вариант.

Правильная настройка соединения

Обратите внимание, что при использовании PDO для доступа к базе данных MySQL подготовленные операторы real не используются по умолчанию . Чтобы это исправить, вы должны отключить эмуляцию подготовленных операторов. Пример создания соединения с использованием PDO:

 $dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

В приведенном выше примере режим ошибки не является строго обязательным, , но рекомендуется добавить его . Таким образом, скрипт не остановится на Fatal Error, если что-то пойдет не так. И это дает разработчику возможность catch любых ошибок, которые throw n как PDOException s.

Что является обязательным , тем не менее, это первая строка setAttribute(), которая указывает PDO отключить эмулированную подготовленную заявления и использовать реальные подготовленные заявления. Это гарантирует, что оператор и значения не будут проанализированы PHP перед отправкой его на сервер MySQL (что не дает возможному злоумышленнику возможности внедрить вредоносный SQL).

Хотя вы можете установить charset в опциях конструктора, важно отметить, что «старые» версии PHP (& lt ; 5.3.6) молча проигнорировал параметр charset в DSN .

Описание

В результате получается, что оператор SQL, который вы передаете prepare, анализируется и компилируется сервером базы данных. Указав параметры (либо ?, либо именованный параметр, например :name в приведенном выше примере) вы указываете движку базы данных, куда вы хотите фильтровать. Затем, когда вы вызываете execute, подготовленный оператор объединяется с указанными вами значениями параметров.

Здесь важно то, что значения параметров объединяются с скомпилированным оператором, а не строкой SQL. Инъекция SQL работает путем обмана скрипта, включающего вредоносные строки, когда он создает SQL для отправки в базу данных. Таким образом, отправляя фактический SQL отдельно от параметров, вы ограничиваете риск того, что вы не захотите. Любые параметры, которые вы отправляете при использовании подготовленного оператора, будут просто обрабатываться как строки (хотя ядро ​​базы данных может выполнять некоторую оптимизацию, поэтому, конечно, параметры могут также оказаться числами). В приведенном выше примере, если переменная $name содержит 'Sarah'; DELETE FROM employees результатом будет просто поиск строки "'Sarah'; DELETE FROM employees", и вы не получите пустая таблица .

Еще одним преимуществом использования подготовленных операторов является то, что если вы выполняете один и тот же оператор много раз в одном и том же сеансе, он будет проанализирован и скомпилирован только один раз, что даст вам некоторое увеличение скорости.

О, и так как вы спросили о том, как сделать это длявставить, вот пример (с использованием PDO):

 $preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

Можно ли использовать подготовленные операторы для динамических запросов?

Хотя вы все еще можете использовать подготовленные операторы для параметров запроса, структура самого динамического запроса не может быть параметризована, а некоторые функции запроса не могут быть параметризованы.

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

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0
  

Внимание:   Пример кода этого ответа (как пример кода вопроса) использует расширение PHP MySQL, которое устарело в PHP 5.5.0 и полностью удалено в PHP 7.0.0.

Если вы используете последнюю версию PHP, описанная ниже опция mysql_real_escape_string больше не будет доступна (хотя mysqli::escape_string является современным эквивалентом). В наши дни опция mysql_real_escape_string будет иметь смысл только для устаревшего кода в старой версии PHP.


У вас есть два варианта - экранирование специальных символов в вашем unsafe_variable или использование параметризованного запроса. Оба защитят вас от внедрения SQL. Параметризованный запрос считается лучшей практикой, но для его использования потребуется изменить новое расширение MySQL в PHP.

Мы рассмотрим нижнюю ударную цепочку, экранирующую первую.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

См. также подробную информацию о mysql_real_escape_string функция.

Чтобы использовать параметризованный запрос, вам нужно использовать MySQLi , а не MySQL функции. Чтобы переписать ваш пример, нам нужно что-то вроде следующего.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

Ключевой функцией, которую вы хотите прочитать, будет mysqli::prepare

Кроме того, как и предлагали другие, вам может быть полезно /проще увеличить уровень абстракции с помощью чего-то вроде PDO .

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

  • Если вы хотите изменить структуру SQL на основе пользовательского ввода, параметризованные запросы не помогут, и требуемый выход не покрывается mysql_real_escape_string. В этом случае вам лучше передать ввод пользователя через белый список, чтобы обеспечить пропуск только «безопасных» значений.
  • Если вы используете целые числа из пользовательского ввода в условии и используете подход mysql_real_escape_string, вы столкнетесь с проблемой, описанной Полином в комментариях ниже. Этот случай сложнее, потому что целые числа не будут заключены в кавычки, так что вы могли бы справиться, проверив, что пользовательский ввод содержит только цифры.
  • Вероятно, есть другие случаи, о которых я не знаю. Вы можете найти этот полезный ресурс по некоторым из более тонких проблем, с которыми вы можете столкнуться.
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Каждый ответ здесь охватывает только часть проблемы. На самом деле, есть четыре разные части запроса, которые мы можем динамически добавить к ним: -

  • строка
  • число
  • идентификатор
  • синтаксическое ключевое слово.

И подготовленные заявления охватывают только два из них.

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

В общем, такой подход к защите основан на белом списке .

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

$orders  = array("name", "price", "qty"); // Field names
$key     = array_search($_GET['sort'], $orders)); // See if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

Однако есть другой способ защитить идентификаторы - экранирование. Пока у вас есть идентификатор в кавычках, вы можете избежать обратных кавычек, удвоив их.

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

Итак, короче говоря, это заполнитель , а не подготовленное заявление можно рассматривать как серебряную пулю.

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

Тем не менее существует проблема с ключевыми словами синтаксиса SQL (такими как AND, DESC и тому подобное), но в этом случае единственным подходом является белый список.

Update

Несмотря на то, что существует общее согласие относительно лучших практик защиты SQL-инъекций, еще много плохих практик. И некоторые из них слишком глубоко укоренились в умах пользователей PHP. Например, на этой самой странице (хотя и невидимой для большинства посетителей) более 80 удаленных ответов - все они удалены сообществом из-за плохого качества или из-за пропаганды плохих и устаревших методов. Хуже того, некоторые плохие ответы не удаляются, а процветают.

Например, там (1) (2) все еще (3) много (4) ответов (5) , включая второй ответ с наибольшим количеством голосов , предлагающий ручное экранирование строк - устаревший подход, который, как оказалось, небезопасен.

Или есть немного лучший ответ, который предлагает просто другой метод форматирования строк и даже может похвастаться его как абсолютной панацеей. Хотя, конечно, это не так. Этот метод ничем не лучше обычного форматирования строк, но при этом он сохраняет все свои недостатки: он применим только к строкам и, как и любое другое ручное форматирование, по сути является необязательным, необязательным показателем, подверженным человеческим ошибкам любого рода.

Я думаю, что все это из-за одного очень старого суеверия, поддерживаемого такими авторитетами, как OWASP или руководство по PHP , в котором провозглашается равенство между "выходом" и защитой от SQL-инъекций.

Независимо от того, что руководство PHP говорило целую вечность, *_escape_string никоим образом не делает данные безопасными и никогда не имеет был предназначен для. Помимо бесполезности для любой части SQL, кроме строки, ручное экранирование неправильно,потому что это руководство, а не автоматизированное.

И OWASP делает это еще хуже, подчеркивая необходимость избегать пользовательского ввода , что является полной ерундой: в контексте защиты от инъекций не должно быть таких слов. Каждая переменная потенциально опасна - независимо от источника! Или, другими словами - каждая переменная должна быть правильно отформатирована, чтобы ее можно было вставить в запрос - независимо от источника снова. Это пункт назначения, который имеет значение. В тот момент, когда разработчик начинает отделять овец от коз (думая, является ли какая-то конкретная переменная «безопасной» или нет), он /она делает свой первый шаг к катастрофе. Не говоря уже о том, что даже формулировка предполагает массовый выход в точке входа, напоминающий функцию очень волшебных кавычек - уже презирали, осуждали и удаляли.

Таким образом, в отличие от любых "экранирующих", подготовленные операторы являются мерой, которая действительно защищает от внедрения SQL (когда это применимо).

Если вы все еще не уверены, вот пошаговое объяснение, которое я написал, Руководство автостопщика в предотвращение SQL-инъекций , где я подробно объяснил все эти вопросы и даже составил раздел, полностью посвященный плохим практикам и их раскрытию.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Я бы рекомендовал использовать PDO (объекты данных PHP) для запустить параметризованные запросы SQL.

Это не только защищает от внедрения SQL, но и ускоряет запросы.

И с помощью PDO вместо mysql_, mysqli_ и pgsql_ делают ваше приложение немного более абстрагированным от базы данных в тех редких случаях, когда вам приходится поменять поставщиков баз данных.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Используйте PDO и подготовленные запросы.

($conn является PDO объект)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Как видите, люди советуют вам использовать максимально подготовленные высказывания. Это не так, но когда ваш запрос выполняется только один раз для каждого процесса, это может привести к небольшому снижению производительности.

Я столкнулся с этой проблемой, но, думаю, я решил ее очень изощренным способом - способом, который используют хакеры, чтобы избежать использования кавычек. Я использовал это в сочетании с подготовленными утверждениями. Я использую его для предотвращения всех возможных атак SQL-инъекций.

Мой подход:

  • Если вы ожидаете, что ввод будет целочисленным, убедитесь, что это действительно . В языке переменного типа, таком как PHP, это очень важно. Например, вы можете использовать это очень простое, но мощное решение: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Если вы ожидаете что-то еще от целого числа , заклейте его . Если вы это сделаете, вы полностью избежите ввода. В C /C ++ есть функция под названием mysql_hex_string() , в PHP вы можете использовать bin2hex() .

    Не беспокойтесь о том, что экранированная строка будет иметь двукратный размер по сравнению с первоначальной длиной, потому что даже если вы используете mysql_real_escape_string, PHP должен выделять такую ​​же емкость ((2*input_length)+1), что тоже самое.

  • Этот шестнадцатеричный метод часто используется при передаче двоичных данных, но я не вижу причин, почему бы не использовать его для всех данных, чтобы предотвратить атаки с использованием SQL-инъекций. Обратите внимание, что вы должны добавить данные к 0x или использовать функцию MySQL UNHEX вместо.

Так, например, запрос:

SELECT password FROM users WHERE name = 'root'

станет:

SELECT password FROM users WHERE name = 0x726f6f74

или

SELECT password FROM users WHERE name = UNHEX('726f6f74')

Hex - это идеальный побег. Нет способа ввести.

Разница между функцией UNHEX и префиксом 0x

Было несколько обсуждений в комментариях, поэтому я, наконец, хочу прояснить это. Эти два подхода очень похожи, но они немного отличаются в некоторых отношениях:

Префикс ** 0x ** можно использовать только для столбцов данных, таких как char, varchar, text, block, binary и т. д. .
Кроме того, его использование немного сложнее, если вы собираетесь вставить пустую строку. Вам придется полностью заменить его на '', иначе вы получите ошибку.

UNHEX () работает с любым столбцом; вам не нужно беспокоиться о пустой строке.


шестнадцатеричные методы часто используются в качестве атак

Обратите внимание, что этот шестнадцатеричный метод часто используется как атака SQL-инъекции, где целые числа подобны строкам и экранируются только с помощью mysql_real_escape_string. Тогда вы можете избежать использования кавычек.

Например, если вы просто делаете что-то вроде этого:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

атака может сделать вам легко . Рассмотрим следующий внедренный код, возвращаемый из вашего скрипта:

  

SELECT ... WHERE id = -1 объединяет все, выбирают имя_таблицы из файла information_schema.tables

а теперь просто извлеките структуру таблицы:

  

SELECT ... WHERE id = -1 объединить все выбрать столбец_имя из information_schema.column, где table_name = 0x61727469636c65

А затем просто выберите все данные, которые хотите. Разве это не круто?

Но если кодировщик сайта для инъекций закодирует его, внедрение будет невозможно, потому что запрос будет выглядеть следующим образом: SELECT ... WHERE id = UNHEX('2d312075...3635')

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0
  

ВАЖНО

     

Лучший способ предотвратить инъекцию SQL - это использовать подготовленные операторы вместо экранирования , так как принятый ответ демонстрирует.

     

Есть такие библиотеки, как Aura.Sql и EasyDB , которые позволяют разработчикам проще использовать подготовленные операторы. Подробнее о том, почему подготовленные заявления лучше узнать по адресу остановка внедрения SQL , см. этот mysql_real_escape_string() обход и недавно исправлены уязвимости Unicode SQL Injection в WordPress .

Предотвращение инъекций - mysql_real_escape_string () р>

PHP имеет специально созданную функцию для предотвращения этих атак. Все, что вам нужно сделать, это использовать полный набор функций, mysql_real_escape_string.

mysql_real_escape_string принимает строку, которая будет использоваться в запросе MySQL, и возвращает ту же строку, если все попытки внедрения SQL были безопасно завершены , По сути, он заменит те неприятные кавычки ('), которые пользователь может ввести, заменой, безопасной для MySQL, - экранированной кавычкой \'.

ПРИМЕЧАНИЕ. вы должны быть подключены к базе данных, чтобы использовать эту функцию!

//Подключаемся к MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

Вы можете найти более подробную информацию в MySQL - предотвращение инъекций SQL .

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0
  

Предупреждение о безопасности . Этот ответ не соответствует рекомендациям по безопасности. Экранирование не подходит для предотвратить внедрение SQL , вместо этого используйте подготовленные операторы . Используйте стратегию, изложенную ниже, на свой страх и риск. (Кроме того, mysql_real_escape_string() был удален в PHP 7.)

     

Устаревшее предупреждение : в настоящее время расширение mysql устарело. мы рекомендуем использовать расширение mysqli

Вы можете сделать что-то простое, как это:

$safe_variable = mysqli_real_escape_string($_POST["user-input"]);
mysqli_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Это не решит всех проблем, но это очень хорошая ступенька. Я упустил очевидные элементы, такие как проверка существования переменной, формат (цифры, буквы и т. Д.).

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Что бы вы ни делали в конечном итоге, убедитесь, что вы проверяли, что ваш ввод еще не был искажен magic_quotes или каким-либо другим источником -значит мусор, и, если необходимо, пропустите его через stripslashes или что-нибудь еще, чтобы очистить его.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Параметризованный запрос И проверка ввода - путь. Существует много сценариев, при которых может происходить внедрение SQL, даже если был использован mysql_real_escape_string().

Эти примеры уязвимы для внедрения SQL:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

или

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

В обоих случаях вы не можете использовать ' для защиты инкапсуляции.

Source : Неожиданное SQL-внедрение (когда нет выхода Достаточно)

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

На мой взгляд, лучший способ вообще предотвратить внедрение SQL в ваше PHP-приложение (или, если на то пошло, любое веб-приложение) - это подумать об архитектуре вашего приложения. Если единственный способ защиты от внедрения SQL-кода - это помнить, что нужно использовать специальный метод или функцию, которая выполняет правильные действия каждый раз, когда вы обращаетесь к базе данных, вы делаете это неправильно. Таким образом, это просто вопрос времени, пока вы не забудете правильно отформатировать запрос в какой-то момент кода.

Принятие шаблона MVC и структуры, такой как CakePHP или CodeIgniter , вероятно, правильный путь: общие задачи, такие как создание безопасных запросов к базе данных, были решены и централизованно реализованы в таких средах. Они помогают разумно организовать ваше веб-приложение и заставляют задуматься о загрузке и сохранении объектов, а не о безопасном построении отдельных SQL-запросов.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Я предпочитаю хранимые процедуры ( MySQL имеет поддержку хранимых процедур начиная с 5.0 ) с точки зрения безопасности - преимущества -

  1. Большинство баз данных (включая MySQL ) разрешить доступ пользователей к выполнению хранимых процедур. Детальное управление безопасным доступом полезно для предотвращения атаки на повышение привилегий. Это не позволяет скомпрометированным приложениям запускать SQL напрямую против базы данных.
  2. Они абстрагируют необработанный SQL-запрос от приложения, поэтому приложение получает меньше информации о структуре базы данных. Это затрудняет понимание людьми структуры базы данных и разработку подходящих атак.
  3. Они принимают только параметры, поэтому преимущества параметризованных запросов есть. Конечно - IMO вам все еще нужно санировать ввод - особенно если вы используете динамический SQL внутри хранимой процедуры.

Недостатки -

  1. Они (хранимые процедуры) сложны в обслуживании и имеют тенденцию очень быстро размножаться. Это делает управление ими проблемой.
  2. Они не очень подходят для динамических запросов - если они созданы для принятия динамического кода в качестве параметров, то многие преимущества сводятся на нет.
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Существует много способов предотвращения SQL-инъекций и других SQL-хаков. Вы можете легко найти его в Интернете (поиск Google). Конечно, PDO - одно из хороших решений. Но я хотел бы предложить вам несколько хороших способов предотвращения ссылок из SQL-инъекций.

Что такое внедрение SQL и как его предотвратить

руководство по PHP для внедрения SQL

Microsoft объяснение внедрения SQL и предотвращения в PHP

и некоторые другие, такие как Предотвращение внедрения SQL с MySQL и PHP

Теперь, зачем вам нужно предотвращать SQL-инъекцию вашего запроса?

Я хотел бы сообщить вам: почему мы пытаемся предотвратить внедрение SQL с помощью короткого примера ниже:

Запрос на совпадение аутентификации при входе в систему:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

Теперь, если кто-то (хакер) поставит

$_POST['email']= [email protected]' OR '1=1

и пароль ничего ....

Запрос будет обработан в системе только до:

$query="select * from users where email='[email protected]' OR '1=1';

Другая часть будет отброшена. Итак, что будет? Неавторизованный пользователь (хакер) сможет войти в систему как администратор, не имея своего пароля. Теперь он может делать все, что может делать администратор /электронная почта. Видите, это очень опасно, если SQL-инъекция не предотвращена.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Я думаю, что если кто-то захочет использовать PHP и MySQL или какой-либо другой сервер базы данных:

  1. Подумайте об обучении PDO (объекты данных PHP) - это уровень доступа к базе данных, обеспечивающий унифицированное метод доступа к нескольким базам данных.
  2. Подумайте об изучении MySQLi
  3. Используйте собственные функции PHP, например: strip_tags , mysql_real_escape_string или, если переменная числовая, просто (int)$foo. Подробнее о типе переменных в PHP читайте здесь . Если вы используете библиотеки, такие как PDO или MySQLi, всегда используйте PDO :: quote () и mysqli_real_escape_string () .

Примеры библиотек:

---- PDO

  

----- Заполнителей нет - созрели для внедрения SQL! Это плохо

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");
  

----- Безымянные заполнители

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);
  

----- Именованные заполнители

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

P.S

PDO выигрывает эту битву с легкостью. С поддержкой двенадцати различные драйверы базы данных и именованные параметры, мы можем игнорировать Небольшая потеря производительности, и привыкнуть к его API. Из безопасности точки зрения, они оба безопасны, пока разработчик использует их как они должны быть использованы

Но хотя PDO и MySQLi работают довольно быстро, MySQLi выполняет незначительно быстрее в тестах - ~ 2,5% для неподготовленных заявления и ~ 6,5% для подготовленных.

И, пожалуйста, протестируйте каждый запрос к вашей базе данных - это лучший способ предотвратить инъекцию.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Если возможно, приведите типы ваших параметров. Но он работает только с простыми типами, такими как int, bool и float.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Если вы хотите воспользоваться механизмами кэширования, такими как Redis или Memcached , возможно, DALMP может быть выбором. Он использует чистый MySQLi . Проверьте это: Уровень абстракции базы данных DALMP для MySQL с использованием PHP.

Кроме того, вы можете «подготовить» свои аргументы перед подготовкой запроса, чтобы вы могли создавать динамические запросы и в конце получить полностью подготовленный запрос операторов. Уровень абстракции базы данных DALMP для MySQL с использованием PHP.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Для тех, кто не знает, как использовать PDO (из функций mysql_), я сделал очень, очень простая оболочка PDO , которая представляет собой один файл. Он существует, чтобы показать, как легко выполнять все обычные задачи, которые необходимо выполнять приложениям. Работает с PostgreSQL, MySQL и SQLite.

По сути, прочитайте его , читая руководство , чтобы узнать, как использовать функции PDO для использования в реальной жизни. чтобы было проще хранить и получать значения в формате , который вы хотите.

  

Мне нужен один столбец

$count = DB::column('SELECT COUNT(*) FROM `user`);
     

Я хочу получить массив (ключ => значение) результатов (т. е. для создания окна выбора)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);
     

Я хочу получить результат в одной строке

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));
     

Мне нужен массив результатов

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Используя эту функцию PHP mysql_escape_string(), вы сможете быстро получить хорошую профилактику.

Например:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string - исключает строку для использования в mysql_query

Для большей профилактики вы можете добавить в конце ...

wHERE 1=1   or  LIMIT 1

Наконец, вы получите:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Несколько рекомендаций по экранированию специальных символов в операторах SQL.

Не используйте MySQL , это расширение устарело, используйте MySQLi или PDO .

MySQLi

Для ручного экранирования специальных символов в строке вы можете использовать mysqli_real_escape_string . Функция не будет работать должным образом, если правильный набор символов не установлен с помощью mysqli_set_charset .

Пример:

$mysqli = new mysqli( 'host', 'user', 'password', 'database' );
$mysqli->set_charset( 'charset');

$string = $mysqli->real_escape_string( $string );
$mysqli->query( "INSERT INTO table (column) VALUES ('$string')" );

Для автоматического экранирования значений с помощью подготовленных операторов используйте mysqli_prepare , и mysqli_stmt_bind_param , где типы для соответствующих переменных связывания должны быть предусмотрено для соответствующего преобразования:

Пример:

$stmt = $mysqli->prepare( "INSERT INTO table ( column1, column2 ) VALUES (?,?)" );

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();

Независимо от того, используете ли вы подготовленные операторы или mysqli_real_escape_string, вы всегда должны знать тип входных данных, с которыми вы работаете.

Поэтому, если вы используете подготовленный оператор, вы должны указать типы переменных для функции mysqli_stmt_bind_param.

И использование mysqli_real_escape_string для, как следует из названия, экранирования специальных символов в строке, так что это не сделает целые числа безопасными. Цель этой функции - предотвратить разрыв строк в операторах SQL и повреждение базы данных, которое она может вызвать. mysqli_real_escape_string - полезная функция при правильном использовании, особенно в сочетании с sprintf.

Пример:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Простая альтернатива этой проблеме может быть решена путем предоставления соответствующих разрешений в самой базе данных. Например: если вы используете базу данных MySQL, войдите в базу данных через терминал или предоставленный пользовательский интерфейс и просто следуйте этой команде:

 GRANT SELECT, INSERT, DELETE ON database TO [email protected]'localhost' IDENTIFIED BY 'password';

Это ограничит пользователя только ограниченным указанным запросом. Удалите разрешение на удаление, чтобы данные никогда не удалялись из запроса, запущенного со страницы PHP. Второе, что нужно сделать, это сбросить привилегии, чтобы MySQL обновил разрешения и обновления.

FLUSH PRIVILEGES; 

дополнительная информация о флеш .

Чтобы увидеть текущие привилегии для пользователя, запустите следующий запрос.

select * from mysql.user where User='username';

Подробнее о GRANT .

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Я использую три различных способа предотвращения уязвимости моего веб-приложения для внедрения SQL-кода.

  1. Использование mysql_real_escape_string(), который является предопределенной функцией в PHP , и этот код добавляет обратную косую черту к следующим символам: \x00, \n, \r, \, ', " и \x1a. Передайте входные значения в качестве параметров, чтобы минимизировать вероятность внедрения SQL.
  2. Самый продвинутый способ - использовать PDO.

Надеюсь, это поможет вам.

Рассмотрим следующий запрос:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string () не будет защищать здесь. Если вы используете одинарные кавычки ('') вокруг переменных внутри запроса, это то, что защищает вас от этого. Вот решение для этого ниже:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

На этот вопрос есть несколько хороших ответов о это.

Я предлагаю использовать PDO - лучший вариант.

Edit:

mysql_real_escape_string() устарело с версии PHP 5.5.0. Используйте либо mysqli, либо PDO.

Альтернативой mysql_real_escape_string () является

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

Пример:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Простой способ - использовать среду PHP, например CodeIgniter или Laravel со встроенными функциями, такими как фильтрация и активная запись, так что вам не придется беспокоиться об этих нюансах.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

** Предупреждение: подход, описанный в этом ответе, применим только к очень конкретным сценариям и небезопасен, поскольку атаки с использованием SQL-инъекций не зависят только от возможности внедрения X=Y. **

Если злоумышленники попытаются взломать форму с помощью переменной PHP $_GET или строки запроса URL-адреса, вы сможете поймать их, если они не в безопасности.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

Потому что 1=1, 2=2, 1=2, 2=1, 1+1=2 и т. Д. - это общие вопросы для базы данных SQL злоумышленника. Возможно, он также используется многими хакерскими приложениями.

Но вы должны быть осторожны, чтобы не переписывать безопасный запрос со своего сайта. Приведенный выше код дает вам подсказку переписать или перенаправить (это зависит от вас) эту динамическую строку запроса, относящуюся к взлому, на страницу, которая будет хранить IP-адрес или ДАЖЕ ИХ КУКИ, историю, браузер или любую другую конфиденциальную информацию, так что вы можете иметь дело с ними позже, заблокировав их аккаунт или связаться с властями.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Существует множество ответов для PHP и MySQL , но здесь приведен код для PHP и Oracle для предотвращения внедрения SQL, а также регулярного использования драйверов oci8:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);
ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Хорошей идеей является использование 'объектно-реляционного картографа' , например Idiorm :

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

Это спасает не только от SQL-инъекций, но и от синтаксических ошибок! Также поддерживает коллекции моделей с цепочкой методов для фильтрации или применения действий к нескольким результатам одновременно и нескольким соединениям.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Использование PDO и MYSQLi - это хорошая практика для предотвращения SQL-инъекций, но если вы действительно хотите работать с функциями и запросами MySQL, было бы лучше использовать

mysql_real_escape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

Существует больше возможностей предотвратить это: например, определить - если входные данные представляют собой строку, число, символ или массив, существует так много встроенных функций, чтобы обнаружить это. Кроме того, было бы лучше использовать эти функции для проверки входных данных.

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

И гораздо лучше использовать эти функции для проверки входных данных с помощью mysql_real_escape_string.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37
0

Я написал эту маленькую функцию несколько лет назад:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

Это позволяет запускать операторы в однострочном C # -ish String.Format, например:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

Он избегает, учитывая тип переменной. Если вы попытаетесь параметризировать имена таблиц и столбцов, произойдет сбой, поскольку каждая строка будет заключена в кавычки, что является недопустимым синтаксисом.

ОБНОВЛЕНИЕ БЕЗОПАСНОСТИ: Предыдущая версия str_replace допускала инъекции путем добавления токенов {#} в пользовательские данные. Эта preg_replace_callback версия не вызывает проблем, если замена содержит эти токены.

ответил H S Progr 1 J000000Sunday18 2018, 21:52:37

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132