Перейти к основному содержимому

ADR: Выбор основного языка разработки

  • Status: Accepted by Andrei Ganiushkin andrey@ganyushkin.ru
  • Date: 2026-06-26
  • Authors: Andrei Ganiushkin andrey@ganyushkin.ru
  • Related issues: -

Context

Проекту Netgap требуется выбрать основной язык разработки. Приложение имплементирует паттерны logical airgap и должно работать на границе доверенных и недоверенных контуров, где ошибки обработки данных, сетевого взаимодействия, памяти или конкурентного доступа могут становиться критичными дефектами безопасности.

Для проекта важны следующие факторы:

  • высокая устойчивость к типовым классам уязвимостей, особенно memory safety ошибкам: use-after-free, double free, buffer overflow, data race и обращение к невалидной памяти;
  • предсказуемая производительность без пауз сборщика мусора, так как приложение планируется под realtime нагрузки и должно сохранять малые задержки при интенсивном сетевом и потоковом обмене;
  • возможность писать низкоуровневые компоненты, сетевые протоколы, парсеры, криптографические и изоляционные механизмы без перехода на другой язык;
  • строгая модель обработки ошибок и состояний, уменьшающая вероятность неявных отказов, некорректного fallback-поведения и silent failure;
  • поддержка многопоточности и асинхронной обработки с минимизацией гонок данных на уровне компиляции;
  • возможность поставлять компактные бинарные артефакты с контролируемыми зависимостями и без обязательного runtime окружения;
  • применимость современных практик secure development lifecycle: dependency audit, fuzzing, static analysis, supply chain контроль, воспроизводимые сборки и автоматическая проверка отсутствия unsafe в коде проекта.

Для Netgap выбор языка является не только инженерным, но и архитектурным решением безопасности. Язык должен снижать вероятность критичных дефектов в базовой реализации, не ухудшая при этом производительность, задержки и контроль над runtime-поведением.

Decision

В качестве основного языка разработки проекта Netgap выбрать Rust.

Rust должен использоваться для реализации компонентов, связанных с обработкой сетевого трафика, logical airgap паттернами, парсингом входных данных, контролем потоков, политиками безопасности, криптографическими операциями, высоконагруженной realtime-обработкой и системной интеграцией.

Выбор Rust фиксирует следующие архитектурные правила:

  • в коде проекта не должно быть unsafe; это требование должно подтверждаться автоматическими проверками, например запретом unsafe_code, CI-контролем и анализом через cargo geiger или аналогичные инструменты;
  • unsafe во внешних зависимостях допустим только при соблюдении требований ИБ: минимизация dependency graph, проверка advisory, фиксация версий, анализ происхождения и отдельный review зависимостей, используемых в security-sensitive частях проекта;
  • если низкоуровневые оптимизации или платформенные интеграции требуют unsafe, их следует выносить из основного кода в отдельные крейты с явно описанными границами ответственности, инвариантами безопасности, тестами, fuzzing и документированными гарантиями безопасности;
  • проектировать критичные парсеры, state machine и обработку политик через строгие типы, enum, Result, явную обработку ошибок и отсутствие неявных исключений;
  • использовать преимущества Rust ownership и borrow checker для снижения риска memory corruption и data race в многопоточных realtime-сценариях;
  • применять zero-cost abstractions и отсутствие garbage collector как основу для предсказуемых задержек;
  • ограничивать и контролировать внешние зависимости, особенно в security-sensitive частях проекта;
  • для boundary-компонентов и работы с недоверенными входными данными использовать fuzzing, property-based тестирование, dependency audit и статический анализ.

Rust не считается единственной мерой безопасности. Он снижает вероятность целых классов дефектов, но не заменяет threat modeling, secure architecture, defense in depth, code review, тестирование, hardening, мониторинг и управление цепочкой поставки зависимостей.

Considered Alternatives

Alternative 1: Rust

