fix(proxy): исправление wire-transparency при fallback и усиление безопасности

Исправлена критическая логическая ошибка в цепочке Fake TLS -> MTProto.
Ранее при валидном TLS-хендшейке, но неверном MTProto-пакете, прокси
ошибочно передавал в маскирующий релей обернутый (FakeTls) поток.
Теперь транспорт корректно разворачивается (unwrap) до сырого сокета
через .into_inner(), обеспечивая полную прозрачность (wire-transparency)
для DPI и маскирующего бэкенда.

Security & Hardening:
- Логика приведена в соответствие с требованиями OWASP ASVS L2 (V5: Validation, Sanitization and Encoding).
- Реализовано поведение "fail-closed": при любой ошибке верификации прокси мимикрирует под обычный веб-сервер, не раскрывая своей роли.
- Улучшена диагностика и логирование состояний аутентификации для защиты от активного пробинга.

Adversarial Testing (Black-hat mindset):
- Добавлен отдельный пакет `client_tls_mtproto_fallback_security_tests.rs` (18+ тестов).
- Покрыты сценарии: хаос-фрагментация (побайтовая нарезка TLS-записей), record-splitting,
  half-close состояния, сбросы бэкенда и replay-pressure.
- В `client_adversarial_tests.rs` добавлено 10+ тестов на "злые" гонки (race conditions),
  утечки лимитов по IP и проверку изоляции состояний параллельных сессий.
- Все 832 теста проходят (passed) в locked-режиме.
This commit is contained in:
David Osipov
2026-03-20 17:33:46 +04:00
parent a78c3e3ebd
commit 8f1ffe8c25
4 changed files with 1896 additions and 2 deletions

View File

@@ -295,8 +295,16 @@ where
).await {
HandshakeResult::Success(result) => result,
HandshakeResult::BadClient { reader, writer } => {
// MTProto failed after TLS ServerHello was already sent.
// Switch fallback relay back to raw transport so the mask
// backend receives valid TLS records (not unwrapped payload).
let reader = reader.into_inner();
let writer = writer.into_inner();
stats.increment_connects_bad();
debug!(peer = %peer, "Valid TLS but invalid MTProto handshake");
debug!(
peer = %peer,
"Authenticated TLS session failed MTProto validation; engaging masking fallback"
);
handle_bad_client(
reader,
writer,
@@ -708,8 +716,16 @@ impl RunningClientHandler {
{
HandshakeResult::Success(result) => result,
HandshakeResult::BadClient { reader, writer } => {
// MTProto failed after TLS ServerHello was already sent.
// Switch fallback relay back to raw transport so the mask
// backend receives valid TLS records (not unwrapped payload).
let reader = reader.into_inner();
let writer = writer.into_inner();
stats.increment_connects_bad();
debug!(peer = %peer, "Valid TLS but invalid MTProto handshake");
debug!(
peer = %peer,
"Authenticated TLS session failed MTProto validation; engaging masking fallback"
);
handle_bad_client(
reader,
writer,
@@ -1044,3 +1060,7 @@ mod security_tests;
#[cfg(test)]
#[path = "client_adversarial_tests.rs"]
mod adversarial_tests;
#[cfg(test)]
#[path = "client_tls_mtproto_fallback_security_tests.rs"]
mod tls_mtproto_fallback_security_tests;