Що таке SQL Injection і як захистити запити

Автор: Опубліковано

Коротко: SQL Injection — це вразливість, яка дає змогу змінити дію запиту до бази даних за допомогою спеціально підготовлених вхідних даних. Вона виникає насамперед тоді, коли застосунок поєднує команди SQL із текстом, який вводить користувач. Основний засіб захисту — параметризовані запити, які відокремлюють текст команди від її даних.

Як дані користувача стають командою

SQL — це мова для читання та зміни даних у реляційних базах даних. Застосунок може за її допомогою знайти обліковий запис, отримати замовлення або зберегти нову адресу клієнта. SQL Injection, тобто ін’єкція SQL, виникає, коли вхідні дані сприймаються як частина синтаксису такої команди.

Припустімо, застосунок шукає обліковий запис за логіном. Розробник пише початок запиту, додає до нього значення з форми й завершує його апострофом. Звичайний логін дає очікуваний результат, але текст з апострофом і додатковою умовою може змінити зміст усього пошуку.

Джерелом небезпечного значення не обов’язково є форма входу. Ним також можуть бути параметр URL-адреси, фільтр товарів, заголовок запиту або дані, раніше збережені в базі. Важливо те, як застосунок використовує це значення, а не звідки він його отримав.

Простий експеримент у Linux

Наведений нижче приклад працює лише з тимчасовою базою в пам’яті процесу. Він не підключається до жодного сайту чи зовнішнього сервера. Приклад демонструє читання зайвих записів, а не повноцінний механізм входу.

Вам потрібен Python 3 з доступним модулем sqlite3. Перевірити його наявність можна командою в терміналі:

python3 -c "import sqlite3; print(sqlite3.sqlite_version)"

Якщо команда не спрацює, перегляньте в документації свого дистрибутива, як установити Python 3 або відсутній модуль. Запускати окремий сервер бази даних не потрібно.

Виконайте в терміналі весь блок:

python3 - <<'PY'
import sqlite3

ziednannia = sqlite3.connect(":memory:")
polaczenie.execute(
    "CREATE TABLE oblikovi_zapysy (id INTEGER PRIMARY KEY, login TEXT)"
)
polaczenie.executemany(
    "INSERT INTO oblikovi_zapysy (login) VALUES (?)",
    [("hanna",), ("petro",)]
)

znachennia = "' OR 1=1 -- "
zapyt = "SELECT id, login FROM oblikovi_zapysy WHERE login = '" + znachennia + "'"

print("Запит:", zapyt)
print("Результат:", polaczenie.execute(zapyt).fetchall())
polaczenie.close()
PY

Результат містить обидва облікові записи, хоча жоден із них не має логіна, що відповідає переданому значенню. Апостроф завершує текст логіна, OR 1=1 додає завжди істинну умову, а -- починає коментар, тому кінцевий апостроф уже не впливає на запит.

Цей код навмисно містить вразливість для локальної демонстрації. Не перевіряйте подібні вхідні дані на чужих сервісах без явного дозволу, що визначає обсяг тестування.

Як виправити цей код

Параметризований запит містить позначки місць для даних, а самі значення передаються окремо до бібліотеки для роботи з базою. У модулі sqlite3 такою позначкою може бути ?. Бібліотека обробляє передане значення як дані, а не як додатковий синтаксис SQL.

У попередньому прикладі замініть побудову та виконання запиту цим фрагментом, залишивши його перед polaczenie.close():

zapyt = "SELECT id, login FROM oblikovi_zapysy WHERE login = ?"
rezultat = polaczenie.execute(zapyt, (znachennia,)).fetchall()
print("Результат:", rezultat)

Тепер ви отримаєте порожній список: база шукає логін, який буквально містить увесь переданий текст. Якщо замість znachennia підставити "hanna", запит поверне лише обліковий запис Ганни. Кома в (znachennia,) створює кортеж з одного елемента, якого потребує цей спосіб передавання параметрів.

Не замінюйте параметри ручним видаленням апострофів або форматуванням тексту, наприклад через f-рядок. Логін з апострофом може бути коректним значенням; саме застосунок має безпечно його обробити. Синтаксис параметрів залежить від бібліотеки, тому шаблон ? не варто без перевірки переносити до кожного драйвера.

Якими можуть бути наслідки вразливості

SQL Injection може дати змогу читати чужі дані, обходити частину перевірок доступу, змінювати або видаляти записи. Фактичні наслідки залежать від запиту, рушія бази даних і прав облікового запису, який використовує застосунок. Саме виявлення вразливості не означає автоматичного захоплення всього сервера.

Відсутність видимої помилки також не доводить безпечності. Застосунок може приховувати повідомлення бази, а зміни в його роботі можуть проявлятися лише у відмінностях між відповідями. Водночас одинична помилка після введення апострофа — це привід для аналізу, а не самодостатній доказ SQL Injection.

Що перевірити у власному застосунку

Почніть перевірку з місць, де формуються запити SQL:

Для початку виберіть один пошук, що використовує дані користувача. Додайте тест зі звичайним значенням і текстом з апострофом та перевірте, чи обидва вхідні значення залишаються даними, а не змінюють структуру запиту.

Дивіться також: Що таке ETF?

Читайте також

Дізнайтеся, як PHP-FPM обробляє запити PHP, чим відрізняється від вебсервера та як налаштувати процеси під трафік і ресурси хостингу.
Дізнайтеся, як BIND обслуговує DNS, чим відрізняються його серверні ролі та коли варто розгортати його самостійно замість керованої послуги.
Дізнайтеся, що означають польський реєстр і номер REGON, чим вони відрізняються від NIP та KRS і як перевірити дані підприємства.