Rust используется как основной язык разработки ядра Netgap и security-sensitive компонентов.

  • Pros:
    • Обеспечивает memory safety без garbage collector, что особенно важно для security-приложения с realtime требованиями.
    • Ownership, borrow checker и строгая система типов уменьшают риск use-after-free, double free, dangling pointer, buffer overflow и data race.
    • Отсутствие GC-пауз повышает предсказуемость задержек по сравнению с managed-языками.
    • Производительность близка к C/C++ и подходит для интенсивной сетевой обработки, парсеров, очередей, криптографии и системных интеграций.
    • Result, Option, enum и pattern matching стимулируют явную обработку ошибок и состояний, что важно для корректной реализации security policy.
    • Экосистема поддерживает инструменты для ИБ-процессов: cargo audit, cargo deny, cargo geiger, fuzzing, sanitizers, Miri, SBOM, воспроизводимые сборки и проверку отсутствия unsafe в коде проекта.
  • Cons:
    • Более высокий порог входа и более строгая модель разработки по сравнению с Go, Java, Kotlin или Python.
    • Часть экосистемы требует внимательного выбора зависимостей, потому что внешние crate могут содержать unsafe или иметь слабую поддержку.
    • Разработка некоторых компонентов может занимать больше времени из-за требований borrow checker и явного моделирования владения данными.
  • Reason for selection: Rust дает лучший баланс для Netgap: безопасность памяти и конкурентного доступа на уровне компиляции, высокая производительность, отсутствие GC-пауз и достаточный контроль над низкоуровневым поведением. Для проекта в области информационной безопасности этот баланс важнее, чем максимальная скорость прототипирования.

Alternative 2: C или C++

Проект реализуется на C или C++ для максимального контроля над памятью, системными API и производительностью.

  • Pros:
    • Максимальный низкоуровневый контроль и зрелая экосистема системной разработки.
    • Очень высокая производительность и широкая доступность компиляторов, библиотек и платформенных интеграций.
    • Подходит для hard realtime и embedded-сценариев при наличии сильной инженерной дисциплины.
  • Cons:
    • Высокий риск memory safety уязвимостей, которые особенно опасны для приложения информационной безопасности.
    • Безопасность владения памятью, потоков и lifetime в основном обеспечивается дисциплиной команды, code review, sanitizers и дополнительными инструментами, а не компилятором по умолчанию.
    • Ошибки парсинга недоверенных данных, переполнения буферов и use-after-free могут приводить к RCE, обходу изоляции или нарушению logical airgap гарантий.
    • Для достижения уровня безопасности, сопоставимого с safe Rust, требуется больше ручных практик, ограничений, проверок и времени на аудит.
  • Reason for rejection: C и C++ дают высокую производительность, но создают слишком большую поверхность memory safety рисков для базового языка проекта Netgap. Для security-first продукта компиляторная защита Rust предпочтительнее, чем зависимость от дисциплины ручного управления памятью.

Alternative 3: Go

Проект реализуется на Go для быстрого создания сетевых сервисов, конкурентной обработки и простой эксплуатации.

  • Pros:
    • Простая модель разработки и быстрый цикл сборки.
    • Хорошая стандартная библиотека для сетевых приложений, HTTP, TLS, concurrency и tooling.
    • Удобная поставка статически собранных бинарных файлов.
    • Memory safety выше, чем у C/C++, за счет managed runtime.
  • Cons:
    • Garbage collector может создавать задержки и менее предсказуемое latency-поведение в realtime-нагрузках.
    • Модель ошибок и типов менее выразительна для строгого моделирования security policy и state machine по сравнению с Rust.
    • Data race возможны и требуют отдельного обнаружения через race detector и дисциплину разработки.
    • Runtime и GC уменьшают контроль над низкоуровневым поведением в критичных участках.
  • Reason for rejection: Go хорошо подходит для инфраструктурных сервисов и быстрой разработки, но для Netgap хуже соответствует сочетанию security-first, realtime latency и низкоуровневого контроля. Наличие GC и меньшие компиляторные гарантии конкурентной безопасности делают Rust более подходящим выбором.

Alternative 4: Java или Kotlin

Проект реализуется на JVM для использования зрелой платформы, высокой продуктивности разработки и широкой экосистемы.

  • Pros:
    • Богатая экосистема библиотек, инструментов наблюдаемости и enterprise-интеграций.
    • Высокая продуктивность разработки и зрелые практики тестирования.
    • Memory safety в типичных сценариях выше, чем у C/C++.
    • JIT может обеспечивать высокую throughput-производительность для долгоживущих сервисов.
  • Cons:
    • JVM, JIT, warm-up и garbage collector усложняют предсказуемость latency в realtime-сценариях.
    • Больший runtime и зависимость от JVM увеличивают эксплуатационную сложность и потенциальную поверхность атаки.
    • Низкоуровневый контроль над памятью, системными вызовами и компактностью поставки хуже, чем у Rust.
    • Для security appliance или boundary-компонентов JVM может быть избыточной платформой.
  • Reason for rejection: Java и Kotlin подходят для бизнес-сервисов и control plane, но хуже подходят как основной язык для ядра security-приложения с logical airgap паттернами и realtime требованиями. Rust обеспечивает более компактную поставку, лучшую предсказуемость задержек и больший контроль над критичными участками.

Alternative 5: Zig

