Кодовая безопасность: JWT, 2FA, XSS, CSRF, SQL-инъекции и всё, что должен знать разработчик, чтобы не проснуться знаменитым

Автор: Алексей Пасечник (Arhellist). Канонический адрес: https://arhellist.ru/blog/kodovaya-bezopasnost-jwt-2fa-xss-csrf-sql-inektsii-i-vsyo-chto-dolzhen-znat-razrabotchik-chtoby-ne-prosnutsya-znamenitym/.

Опубликовано: 2026-07-03.

Твой код может быть идеальным. Твоя архитектура — эталонной. Но если ты забыл про безопасность на уровне кода — ты построил замок из песка. Прилив не заставит себя ждать.

В прошлый раз мы говорили о защите сервера: SSH, UFW, бэкапы, мониторинг целостности. Всё это — про инфраструктуру. Но есть ещё один слой, который лежит ближе к пользователю, ближе к данным, ближе к деньгам. Это кодовая безопасность.

Сегодня я расскажу про основные стандарты и практики, которые должен знать каждый разработчик, который выпускает код в прод. Без паники, без перегруза. Только база, которая спасёт тебя от большинства проблем.

🔑 JWT: не просто строка, а ключ к королевству JWT (JSON Web Token) — это стандарт для авторизации. Выглядит как длинная строка из трёх частей, разделённых точками. Внутри — информация о пользователе, подписанная секретным ключом.

Что важно знать:

Храни секрет правильно. Не в коде. В переменных окружения. И никогда не коммить .env в репозиторий.

Время жизни токена. accesstoken — 15-30 минут, refreshtoken — 7-30 дней. Чем короче — тем безопаснее. Да, пользователи будут перелогиниваться чаще. Но если токен украдут — у злоумышленника будет меньше времени на атаку.

Храни refreshtoken в httpOnly cookie. Не в localStorage, не в sessionStorage. Только в защищённых куках, которые не доступны JavaScript. Это защищает от XSS.

Алгоритм подписи. Используй RS256 или ES256 (асимметричные) вместо HS256 (симметричный). HS256 — общий секрет на всех серверах. Если один сервер скомпрометирован — все токены под угрозой.

Ротация - обеспечь замену одних токенов другими в хранилище при из экспирации. Иначе твое хранилище превратится в свалку протухших токенов.

🛡️ 2FA: второй рубеж обороны Двухфакторная аутентификация — это не "фича для гиков". Это стандарт. Особенно для администраторов и пользователей с доступом к деньгам.

Как работает:

Пользователь вводит логин и пароль.

Затем — одноразовый код из приложения (Google Authenticator, Authy) или SMS (менее безопасно, но лучше, чем ничего).

Код генерируется на основе общего секрета и текущего времени (TOTP — Time-based One-Time Password).

Почему это важно:

Пароль могут украсть, подобрать или перехватить.

Код меняется каждые 30 секунд и доступен только с устройства пользователя.

Даже если злоумышленник знает пароль — без второго фактора он не войдёт.

Где внедрять:

Административная панель — обязательно.

Личный кабинет пользователя с платежами — обязательно.

Вход в систему — желательно для всех.

💉 SQL-инъекции: старая, но не менее опасная SQL-инъекция — это когда пользователь вводит в поле формы не "Иван", а ' OR 1=1; DROP TABLE users; --. И если ты не защитился — база данных превращается в руины.

Как защититься:

Параметризованные запросы (Prepared Statements). Это когда ты передаёшь значения отдельно от самого запроса. БД понимает, что это данные, а не команда. В PostgreSQL — $1, $2. В Node.js с библиотеками типа pg или Sequelize — автоматически.

Никогда не склеивай строки. Никогда не пиши "SELECT FROM users WHERE name = '" + userName + "'". Это прямой путь к катастрофе.

Валидация входных данных. Не верь пользователю. Проверяй тип, длину, формат, допустимые символы.

Минимальные права для БД. Пользователь приложения должен иметь только те права, которые ему нужны: SELECT, INSERT, UPDATE, DELETE для своей схемы. Никаких DROP, ALTER, CREATE.

🌐 XSS: когда JavaScript становится оружием XSS (Cross-Site Scripting) — это когда злоумышленник вставляет свой JavaScript-код на твою страницу. Пользователь открывает страницу, скрипт выполняется и, например, крадёт его куки с токеном авторизации.

Типы XSS:

Reflected (отражённый). Скрипт передаётся через URL-параметры. Пользователь переходит по ссылке — скрипт выполняется.

