Как реализовать массовую вставку параметров в SQL-запрос (placeholders) для списков ников, SteamID или UserID, чтобы избежать инъекций?
Массовая параметризация SQL-запросов — ключевой приём защиты от SQL-инъекций при работе со списками значений (ники, SteamID, UserID). Нельзя просто склеивать строки через конкатенацию — это открывает уязвимость. Правильный подход — динамически генерировать плейсхолдеры и передавать значения отдельным списком параметров.
**Python (sqlite3 / psycopg2)**
Для sqlite3 используй знак вопроса как плейсхолдер:
python
user_ids = [1, 2, 3, 42]
placeholders = ‘,’.join(‘?’ * len(user_ids))
query = f’SELECT * FROM users WHERE user_id IN ({placeholders})’
cursor.execute(query, user_ids)
Для psycopg2 (PostgreSQL) используй `%s`:
python
from psycopg2.extras import execute_values
nicknames = [‘admin’, ‘player1’, ‘valve_bot’]
placeholders = ‘,’.join([‘%s’] * len(nicknames))
cursor.execute(f’SELECT * FROM users WHERE nickname IN ({placeholders})’, nicknames)
**PHP (PDO)**
php
$steamIds = [‘76561198000000001’, ‘76561198000000002’];
$placeholders = implode(‘,’, array_fill(0, count($steamIds), ‘?’));
$stmt = $pdo->prepare(«SELECT * FROM users WHERE steam_id IN ($placeholders)»);
$stmt->execute($steamIds);
**C# (Dapper / ADO.NET)**
Dapper поддерживает передачу коллекций напрямую:
csharp
var userIds = new[] { 1, 2, 3 };
var result = connection.Query(«SELECT * FROM users WHERE user_id IN @Ids», new { Ids = userIds });
В чистом ADO.NET нужно генерировать параметры вручную:
csharp
var ids = new[] { 1, 2, 3 };
var paramNames = ids.Select((id, i) => $»@p{i}»).ToArray();
var query = $»SELECT * FROM users WHERE user_id IN ({string.Join(«,», paramNames)})»;
var cmd = new SqlCommand(query, connection);
for (int i = 0; i < ids.Length; i++)
cmd.Parameters.AddWithValue($"@p{i}", ids[i]);
**Важные правила безопасности:**
1. **Никогда не вставляй значения через строковую конкатенацию** — даже если они «кажутся» числами.
2. **Проверяй длину списка** — пустой список (`IN ()`) вызовет синтаксическую ошибку в большинстве СУБД. Добавляй проверку `if not user_ids: return []`.
3. **Ограничивай размер списка** — слишком большой IN-список может деградировать производительность. При тысячах значений используй временные таблицы или `JOIN`.
4. **Типизируй данные** — SteamID лучше хранить как BIGINT, а не VARCHAR, чтобы исключить инъекции через тип.
5. **Используй ORM с умом** — SQLAlchemy, Hibernate, EF Core автоматически параметризуют запросы, но при сыром SQL (`raw`) правила те же.
Такой подход полностью исключает SQL-инъекции через подставляемые списки и работает с любыми данными: ники с кавычками, SteamID в виде длинных чисел, UUID пользователей.
