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

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

(define (factorial x acc)
  (if (zero? x)
      acc
      (factorial (sub1 x) (* x acc))))

Вышеупомянутая процедура будет изначально вызываться с помощью 1 в качестве аккумулятора, например:

(factorial 10 1)
=> 3628800

Обратите внимание, что накопленное значение возвращается, когда базовый случай достигнут, и что параметр acc обновляется в каждой точке рекурсивного вызова. Мне пришлось добавить один дополнительный параметр в процедуру, но этого можно избежать, указав внутреннюю процедуру или имя let, например:

(define (factorial x)
  (let loop ((x x)
             (acc 1))
    (if (zero? x)
        acc
        (loop (sub1 x) (* x acc)))))
2776
задан 44 revs, 36 users 14% 1 October 2016 в 08:08
поделиться

7 ответов

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

у Вас в основном есть две опции достигнуть этого:

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

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // Do something with $row
    }
    
  2. Используя [1 122] 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 является универсальной опцией.

Correctly, настраивающий соединение

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

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

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

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

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

, Хотя можно установить charset в опциях конструктора, важно отметить, что 'более старые' версии PHP (перед 5.3.6) тихо проигнорировали параметр набора символов в DSN.

Объяснение

SQL-оператор Вы передаете [1 114], анализируется и компилируется сервером базы данных. Путем определения параметров (или ? или именованного параметра как [1 116] в примере выше) Вы говорите механизм базы данных, где Вы хотите отфильтровать на. Тогда, когда Вы звоните execute, подготовленный оператор объединен со значениями параметров, которые Вы определяете.

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

Любые параметры Вы отправляете, когда использование подготовленного оператора будут просто рассматривать как строки (хотя механизм базы данных может сделать некоторую оптимизацию, таким образом, параметры могут закончиться как числа также, конечно). В примере выше, если бы $name переменная содержит 'Sarah'; DELETE FROM employees, результатом просто был бы поиск строки "'Sarah'; DELETE FROM employees", и Вы не закончите с [1 124] пустая таблица .

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

, О, и так как Вы спросили о том, как сделать это для вставки, вот пример (использующий 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';
}
8612
ответ дан 41 revs, 24 users 19% 1 October 2016 в 08:08
поделиться
  • 1
    @o.v. Я дал этому ответу upvote, don' t беспокойство. Дополнительная информация в комментарии была бы полезна мне как бы то ни было. – iwein 26 September 2013 в 08:18

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

Не только делает это защищает от Внедрения SQL, но и оно также ускоряет запросы.

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

823
ответ дан 5 revs, 5 users 35% 1 October 2016 в 08:08
поделиться

Предупреждение устаревшее: пример кода Этого ответа (как пример кода вопроса) использует PHP's MySQL расширение, которое было удержано от использования в PHP 5.5.0 и удалено полностью в PHP 7.0.0.

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

Если Вы используете последнюю версию 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 подход, Вы пострадаете от проблемы, описанной Многочленом в комментариях ниже. Этот случай более хитер, потому что целые числа не были бы окружены кавычками, таким образом, Вы могли иметь дело с путем проверки, что ввод данных пользователем содержит только цифры.
  • Существуют вероятные другие случаи, о которых я не знаю. Вы могли бы найти, что это - полезный ресурс на некоторых более тонких проблемах, с которыми можно встретиться.
1622
ответ дан 19 revs, 14 users 71% 1 October 2016 в 08:08
поделиться

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

($conn PDO объект)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();
611
ответ дан Imran 1 October 2016 в 08:08
поделиться
  • 1
    Я думаю, что это отвечает когда , не почему – Henk Holterman 29 May 2011 в 17:35

Вы могли сделать что-то основное как это:

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

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

456
ответ дан 10 revs, 9 users 38% 1 October 2016 в 08:08
поделиться

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

376
ответ дан 2 revs, 2 users 67% 1 October 2016 в 08:08
поделиться
  • 1
    Человек спасибо, didn' t означают придираться к мелочам, поскольку я не хорош в английском языке как Вы: O – Joan Venge 30 May 2011 в 17:15

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

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

Недостатки -

  1. Их (хранимые процедуры) сложно поддерживать и они имеют тенденцию очень быстро размножаться. Это делает управление ими проблемой.
  2. Они не очень подходят для динамических запросов - если они построены так, чтобы принимать динамический код в качестве параметров, то многие преимущества сводятся на нет.
294
ответ дан 22 November 2019 в 19:51
поделиться
Другие вопросы по тегам:

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