Проект реализуется на Zig как современном системном языке с простым runtime и явным управлением памятью.

  • Pros:
    • Высокий контроль над памятью, сборкой и системными интеграциями.
    • Отсутствие garbage collector и хорошая применимость для низкоуровневых компонентов.
    • Более простая языковая модель по сравнению с C++ и потенциально удобная кросс-компиляция.
  • Cons:
    • Экосистема, tooling и кадровый рынок менее зрелые, чем у Rust.
    • Гарантии memory safety слабее, чем у safe Rust, и больше зависят от дисциплины разработки.
    • Для security-critical проекта выше риск нехватки проверенных библиотек, практик аудита и долгосрочной поддержки.
  • Reason for rejection: Zig перспективен для системной разработки, но на текущем этапе Rust предоставляет более зрелый баланс безопасности, производительности, tooling и экосистемы для проекта Netgap.

Consequences

Positive

  • Проект получает основной язык, который снижает вероятность memory corruption и data race уязвимостей в базовой реализации.
  • Realtime-компоненты могут работать без GC-пауз и с более предсказуемыми задержками.
  • Security-sensitive логика может быть выражена через строгие типы, явные состояния и обязательную обработку ошибок.
  • Низкоуровневые компоненты, сетевые протоколы, парсеры и интеграции можно реализовывать без перехода на C/C++ как основной язык.
  • Компактные бинарные артефакты упрощают поставку, изоляцию, hardening и эксплуатацию.
  • Экосистема Rust позволяет встроить в процесс разработки dependency audit, fuzzing, статический анализ и автоматическое подтверждение отсутствия unsafe в коде проекта.
  • Выбор языка хорошо соответствует назначению Netgap как приложения информационной безопасности, реализующего logical airgap паттерны.

Negative

  • Скорость разработки на раннем этапе может быть ниже из-за порога входа, borrow checker и необходимости точнее моделировать владение данными.
  • Не все библиотеки экосистемы одинаково зрелые или одинаково подходят для security-sensitive контекста.
  • Внешние зависимости могут содержать unsafe или транзитивно приносить код, который сложнее аудировать.
  • Некоторые интеграции с платформенными API, драйверами или legacy-библиотеками могут потребовать FFI и unsafe, что потребует вынесения такого кода в отдельные крейты и усиленного review.
  • Для команды потребуется закрепить практики Rust code review, threat modeling, fuzzing и dependency governance.

Risks and Mitigations

  • Risk: основная проблема Rust в контексте ИБ состоит в том, что гарантии языка действуют только для safe Rust; unsafe, FFI и внешние зависимости могут обходить часть проверок компилятора и возвращать проект к рискам memory safety.
    • Mitigation: запретить unsafe в коде проекта и подтверждать это автоматическими проверками, например #![forbid(unsafe_code)], CI-контролем и cargo geiger или аналогичными инструментами. unsafe во внешних зависимостях допускать только при выполнении требований ИБ к supply chain, а при необходимости низкоуровневых оптимизаций выносить такой код в отдельные крейты с документированными инвариантами, fuzzing, targeted-тестами, security review и явно предоставленными гарантиями безопасности.
  • Risk: supply chain уязвимости в crate-экосистеме могут попасть в security-sensitive компоненты проекта.
    • Mitigation: использовать минимальный набор зависимостей, фиксировать версии через lockfile, применять cargo audit и cargo deny, проверять лицензии и advisory, ограничивать транзитивные зависимости, формировать SBOM и отдельно ревьюить зависимости, используемые на границе доверенных контуров.
  • Risk: сложность Rust может привести к ошибочным архитектурным обходам, чрезмерному клонированию данных, блокировкам или неоптимальным realtime-путям.
    • Mitigation: закрепить правила проектирования ownership-модели, проводить performance review для критичных путей, использовать benchmarks, tracing, профилирование и load-тесты, а также предпочитать простые и проверяемые структуры данных сложным абстракциям.
  • Risk: логические ошибки безопасности возможны даже при отсутствии memory corruption.
    • Mitigation: дополнять свойства языка threat modeling, негативными тестами, fuzzing недоверенных входов, проверкой state machine, независимым code review и явной спецификацией security policy.
  • Risk: FFI-интеграции с C/C++ библиотеками могут создать неочевидные lifetime, ownership и threading ошибки.
    • Mitigation: минимизировать FFI, использовать проверенные библиотеки, выносить FFI-границы в отдельные крейты, документировать владение ресурсами и инварианты безопасности, тестировать границы интеграции с помощью sanitizers и fuzzing, а для критичных зависимостей рассматривать переписывание на safe Rust.