~/VibeHandbook
PDF бесплатно

Глава 18 · 02

Инъекции: когда ввод становится кодом

Инъекция — самый старый и по-прежнему самый частый серьёзный баг. Он случается, когда данные от пользователя начинают трактоваться как инструкции вместо данных. Для большинства приложений важны две разновидности.

injection — SQL (Structured Query Language) — это язык, на котором приложения просят у базы данных данные — это когда пользовательский ввод попадает в запрос к базе. Классический уязвимый паттерн:

// VULNERABLE: ввод пользователя вклеивается прямо в запрос
app.get("/user", (req, res) => {
  const name = req.query.name;
  db.query(`SELECT * FROM users WHERE name = '${name}'`);
});
// Если кто-то передаст  name = '; DROP TABLE users; --
// ваш запрос превратится в команду на удаление таблицы.

Исправление — это параметризованный запрос (его ещё называют prepared statement). Вы отправляете запрос и данные раздельно, чтобы база никогда не путала одно с другим:

// SAFE: значение передаётся как параметр, а не как код
app.get("/user", (req, res) => {
  const name = req.query.name;
  db.query("SELECT * FROM users WHERE name = ?", [name]);
});

Правило: никогда не собирайте запрос конкатенацией строк. Если в диффе вы видите обратные кавычки или +, склеивающие SQL вокруг переменной, остановитесь и попросите параметризованную версию.

Оба пути кладут один и тот же ввод пользователя в базу данных, но только один не даёт базе принять этот ввод за команды:

  ввод данных: '; DROP TABLE users; --
        │
        ├─────────────────────────┬───────────────────────────┐
        ▼                         ▼                            
  ✗ КОНКАТЕНАЦИЯ           ✓ ПАРАМЕТРИЗАЦИЯ                    
  "...WHERE name=             "...WHERE name = ?", [input]      
     '" + input + "'"          (запрос и данные раздельно)     
        │                         │
        ▼                         ▼
  ┌───────────────┐         ┌───────────────┐
  │   БАЗА ДАННЫХ │         │   БАЗА ДАННЫХ │
  │ ввод как      │         │ ввод только   │
  │ КОМАНДЫ       │         │ как ДАННЫЕ    │
  │  → нет таблицы│         │  → безопасно  │ 
  └───────────────┘         └───────────────┘
       ВЗЛОМ                БЕЗОПАСНО

XSS (cross-site scripting) — та же идея, но в браузере. Если вы берёте пользовательский ввод и вставляете его на страницу как сырой HTML (HyperText Markup Language, код, который говорит браузеру, что отображать), атакующий может внедрить тег <script>, который выполнится в браузерах ваших других пользователей — например, угнав их сессии. Исправление — экранирование: выводить пользовательский контент как текст, а не как HTML. Современные фреймворки вроде React экранируют по умолчанию, и это отлично — ровно до тех пор, пока AI не потянется к лазейке вроде dangerouslySetInnerHTML или innerHTML, чтобы «сделать, чтобы работало». Они обходят защиту. Любой такой вызов в диффе воспринимайте как повод задать вопрос.

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

Хотите офлайн-версию?

Скачайте всю книгу в PDF или EPUB — бесплатно.