Created 2026-03-13

0 stars
Code

27 KiB

Процесс 8: Формирование и поддержание правил кодинга

Дата публикации: 13 марта 2026 г.
Время на чтение: ~10 мин
ТГ канал: Про_SEC_со

Структура

  • Суть процесса
  • Цели процесса
  • Требования к реализации
  • Артефакты процесса
  • Практические примеры

Суть процесса простыми словами

В ревью прилетает коммит. В нём по сути три вещи:

  • В конфиге лежит пароль к тестовой БД в открытом виде.
  • SQL к пользователю собирается конкатенацией строк: "SELECT * FROM users WHERE id = " + request.id.
  • В лог пишется полный заголовок Authorization, вместе с токеном.

Ревьюер спрашивает: «а у нас вообще есть правила, как такое не делать?» Оказывается — нет. Каждый пишет как привык, линтер настроен только на форматирование, про безопасность в коде договориться не успели.

Процесс 8 по ГОСТ Р 56939-2024 — это как раз про то, чтобы не гадать на кофейной гуще, а зафиксировать правила: как оформлять код, чего в коде быть не должно и как это проверять. Не «на словах», а в регламенте, с примерами опасных и безопасных конструкций, с привязкой к языкам и стеку команды. И желательно — с автоматической проверкой, чтобы правила работали при каждом коммите, а не только когда кто-то вспомнит.

Здесь важно не путать два слоя. Оформление кода (имена переменных, отступы, длина строк, стиль комментариев) — про читаемость и единообразие; это основа, без неё команда тонет в стилевых войнах. Безопасное кодирование — про запрет опасных приёмов и явные требования к безопасности: не хранить секреты в коде, не использовать уязвимые конструкции, следовать практикам (параметризованные запросы, санитизация ввода и т.п.). ГОСТ объединяет оба в одном регламенте: и как код выглядит, и как он не должен ломать безопасность.

Чем процесс 5.3 отличается от процесса 5.8. Часто путают: и там, и там речь про «требования» и «безопасность». Разница в уровне и артефакте. Процесс 5.3 создаёт набор требований безопасности к ПО (артефакт 5.3.3.2): это документ с формулировками что должно быть у продукта — «пароли хранятся только в хешированном виде», «доступ к данным проверяется по одному источнику правды», «защита от SQL-инъекций», «токены не попадают в логи». Требования предъявляют к ПО в целом, ими руководствуются архитектура (5.6), тесты, приёмка. Процесс 5.8 создаёт регламент оформления кода и безопасного кодирования (артефакт 5.8.3.1): это уже как писать код, чтобы эти требования выполнять — запрет хардкода паролей, использование параметризованных запросов или ORM, правила логирования (не выводить токены), примеры опасных и безопасных конструкций по языкам, порядок проверки. Пример: требование из 5.3 — «система защищена от SQL-инъекций»; регламент 5.8 — «в коде не собираем SQL конкатенацией строк, используем prepared statements или ORM, в репозитории есть примеры „плохо / хорошо“». Итого: 5.3 отвечает на вопрос «что нужно от продукта», 5.8 — «как именно мы это кодируем и проверяем в коде». Регламент 5.8 должен соответствовать предъявляемым к ПО требованиям — в том числе тем, что сформированы в процессе 5.3.

Если правил нет — каждый тянет в свою сторону, ревью превращается в «а давай вот так», а уязвимости появляются просто потому, что никто не договорился, «так у нас не пишут» - слышал я не один раз. С регламентом и линтерами под него команда получает общий язык и автоматический фильтр на входе. Тогда и экспертиза кода (процесс 5.9), и статический анализ (процесс 5.10) опираются на одни и те же правила — без резких переходов от «как попало» к «теперь давайте по ГОСТу».


Цели процесса

Цель 5.8.1.1: Обеспечение эффективной и единообразной организации оформления и использования исходного кода в соответствии с предъявляемыми к ПО требованиям.

