Предотвращение SQL-инъекций

in #ru3 years ago (edited)

sql-injection.jpg

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

Атаки SQL-инъекций позволяют злоумышленникам выполнять произвольные запросы или команды к базе данных. Разработчики вносят уязвимости в свой код, когда конкатенируют или подставляют пользовательский ввод в элементы SQL-запроса. В следующем примере на языке Python программа принимает любой ввод в качестве переменной user_id (возвращаемой как часть login_data) и добавляет её в конец строки, которая впоследствии выполняется как sql-запрос:

login_data = web.input()
query_string = "SELECT * FROM USERS WHERE ID = '%s'" %
    login_data.user_id
cursor.execute(query_string)


Если пользователь введёт carlos, запрос выберет запись с идентификатором carlos. Если пользователь введёт ' или 1=1, запрос вернёт все строки в таблице USERS. Если он добавит ;DROP TABLE USERS, это приведёт к удалению таблицы USERS. Данная атака возможна потому, что исполняемый оператор SQL и часть данных запроса являются строками, и разработчики смешивают их вместе так, что у SQL-сервера нет возможности отличить их друг от друга.

Предотвращение SQL-инъекций

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

login_data = web.input()
query_string = "SELECT * FROM USERS WHERE ID = ?"
cursor.execute(query_string, (login_data.user_id))


Здесь ? — это место для ввода пользователем. Когда вызывается cursor.execute(), программист передаёт строку запроса и параметр user_id как два разных элемента.

Чтобы избежать SQL-инъекций и других атак (например, XSS), разработчики должны также фильтровать вводимые пользователем данные. Фильтрация ввода помогает предотвратить все атаки, основанные на неправильном вводе, а не только SQL-инъекции. Это также является примером «защиты в глубину» (Defense in Depth). Фильтрация ввода и параметризованные запросы являются дополнительными мерами защиты от SQL-инъекций.

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

login_data = web.input()
if not re.match(r '^[\w.]{3,32}+$', login_data.user_id):
    some_error_thingy()
query_string = "SELECT * FROM USERS WHERE ID = ?"
cursor.execute(query_string, (login_data.user_id))


^ и $ соответствуют началу и концу строки. \w является сокращением для [0-9a-zA-z_], а {3,32} определяет минимальную и максимальную длину в 3 и 32 соответственно.

Очень важно использовать параметризованные запросы AND проверку ввода. Параметризованные запросы не предотвращают все SQL-инъекции. Сообщение на Y Combinator от Томаса Птачека (Thomas Ptacek):

В частности, вы пишете: «Параметризованные запросы — лучший способ решения проблемы, потому что он не требует экранирования». Это неверно. Большинство протоколов баз данных позволяют привязывать данные к запросу, но не ключевые слова или даже ограничения и смещения. Целое поколение программистов было убеждено, что использование параметризованных запросов защищает их от SQL-инъекций, в то время как написание кода пагинации или сортируемых таблиц тривиально подвержено инъекциям.

Для руководителей

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

Если у вас нет достаточных знаний в области безопасности, вы можете поручить кому-то из своих разработчиков провести исследование. Если у вас есть деньги, наймите консультантов. Им тоже нужно кушать :)

Источник: Basics: Avoiding SQL Injection