Чтобы функция была рекурсивной с хвостом, после возвращения функции не должно быть ничего общего, кроме как вернуть ее значение. То есть последнее, что происходит на рекурсивном шаге, - это вызов самой функции. Это обычно достигается с помощью параметра аккумулятора для отслеживания ответа:
(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)))))
Использование подготовило операторы и параметризированные запросы. Это SQL-операторы, которые отправляются в и анализируются сервером базы данных отдельно от любых параметров. Таким образом, для взломщика невозможно ввести злонамеренный SQL.
у Вас в основном есть две опции достигнуть этого:
Используя [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
}
Используя [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 является универсальной опцией.
Примечание, что при использовании 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';
}
Я рекомендовал бы использовать PDO (Объекты данных PHP) для выполнения параметризованных SQL-запросов.
Не только делает это защищает от Внедрения SQL, но и оно также ускоряет запросы.
И при помощи PDO, а не mysql_
, mysqli_
, и pgsql_
функции, Вы подаете свою заявку, немного более абстрактную от базы данных в редком возникновении, что необходимо переключить поставщиков БД.
Предупреждение устаревшее: пример кода Этого ответа (как пример кода вопроса) использует 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.
Обратите внимание на то, что случай, о котором Вы спросили, является довольно простым и что более сложные случаи могут потребовать более сложных подходов. В особенности:
mysql_real_escape_string
. В этом виде случая Вы были бы более обеспеченной передачей входа пользователя через белый список, чтобы гарантировать, что только 'безопасные' значения позволяются через.mysql_real_escape_string
подход, Вы пострадаете от проблемы, описанной Многочленом в комментариях ниже. Этот случай более хитер, потому что целые числа не были бы окружены кавычками, таким образом, Вы могли иметь дело с путем проверки, что ввод данных пользователем содержит только цифры. Используйте PDO
и подготовленные запросы.
($conn
PDO
объект)
$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();
Вы могли сделать что-то основное как это:
$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
Это не решит каждую проблему, но это - очень хорошая стартовая площадка. Я не учел очевидные объекты, такие как проверка существования переменной, формат (числа, буквы, и т.д.).
Независимо от того, что Вы действительно заканчиваете тем, что использовали, удостоверьтесь, что Вы проверяете, что Ваш вход не был уже искажен magic_quotes
или некоторый другой действующий из лучших побуждений мусор, и при необходимости, выполнял его до stripslashes
или безотносительно санировать его.
Я предпочитаю хранимые процедуры ( MySQL поддерживает хранимые процедуры с версии 5.0 ) с точки зрения безопасности - преимущества -
Недостатки -