Простыми словами: код должен быть оформлен единообразно и использоваться так, чтобы он соответствовал требованиям к ПО — в том числе к безопасности. Единообразие снижает ошибки при чтении и поддержке; явные правила по безопасному кодированию снижают типовые уязвимости. Цель процесса — не «написать красивый документ», а сделать так, чтобы эти правила реально применялись в разработке: приняты, доведены до команды и по возможности проверяются автоматически.


Требования к реализации

Требование 5.8.2.1: Принять и использовать в процессе разработки кода ПО регламент оформления исходного кода и безопасного кодирования для используемых разработчиком языков программирования.

По ГОСТу: регламент должен быть принят и использоваться при разработке для каждого языка, на котором пишет команда.

Простыми словами: регламент не лежит в папке «для аудита» — по нему живут. Для Python, Java, JavaScript, Go и т.д. либо один общий регламент с разделами по языкам, либо отдельные разделы/приложения. Важно: все, кто пишет код, знают, где смотреть правила, и они применяются в текущих проектах. Без этого требование не выполнено.

Примечание по ГОСТу: под безопасным кодированием понимаются практики разработки в соответствии с требованиями по безопасности, зафиксированными в этом регламенте.


Требование 5.8.2.2: Учитывать при разработке регламента оформления исходного кода и безопасного кодирования примеры опасных и безопасных конструкций для используемых в ПО языков программирования.

По ГОСТу: в регламенте должны быть учтены примеры опасных и безопасных конструкций по каждому используемому языку.

Простыми словами: разработчик должен видеть не только «так нельзя», но и «вот так можно». Такие примеры сильно ускоряют онбординг и снижают споры на ревью. Их можно брать из OWASP, из стандартов языка, из внутренних инцидентов — и обязательно привязывать к вашему стеку.

Примеры опасных и безопасных конструкций (для включения в регламент):

SQL-запросы (Python):

# Плохо: конкатенация строк — риск SQL-инъекции
query = "SELECT * FROM users WHERE id = " + user_id
cursor.execute(query)

# Хорошо: параметризованный запрос
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
# или ORM: User.query.filter_by(id=user_id).first()

Секреты (любой язык):

# Плохо: пароль или ключ в коде
API_KEY = "sk-1234567890abcdef"
db_password = "super_secret_123"

# Хорошо: из окружения или секрет-хранилища
import os
API_KEY = os.environ.get("API_KEY")
# или загрузка из Vault / конфига, не коммитящегося в репо

Логирование (не выводить чувствительные данные):

# Плохо: токен или заголовок в лог
logger.info("Request headers: %s", request.headers)

# Хорошо: только метаданные
logger.info("Request from user_id=%s, path=%s", request.user_id, request.path)

Вывод в HTML / XSS (JavaScript):

// Плохо: несанитизированный вывод
element.innerHTML = userInput;

// Хорошо: текстовое содержимое или санитизация
element.textContent = userInput;
// или DOMPurify.sanitize(userInput) при необходимости HTML

Требование 5.8.2.3: Учитывать при разработке регламента оформления исходного кода и безопасного кодирования общепринятые стандарты и рекомендации разработчиков (экспертов, специалистов) для соответствующих языков программирования.

По ГОСТу: регламент должен опираться на общепринятые стандарты и рекомендации по языкам.

Простыми словами: не изобретаем велосипед. В регламенте явно перечисляют принятые стандарты и ссылки. Так регламент остаётся связанным с мировой практикой и проще обновлять.

Пример раздела «Принятые стандарты и рекомендации» в регламенте:

Стиль кода:
- Python: PEP 8, инструменты Black + isort (конфиг в pyproject.toml).
- JavaScript/TypeScript: Airbnb Style Guide (или Google), ESLint config в репо.
- Go: gofmt, golangci-lint с настройками из .golangci.yml.

Безопасность:
- OWASP Secure Coding Practices (https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/).
- CWE Top 25  учтены в правилах линтеров и в примерах «плохо / хорошо».
- Язык: Bandit (Python), eslint-plugin-security (JS), gosec (Go)  правила включены в CI.
- Внутренние дополнения: список запрещённых функций (eval, exec с вводом пользователя, gets и т.д.)  см. раздел «Запрещённые конструкции».