Stored (хранимый). Скрипт сохраняется в базе данных (например, в комментариях) и выполняется при каждом просмотре страницы.

DOM-based. Скрипт манипулирует DOM через уязвимости в коде.

Как защититься:

Экранирование (escaping). При выводе пользовательского контента экранируй специальные символы: <, , &, ", '. Библиотеки типа React делают это автоматически, но не везде.

Content Security Policy (CSP). Заголовок, который говорит браузеру, откуда можно загружать скрипты, стили, изображения. Если злоумышленник вставит свой скрипт — браузер его не выполнит, потому что он не из разрешённого источника.

HttpOnly и Secure куки. Куки с токеном не должны быть доступны через JavaScript. Только сервер их видит.

Санитизация на сервере. Если ты хранишь HTML от пользователя (например, для редактора) — используй библиотеки для очистки, такие как DOMPurify.

🕵️ CSRF: атака из другого окна CSRF (Cross-Site Request Forgery) — это когда злоумышленник заставляет браузер пользователя отправить запрос на твой сервер, используя активную сессию. Например, пользователь зашёл на фишинговый сайт, а тот отправил запрос на смену пароля в твоём приложении.

Как защититься:

CSRF-токены. Генерируешь уникальный токен для каждой формы или запроса, хранишь его в сессии на сервере. При отправке формы проверяешь, что токен совпадает.

SameSite Cookie. Устанавливай куки с атрибутом SameSite=Strict или SameSite=Lax. Браузер не будет отправлять куки при запросах с других сайтов.

Проверка Referer/Origin заголовков. Сервер проверяет, откуда пришёл запрос. Если не с твоего домена — отбрасывает.

Используй POST для изменений. GET-запросы не должны менять состояние системы. Никогда.

👥 RBAC: кто есть кто в твоей системе RBAC (Role-Based Access Control) — это управление доступом на основе ролей. Пользователь получает права не напрямую, а через роль.

Пример:

Роль user — может читать свои данные, комментировать посты.

Роль moderator — может удалять комментарии других пользователей.

Роль admin — может управлять пользователями, настройками, платежами.

Что важно:

Минимальные привилегии. Пользователь должен иметь ровно столько прав, сколько нужно для выполнения его задач. И ни на шаг больше.

Разделение ролей. Админ не должен быть обычным пользователем с дополнительными правами. Это разные сущности.

Проверка на каждом уровне. Не доверяй клиенту. Даже если интерфейс показывает "кнопка скрыта" — проверяй права на сервере для каждого запроса.

Инспекция. Логируй, кто что делал. Если кто-то удалил важные данные — ты должен знать, кто это был.

💳 PCI DSS: если ты работаешь с картами PCI DSS (Payment Card Industry Data Security Standard) — это набор требований для компаний, которые обрабатывают данные банковских карт.

Основные требования (очень кратко):

Храни данные только при необходимости. Никогда не храни CVV. Никогда не храни PAN (номер карты) в открытом виде. Если хранишь — только зашифрованным.

Передавай данные только по зашифрованным каналам. TLS 1.2+.

Регулярно обновляй антивирусы и патчи.

Ограничь доступ к данным. Только те сотрудники, кому реально нужно.

Веди логи доступа к данным.

Регулярно тестируй систему на уязвимости.

Документируй политики безопасности.

Если ты принимаешь карты — ты обязан соответствовать. Но лучше вообще не хранить данные карт на своём сервере. Используй платежные шлюзы (Stripe, YooKassa, Tinkoff), которые принимают данные на своей стороне.

🛡️ WAF: щит перед твоим приложением WAF (Web Application Firewall) — это файрвол на уровне веб-приложений. Он анализирует HTTP-трафик и блокирует подозрительные запросы до того, как они достигнут твоего кода.

Что умеет WAF:

Блокировать SQL-инъекции на уровне запроса.

Блокировать XSS-атаки.

Блокировать ботов и сканеры уязвимостей.

Ограничивать частоту запросов (rate limiting).

Блокировать IP из чёрных списков.

Популярные решения:

Cloudflare WAF — облачный, настраиваемый, с коллективной защитой.

ModSecurity — open-source, работает с Nginx/Apache.

AWS WAF — для тех, кто в облаке Amazon.

Fail2ban/CrowdSec — частично решают задачи, но это не полноценный WAF.

Когда нужен WAF:

Всегда, если у тебя есть публичное веб-приложение.

