Отже, спочатку розберемося, що ми захищаємо, а точніше — від кого. На початку цього рівня ми вже згадували SQL-інʼєкції — це одні з найпопулярніших і найруйнівніших атак на бази даних. Атака працює так: зловмисник відправляє шкідливий SQL-код у твій запит, щоб "обдурити" його і отримати доступ до даних, до яких він не повинен мати доступ. Тепер розберемося з ними детальніше.
Приклад SQL-інʼєкції
Уяви, що у тебе є веб-додаток із простим вводом імені користувача та пароля. Бекенд виконує SQL-запит, щоб перевірити, чи є користувач у базі даних:
SELECT * FROM users WHERE username = 'admin' AND password = 'password123';
Цей запит виглядає чудово... поки користувач вводить коректні дані. Але що буде, якщо хтось введе:
- Імʼя користувача:
admin' -- - Пароль: (залишимо це поле порожнім)
У підсумку запит перетвориться на щось таке:
SELECT * FROM users WHERE username = 'admin' -- AND password = 'password123';
Зверни увагу на символи --, які в SQL означають початок коментаря. Все, що йде після них, ігнорується. В результаті перевірка пароля просто пропускається, і зловмисник отримує доступ як адміністратор!
Потенційні наслідки SQL-інʼєкцій
SQL-інʼєкція може призвести до жахливих наслідків:
- Отримання несанкціонованого доступу до даних. Наприклад, зловмисник може отримати доступ до конфіденційних даних, типу паролів користувачів.
- Видалення або модифікація даних. Хтось може видалити всю таблицю або влаштувати там хаос.
- Виконання довільного SQL-коду. Уяви, що зловмисник запускає команду
DROP DATABASE... Так, звучить як нічний жах.
Але ми не з лякливих! PostgreSQL дає купу інструментів, які допоможуть захиститися від таких атак.
Як захиститися? Методи запобігання SQL-інʼєкціям
- Використання підготовлених запитів (
PREPAREіEXECUTE)
Підготовлені запити (prepared statements) — це як перевірений рецепт для твого SQL-коду. Вони працюють так: ти "підготовлюєш" SQL-запит один раз, а потім передаєш дані окремо. Це робить неможливим впровадження шкідливого коду.
Ось приклад правильного підходу:
Підготуй запит, використовуючи PREPARE.
PREPARE user_login (text, text) AS
SELECT *
FROM users
WHERE username = $1 AND password = $2;
Запит із двома параметрами $1 і $2 чекає, що реальні значення будуть передані пізніше.
Використовуй EXECUTE для виконання запиту.
EXECUTE user_login('admin', 'password123');
При цьому PostgreSQL автоматично екранує всі користувацькі дані, не даючи впровадити шкідливий SQL-код.
Переваги підходу:
- Неможливо виконати SQL-інʼєкцію, бо параметри обробляються як прості дані, а не як частина SQL-коду.
- Запити стають безпечнішими і швидше виконуються завдяки кешуванню плану виконання.
- Параметризовані запити (Parameterized Queries)
Цей метод особливо популярний у додатках, написаних на мовах програмування, таких як Python чи Java. Замість ручної роботи з PREPARE і EXECUTE ти можеш використовувати бібліотеки або ORM, які автоматично обробляють параметри.
Приклад із Python і бібліотекою psycopg2:
import psycopg2
connection = psycopg2.connect(
dbname="your_db",
user="your_user",
password="your_password",
host="localhost",
port="5432"
)
cursor = connection.cursor()
# Використання параметризованого запиту
username = "admin"
password = "password123"
query = "SELECT * FROM users WHERE username = %s AND password = %s"
cursor.execute(query, (username, password))
# Твої дані повністю захищені!
result = cursor.fetchall()
print(result)
Зверни увагу на %s у SQL-запиті — це місце, куди вставляється параметр. Бібліотека psycopg2 дбає про безпечну передачу даних.
- Перевірка вхідних даних
Якщо ти передаєш дані, введені користувачем, переконайся, що вони відповідають твоїм очікуванням. Наприклад:
- Для текстових даних використовуй регулярні вирази, щоб переконатися, що вони не містять заборонених символів.
- Для числових даних переконайся, що це дійсно числа.
Приклад у Python:
import re
username = input("Введи імʼя користувача: ")
# Дозволяємо тільки літери, цифри та підкреслення
if re.match(r"^\w+$", username):
print("Імʼя користувача коректне")
else:
print("Небезпечне імʼя користувача!")
- Використання мінімальних привілеїв
Подбай, щоб ролі, які використовуються для виконання запитів, мали мінімально необхідні привілеї. Наприклад, не давай ролі доступу до команд DROP TABLE чи ALTER TABLE, якщо це не потрібно.
- Логування підозрілих дій
Ти можеш використовувати параметри PostgreSQL для відстеження активності користувачів:
log_statement = 'all'— логування всіх запитів.log_connections = on— логування всіх підключень до бази даних.
Ці параметри допомагають виявити потенційно шкідливі дії.
Приклади реалізації
Приклад 1: Використання підготовленого запиту через SQL
-- Створимо підготовлений запит
PREPARE check_credentials (text, text) AS
SELECT * FROM users WHERE username = $1 AND password = $2;
-- Виконаємо запит із безпечними параметрами
EXECUTE check_credentials('admin', 'password123');
Приклад 2: Параметризований запит у Python
query = "UPDATE users SET last_login = NOW() WHERE username = %s"
username = "admin"
cursor.execute(query, (username,))
Рекомендації з безпеки
Завжди перевіряй вхідні дані. Ніколи не довіряй даним, які прийшли від користувача.
Використовуй тільки підготовлені або параметризовані запити. Ці методи — твій головний барʼєр від SQL-інʼєкцій.
Призначай ролі з мінімально необхідними привілеями. Це мінімізує шкоду, якщо зловмисник отримає доступ.
Налаштуй логування і регулярно перевіряй логи. Будь у курсі активності у своїй базі даних.
SQL-інʼєкції — це, звісно, страшний звір, але якщо використовувати підготовлені запити, параметризовані запити і дотримуватися хороших практик, ти захистиш свою базу даних і зможеш спокійно спати вночі, не боячись, що хтось "випадково" зітре всі твої таблиці. PostgreSQL дає тобі всі інструменти для цього, тож вперед!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