Как стать автором
Обновить

Комментарии 24

Bind variables are the best way to prevent SQL injection.
способов предотвращения SQL-инъекций два:

• не использовать динамические запросы к базе;

• не использовать пользовательские данные.

Есть еще один — использовать хранимые процедуры и отделить данные от логики

Без динамических запросов иногда бывает довольно сложно обойтись. С процедурами тоже не всё гладко, но есть еще один способ – это использование Query Builder-ов. Идею я недавно описал в этой статье на Хабре (так же есть пример реализации для .Net)

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

Хранимые процедуры сами по себе ни от чего не защищают. Вообще эта мантра ничем не лучше пресловутого "Escaping All User Supplied Input", поскольку должна сопровождаться длинным пояснительным текстом, что защищают только тогда, когда параметры процедуры подставляются в запрос в виде переменных, а не участвуют в ручной сборке запроса. Т.е.


 INSERT INTO t (a,b,c,d) VALUES (@a, @b, @c, @d);

безопасно, а если там что-то вроде


SET @sql=CONCAT('SELECT * FROM t WHERE f=\'',@a,'\'');

То разумеется никакая процедура не поможет.


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

А так будет безопасно?

// Postgres, Laravel
$bindings = [
'str' => $_GET['name'];
];
$query = 
'SELECT id, name FROM animals WHERE name like \'%\' || lower(:str) || \'%\' ';

$result = $connection->select($query, $bindings); 

:str называется плейсхолдером или параметром и используется в подготовленном выражении (prepared statement), о котором говорится выше. :str здесь замещает актуальные данные в запросе и таким образом $_GET['name'] не может повлиять на запрос и вызвать ошибку или инъекцию. Это безопасно и как раз является рекомендуемым способом работы с динамическим SQL.


Только код в запросе какой-то очень уж замороченный. Я бы добавил проценты в РНР и сделал


$bindings = [
    'str' => "%{$_GET['name']}%";
];
$query = 'SELECT id, name FROM animals WHERE name like lower(:str)';

так, мне кажется, получается читабельнее. Хотя дело вкуса.

Что касается примера — да ваш вариант мне симпатичней.

А, что до плейсхолдеров, понятно.
Получается, что вместо всех этих правил:
способов предотвращения SQL-инъекций два:
• не использовать динамические запросы к базе;
• не использовать пользовательские данные.

Учите матчасть:
Primary Defenses:
Option 1: Use of Prepared Statements (with Parameterized Queries)
Option 2: Use of Stored Procedures
Option 3: Whitelist Input Validation
Option 4: Escaping All User Supplied Input
Additional Defenses:
Also: Enforcing Least Privilege
Also: Performing Whitelist Input Validation as a Secondary Defense


Достаточно одного правила — используйте плейсхолдеры/параметры. По английски говорят Bind variables.

Как я уже писал в другом комментарии, Bind variables работает только для данных. Если нужно сделать order by :column, то такой запрос выдаст ошибку.


И в этом случае надо применять Option 3: Whitelist Input Validation. У Лары как раз с этим была проблема до недавнего времени — данные защищались, а имена полей вообще никак не проверялись, и пропускали инъекцию.

Понятно, спасибо.

Статьи подобного содержания видел ещё в 2007 году. Не очень понимаю что автор хочет нового рассказать?

В 2007 году у него не было аккаунта который надо попиарить

способов предотвращения SQL-инъекций два:
• не использовать динамические запросы к базе;
• не использовать пользовательские данные.

Учите матчасть:
Primary Defenses:
Option 1: Use of Prepared Statements (with Parameterized Queries)
Option 2: Use of Stored Procedures
Option 3: Whitelist Input Validation
Option 4: Escaping All User Supplied Input
Additional Defenses:
Also: Enforcing Least Privilege
Also: Performing Whitelist Input Validation as a Secondary Defense

cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html

А если везде используются только prepared statement (которые вроде бы как и работают быстрее, чем вручную сгенерированные с прописанными в теле запроса за'escape'ными данными), то все остальное (option 2-4) все еще нужно? Или одних prepared statement'ов будет достаточно?
На уровне базы данных могут не поддерживаются привязанные переменные, поэтому необходимо использовать другие варианты.

Вот примеры с PHP: www.php.net/manual/ru/security.database.sql-injection.php
Но меня интересовал тот самый случай, когда изначально была выбрана база данных, поддерживающая prepared statements (кажется, большинство используемых многопользовательских реляционных баз данных такое умеют). А то получается как в этом диалоге:

— Но если я сегда запираю дверь на ключ, мне все еще нужно забивать ее гвоздями?
— Не ко всякой двери можно замок приделать.

П.3 нужен если работаем не с данными, а с другими частями запроса, например именами полей.
Остальное в этом списке — чушь собачья, особенно п.4, который сам по себе адъ и позор.

Хорошая и полезная памятка, но и она не гарантирует абсолютной защиты. В этом абзаце мы с юмором подошли к вопросу о том, что универсального способа защиты от подобного рода уязвимостей не существует. Это подтверждается OWASP Top 10, где инъекции до сих пор находятся на позиции А1. Программисты, разрабатывая веб-приложения, могут использовать потенциально небезопасный сторонний код или модули. Поэтому дополнительно необходимо проводить регулярный аудит веб-приложения и использовать средства защиты, например, WAF. Последние, кстати, довольно хорошо себя показывают, когда дело доходит до защиты веб-приложений от подобного рода атак.

Гарантирует, если не метаться между наваленными в кучу пунктами, а использовать 1 и 3 для данных и идентификаторов/операторов соответственно.


А то что инъекции в топ 10 — это только благодаря статьям в интернете, которые рассказывают что защиты нет и надо обязательно заказывать аудит.

Glad to see the GUI on Russian :), I hope there's not many mistakes on the translation.

To do injection on Cookie you'll need to click on Header label under GET and POST, it'll be underlined and will display that the injection will be run on Header parameters instead of on GET parameters.
Самый простой способ использовать intval() для чисел
" where id=".intval($_GET['number'])

Самый надежный — фильтровать подозрительные строки еще до того, как они попадут в программу. Это можно делать через WAF web application firewall.

Вот еще одна жертва некачественных статей.
"фильтрация" — это не "самый надёжный способ" а просто адова ересь.
Скажи, ты смог бы свой комментарий здесь оставить, если бы "подозрительные строки" в нем были "отфильтрованы"?
Не говоря уже о том что все фильтры — это по определению черные списки, который никогда не гарантируют безопасность, в отличие от белых.
Не говоря уже о том, что есть инъекции второго порядка
Не говоря о том, что точек входа в программу может быть несколько, и все не зафильтруешь.

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