Требование 5.8.2.4: При разработке ПО рекомендуется использовать программные средства автоматической проверки правил кодирования.

По ГОСТу: рекомендуется применять средства автоматической проверки правил кодирования.

Примечание по ГОСТу: допускается реализовывать проверку правил средствами компиляции или статического анализа.

Простыми словами: правила, которые никто не проверяет, быстро забываются. Линтеры и форматтеры (ESLint, Pylint, SpotBugs, Gosec, RuboCop и т.д.), правила безопасности в статических анализаторах (Bandit, Semgrep, Checkmarx и др.), pre-commit хуки и проверки в CI — всё это превращает регламент в работающий фильтр. Компилятор и статический анализ тоже считаются: например, включённые предупреждения компилятора или правила SAST могут закрывать часть пунктов регламента. Суть — автоматизация, а не только человеческая память.


Артефакты процесса

По ГОСТ Р 56939-2024 у процесса 8 один основной артефакт.

Артефакт 5.8.3.1: Регламент оформления исходного кода и безопасного кодирования

По ГОСТу регламент должен содержать:

  • Информацию о способах оформления исходного кода — например: как именовать переменные, функции, классы; стиль отступов и ограничения логических блоков; пробелы в выражениях; стиль комментариев и документирования; ограничения (длина строки, размер модуля и т.п.).
  • Перечень запрещённых способов кодирования и конструкций — например: пароли в коде в явном виде, «магические числа» без констант, небезопасные функции (gets, eval по недоверенному вводу) и т.д., с привязкой к языкам.
  • Примеры опасных и безопасных конструкций для используемых языков программирования (в соответствии с требованиями 5.8.2.2).
  • Область применения правил кодирования — к каким проектам, репозиториям, языкам и этапам применяется регламент; есть ли исключения и как они оформляются.
  • Порядок проверки выполнения правил для вносимых изменений в исходный код — когда проверяем (при коммите, в CI, на ревью), кто отвечает, как обрабатываются нарушения, кто может временно отключать правило и по какой процедуре.
  • Рекомендации разработчиков языков и принятые стандарты — ссылки на PEP, OWASP, стандарты кодирования (в т.ч. собственные), которые организация приняла к применению (в соответствии с 5.8.2.3).

Простыми словами: регламент — это один живой документ (или набор связанных разделов), из которого разработчик понимает: как код должен выглядеть, чего в коде быть не должно, как выглядит «хорошо» и «плохо» на примерах, где правила действуют и как их проверяют. Его создают и поддерживают ответственные за качество и безопасность кода (например, Tech Lead, Security Champion, AppSec); обновление — при смене стека, появлении нового языка или после инцидентов, когда выясняется, что какого-то правила не хватало.

Пример фрагмента регламента — перечень запрещённых конструкций:

Запрещено в исходном коде:
- Хранение паролей, ключей API, токенов в явном виде (в коде, в коммитах). Использовать переменные окружения или секрет-хранилище.
- Сборка SQL/запросов через конкатенацию строк с пользовательским или внешним вводом. Использовать параметризованные запросы или ORM.
- eval(), exec() с данными, пришедшими от пользователя или извне (в т.ч. десериализация без валидации).
- Вывод в логи: пароли, токены, заголовки Authorization/Cookie целиком, номера карт, персональные данные в открытом виде.
- Вставка несанитизированного пользовательского ввода в HTML (innerHTML, dangerouslySetInnerHTML без санитизации). Использовать textContent или санитизацию (DOMPurify и т.п.).
- «Магические числа» и строки без вынесения в именованные константы или конфиг (в т.ч. коды ошибок, лимиты, таймауты).

Пример фрагмента — порядок проверки правил:

Проверка выполнения правил:
1. Pre-commit: форматтеры и линтеры (Black, ESLint и т.д.) — блокируют коммит при ошибках форматирования и при срабатывании правил безопасности из конфига.
2. CI: те же линтеры + статический анализатор (Bandit, Semgrep, SonarQube и т.п.). Критичные нарушения безопасности — сборка не проходит, MR не мержится без исправления или явного исключения по процедуре.
3. Ревью кода: ревьюер проверяет соответствие регламенту (в т.ч. по чек-листу в описании MR). Временное отключение правила — только через тикет с обоснованием и сроком устранения, согласование с Security Champion.

Практические примеры

Три сценария: как регламент и правила кодирования выглядят в разных условиях — от уже сложившегося стека до «начинаем с нуля» и реакции на инцидент.

Пример 1: Регламент для веб-бэкенда (Python, JavaScript)

Команда ведёт веб-сервис: бэкенд на Python (FastAPI), фронтенд на TypeScript/React. В регламенте завели два раздела по языкам.

По Python в регламенте зафиксировали:

  • Оформление: PEP 8, Black + isort (конфиг в репо).
  • Запрещено: eval/exec с пользовательским вводом; конкатенация строк в SQL; пароли и ключи в коде.
  • Обязательно: параметризованные запросы или ORM (SQLAlchemy, Django ORM); секреты из os.environ или vault.
  • Примеры «плохо / хорошо» — один блок по SQL, один по секретам, один по логированию (как в листингах выше).

По JavaScript/TypeScript:

  • Оформление: ESLint по принятому style guide, Prettier.
  • Запрещено: dangerouslySetInnerHTML без санитизации; хранение токенов в localStorage для критичных данных (где требуется — httpOnly cookie).
  • Обязательно: для вывода пользовательского ввода — textContent или санитизация (DOMPurify); cookie с флагами httpOnly, secure, sameSite.

Проверка: pre-commit (Black, isort, ESLint), в CI — те же линтеры плюс Bandit (Python) и Semgrep с правилами под OWASP. Регламент в Confluence, ссылка — в README репо и в шаблоне PR.

Пример 2: Введение правил без «большого регламента»

Небольшая команда раньше жила без формального регламента. Решили начать с малого: один общий документ на 3–4 страницы — файл SECURITY-CODING.md в корне репо.

Содержимое документа:

  1. Стиль: ссылки на PEP 8 (Python) и выбранный style guide для JS; «запускаем Black/Prettier до коммита».
  2. Запрещено: пароли и ключи в коде; конкатенация SQL с пользовательским вводом; вывод пользовательских данных в HTML без санитизации; логирование токенов и паролей.
  3. Примеры: по одному блоку «плохо / хорошо» на SQL, на секреты и на логи — скопировали из OWASP и слегка адаптировали под свой стек.
  4. Область применения: все репозитории команды.
  5. Проверка: линтеры в CI (Bandit, eslint-plugin-security), при критичных нарушениях MR не мержится; исключение — только через тикет с обоснованием.

Регламент согласовали на митапе, линтеры настроили за одну итерацию. Через полгода документ расширили: добавили раздел по работе с секретами и ссылку на процесс 15.

Пример 3: Обновление регламента после инцидента

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

Что сделали после разбора:

  • В раздел «Запрещённые конструкции» добавили пункт: «Вывод в логи паролей, токенов, cookie, заголовков Authorization/Cookie целиком».
  • В раздел «Примеры» добавили блок по логированию (как в листинге выше: плохо — request.headers, хорошо — user_id, path, timestamp).
  • В Bandit/Semgrep добавили правило на типичные конструкции: logger.*(password|token|secret|authorization|headers), чтобы такие места подсвечивались в коде.

Так процесс 8 сработал реактивно: регламент и проверки подтянулись под реальный кейс, чтобы такое не повторялось.


Итог: процесс 8 связывает «как мы пишем код» и «как мы не создаём уязвимости» в один прозрачный набор правил. Регламент с примерами и областью применения даёт команде общий язык; автоматическая проверка удерживает правила в действии при каждом изменении кода. Без резких переходов — от общих целей к конкретным требованиям и одному артефакту, который можно развивать по мере роста стека и зрелости команды.