Особенно если ты обрабатываешь платежи или личные данные.

🧹 Санитизация входных данных: не верь никому, даже себе «Пользователь — это враг. Не потому что он злой. А потому что он случайно введёт "' OR 1=1 --" в поле "Имя" и сломает тебе всю базу, даже не заметив этого.» — Моя памятка на рабочем столе

Когда я говорю «санитизация», я не про «помыть руки перед едой». Я про то, как ты обрабатываешь данные, которые приходят от пользователя, прежде чем делать с ними что-то важное: записать в БД, отправить в email, вывести на страницу, передать в системный вызов.

Это критично. Потому что пользователь — это не злоумышленник. Но злоумышленник — это тоже пользователь.

Что такое санитизация и чем она отличается от валидации Эти два понятия путают даже опытные разработчики. Давай разберёмся раз и навсегда.

Валидация — это проверка на «правильность». Ты спрашиваешь: «А соответствует ли это тому, что я ожидаю?»

Это email? → Да/Нет.

Это число от 1 до 100? → Да/Нет.

Это строка не длиннее 255 символов? → Да/Нет.

Санитизация — это очистка и приведение к безопасному виду. Ты спрашиваешь: «Как сделать так, чтобы эти данные не навредили, даже если они содержат что-то опасное?»

Удалил все теги HTML → <scriptalert(1)</script → alert(1)

Экранировал спецсимволы → O'Reilly → O''Reilly

Привёл к нужному типу → "42" → 42

Обрезал лишние пробелы → " hello " → "hello"

Обычно фреймворки типа React содержат модули санитизации.

Классические ошибки в санитизации Ошибка 1: Санитизировать только то, что видишь

Разработчики часто проверяют только поля формы, которые знают. А злоумышленник может добавить своё поле и отправить его. Валидируй и санитизируй все данные, а не только ожидаемые.

Ошибка 2: Использовать чёрные списки

«Я запрещу слова <script и DROP». Это не работает. Злоумышленник напишет <ScRiPt или DRoP. Или использует другой вектор атаки. Используй белые списки: разрешай только то, что безопасно.

Ошибка 3: Санитизировать только на клиенте

«У меня же React экранирует вывод». Да, по умолчанию. Но ты можешь случайно использовать dangerouslySetInnerHTML. Или забыть экранировать в атрибутах. Или данные могут прийти из другого источника. Не доверяй клиенту.

Ошибка 4: Игнорировать контекст

То, что безопасно в одном контексте, может быть опасно в другом. То, что безопасно для JSON, может быть опасно для HTML. То, что безопасно для SQL, может быть опасно для shell-команды. Используй санитизацию, адаптированную к контексту.

📊 Что ещё важно знать Rate Limiting: Ограничение числа запросов с одного IP или пользователя. Защита от брутфорса и DDoS.

Input Validation: Проверяй всё, что приходит от пользователя. Тип, формат, длина, допустимые символы. Не доверяй никому.

Error Handling: Не показывай пользователю детали ошибки. "Что-то пошло не так" — достаточно. Полные стектрейсы — только в логи.

Logging: Логируй всё, что связано с безопасностью: неудачные попытки входа, смену паролей, повышение прав, доступ к чувствительным данным.

Session Management: Сессии должны истекать. И пользователь должен иметь возможность выйти. Храни сессии в защищённых куках.

Secure Headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Content-Security-Policy, Referrer-Policy, Permissions-Policy, HSTS.

💎 Итог Безопасность — это не про "поставить firewall и забыть". Это про слои:

Инфраструктура — сервер, SSH, UFW, бэкапы.

Сеть — WAF, rate limiting, DDoS-защита.

Приложение — JWT, 2FA, RBAC, валидация.

Данные — шифрование, минимальные права, логирование.

Процессы — регулярные аудиты, обновления, обучение команды.

И самое главное: безопасность — это не разовое действие. Это процесс. Это дисциплина. Это культура.

Ты никогда не будешь на 100% защищён. Но ты можешь сделать так, чтобы взломать тебя было слишком сложно, слишком дорого и слишком долго. Чтобы злоумышленник просто пошёл искать более лёгкую цель.

Будь той целью, которую не хочется трогать.

О чём пишем

  • Архитектура и эксплуатация веб-продуктов
  • AI-агенты, RAG и практические контуры
  • Кейсы LMS, маркетплейсов и защищённых коммуникаций
  • Заметки по Next.js, Node.js, PostgreSQL и realtime