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 в корне репо.
Содержимое документа:
- Стиль: ссылки на PEP 8 (Python) и выбранный style guide для JS; «запускаем Black/Prettier до коммита».
- Запрещено: пароли и ключи в коде; конкатенация SQL с пользовательским вводом; вывод пользовательских данных в HTML без санитизации; логирование токенов и паролей.
- Примеры: по одному блоку «плохо / хорошо» на SQL, на секреты и на логи — скопировали из OWASP и слегка адаптировали под свой стек.
- Область применения: все репозитории команды.
- Проверка: линтеры в 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 связывает «как мы пишем код» и «как мы не создаём уязвимости» в один прозрачный набор правил. Регламент с примерами и областью применения даёт команде общий язык; автоматическая проверка удерживает правила в действии при каждом изменении кода. Без резких переходов — от общих целей к конкретным требованиям и одному артефакту, который можно развивать по мере роста стека и зрелости команды.