Skip to content

PR-SEC-1: Доп. харденинг и маскинг#454

Merged
axkurcom merged 3 commits intotelemt:flow-secfrom
DavidOsipov:pr-sec-1
Mar 17, 2026
Merged

PR-SEC-1: Доп. харденинг и маскинг#454
axkurcom merged 3 commits intotelemt:flow-secfrom
DavidOsipov:pr-sec-1

Conversation

@DavidOsipov
Copy link
Contributor

🛡 Основные изменения (Security & Anti-Censorship)

  • Исправление TCP Half-Close (FIN) в Masking-фоллбеке (masking.rs)
    • Проблема: Использование tokio::select! приводило к мгновенному прерыванию задачи чтения от маск-хоста, если клиент (например, HTTP-сканер цензора) отправлял запрос и сразу закрывал поток на запись (TCP Half-Close). Прокси рвал соединение до того, как Nginx успевал ответить, что является явным паттерном (fingerprint) прокси-сервера.
    • Решение: Заменено на tokio::join!(c2m, m2c). Теперь обе задачи корректно дожидаются завершения, позволяя fallback-серверу отправить легитимный HTTP-ответ сканеру. Защита от Slowloris обеспечивается глобальным таймаутом MASK_RELAY_TIMEOUT.
  • Защита от обхода Rate Limit через IPv6 (handshake.rs)
    • Проблема: Троттлинг AUTH_PROBE_STATE опирался на точное совпадение IpAddr. Атакующий с /64 подсетью (18 квинтиллионов адресов) мог обходить блокировку, инкрементируя младшие биты.
    • Решение: Внедрена функция normalize_auth_probe_ip, которая маскирует все IPv6-адреса до /64 перед записью в стейт пре-авторизации.
  • Защита от Cache Eviction Attack в Pre-Auth стейте (handshake.rs, middle_relay.rs)
    • Проблема: При заполнении DashMap до лимита (AUTH_PROBE_TRACK_MAX_ENTRIES) и отсутствии протухших ключей в первых 1024 элементах, логика переходила в состояние fail-closed — новые IP переставали отслеживаться, позволяя беспрепятственно брутфорсить хэндшейк.
    • Решение: Добавлен механизм гарантированного вытеснения (eviction_candidate). Если stale-ключи не найдены, принудительно удаляется случайный элемент из отсканированного батча, сохраняя работоспособность защиты под DDoS-нагрузкой.
  • Устранение Timing Side-Channel при проверке TLS (tls.rs)
    • Проблема: В цикле валидации секретов происходила аллокация памяти (user.clone(), session_id.clone()) при совпадении HMAC, что создавало микроскопический тайминг-оракул.
    • Решение: Аллокации и конструирование TlsValidation вынесены за пределы цикла. Внутри цикла теперь сохраняется только легковесная ссылка (&String, u32). Валидация стала строго constant-time.

⚡ Производительность и Архитектура (Data Plane)

  • Восстановление Zero-Allocation в Hot-Path (middle_relay.rs)
    • Проблема: В функции read_client_payload происходила аллокация BytesMut на куче и двойное копирование (extend_from_slice) для каждого MTProto-фрейма, что нарушало изоляцию Data Plane и приводило к деградации кэша CPU при высоком RPS.
    • Решение: Код переписан для чтения данных из сокета напрямую в буфер, полученный из BufferPool. Изменение размера буфера (reserve / resize) и последующий вызов freeze() позволяют передавать данные в Control Plane с нулевым копированием (zero-copy) и без дополнительных аллокаций.

📋 Чек-лист (Checklist)

  • Код компилируется без предупреждений (cargo check, cargo clippy).
  • Изменения не нарушают non-blocking инварианты Data Plane.
  • Криптографические операции (HMAC, сверки) выполняются за константное время (constant-time).
  • Логика корректно обрабатывает backpressure и не подвержена распределенным дедлокам.
  • Соответствует требованиям OWASP ASVS L2 (V2.1.7, V5.1.1, V11.1.4, V11.1.5).

Copilot AI review requested due to automatic review settings March 17, 2026 11:18
@DavidOsipov DavidOsipov changed the title feat(proxy): enhance auth probe handling with IPv6 normalization and … PR-SEC-1: Доп. харденинг и маскинг Mar 17, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens the proxy’s anti-censorship and security posture by hardening fallback relaying behavior, improving pre-auth probe throttling robustness (including IPv6 normalization), and tightening TLS validation behavior to reduce side-channel surface area.

Changes:

  • Adjust masking fallback relay to better handle client half-close / stalled directions while still allowing backend responses to reach the client.
  • Improve pre-auth auth-probe tracking by normalizing IPv6 to /64 and adding bounded eviction when the tracking map is full.
  • Refine data-plane payload reading to reduce copies, and extend security test coverage for these behaviors.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/proxy/middle_relay_security_tests.rs Adds/updates tests for dedup cache churn and payload parsing correctness.
src/proxy/middle_relay.rs Adds bounded eviction for desync dedup and refactors payload reads to use pooled buffers.
src/proxy/masking_security_tests.rs Adds regression tests for fallback relaying under stall/half-close conditions.
src/proxy/masking.rs Reworks relay loop to avoid early termination on client half-close by relaying both directions concurrently.
src/proxy/handshake_security_tests.rs Updates and extends tests for auth-probe throttling/eviction and IPv6 /64 bucketing behavior.
src/proxy/handshake.rs Implements IPv6 /64 normalization for auth-probe throttling and bounded eviction under full tracking state.
src/protocol/tls_security_tests.rs Adds additional TLS parsing/validation tests (session_id length edges, version rejection, overflow guards).
src/protocol/tls.rs Refactors TLS validation to avoid allocations in the secret-check loop (reducing timing side-channel risk).

You can also share your feedback on Copilot code review. Take the survey.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the proxy against censorship probing and resource-exhaustion patterns by improving masking fallback relay behavior, strengthening pre-auth throttling (especially for IPv6), and tightening TLS handshake validation to reduce side-channels—backed by expanded security regression tests.

Changes:

  • Update mask fallback relaying to tolerate client TCP half-close while still allowing backend responses to flow back to the client.
  • Strengthen pre-auth probe tracking by normalizing IPv6 peers to /64 buckets and adding bounded eviction under full-map pressure.
  • Refactor data-plane frame reads and TLS validation for more predictable behavior, with new/expanded regression tests.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/proxy/middle_relay.rs Adds bounded eviction for desync dedup, refactors client payload reads to use BufferPool and adjusts emitted payload construction.
src/proxy/middle_relay_security_tests.rs Adds regression tests for full-cache churn suppression and multiple read_client_payload framing/edge cases (including pool-return behavior).
src/proxy/masking.rs Replaces tokio::select! relay loop with tokio::join! + tokio::io::copy to preserve backend→client flow during client half-close/stalls.
src/proxy/masking_security_tests.rs Adds tests covering half-close behavior, one-direction stall behavior, and timeout-driven cancellation/drop of I/O endpoints.
src/proxy/handshake.rs Implements IPv6 /64 normalization for auth-probe throttling and adds deterministic bounded eviction when the probe map is full.
src/proxy/handshake_security_tests.rs Adds/updates tests for IPv6 bucketing, forced eviction behavior, and adjusts probe/throttle assertions/locking.
src/protocol/tls.rs Refactors TLS validation to avoid per-match allocations in the secret-validation loop (reducing timing side-channel signal).
src/protocol/tls_security_tests.rs Expands TLS tests for session_id length handling, record header version rejection, and ClientHello overflow/truncation edge cases.

You can also share your feedback on Copilot code review. Take the survey.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Security hardening and data-plane performance updates across the proxy stack, focusing on masking fallback robustness, pre-auth throttling resilience, and constant-time TLS validation.

Changes:

  • Fix masking relay behavior on client TCP half-close by running both directions to completion (bounded by a global timeout) and add regression tests.
  • Harden pre-auth probe throttling by normalizing IPv6 addresses to /64 and adding bounded eviction to prevent fail-closed behavior under cache pressure (plus concurrency/IPv6 coverage in tests).
  • Restore zero-copy payload handling in the middle relay hot path by switching client frame payloads to PooledBuffer and expanding related security/perf tests; remove TLS validation allocations from the secret-iteration loop.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/proxy/middle_relay.rs Switch C2ME payloads to PooledBuffer, add dedup bounded eviction, and rework client payload reads to reuse pool buffers.
src/proxy/middle_relay_security_tests.rs Update/enhance tests for pooled payload recycling, desync dedup churn behavior, and payload read edge cases.
src/proxy/masking.rs Replace tokio::select! relay loop with tokio::join! + io::copy to preserve backend responses across half-close.
src/proxy/masking_security_tests.rs Add regression tests for half-close behavior, stalled-direction behavior, and timeout cancellation dropping endpoints.
src/proxy/handshake.rs Normalize IPv6 probe IPs to /64, add bounded eviction for auth-probe tracking, and introduce a warned-secrets test lock.
src/proxy/handshake_security_tests.rs Add tests for IPv6 bucketing, forced eviction behavior, and concurrent probe update correctness.
src/protocol/tls.rs Avoid per-secret allocations on HMAC match by deferring TlsValidation construction until after iteration.
src/protocol/tls_security_tests.rs Expand TLS parsing/validation tests (prefix-length robustness, session_id length coverage, invalid record versions, overflow guards).

You can also share your feedback on Copilot code review. Take the survey.

@DavidOsipov DavidOsipov requested a review from Copilot March 17, 2026 12:33
.unwrap_or_else(|poisoned| poisoned.into_inner());
clear_auth_probe_state_for_testing();

let secret = [0x31u8; 16];

Check failure

Code scanning / CodeQL

Hard-coded cryptographic value Critical

This hard-coded value is used as
a key
.
}

fn make_crypto_writer(writer: tokio::io::DuplexStream) -> CryptoWriter<tokio::io::DuplexStream> {
let key = [0u8; 32];

Check failure

Code scanning / CodeQL

Hard-coded cryptographic value Critical

This hard-coded value is used as
a key
.

fn make_crypto_writer(writer: tokio::io::DuplexStream) -> CryptoWriter<tokio::io::DuplexStream> {
let key = [0u8; 32];
let iv = 0u128;

Check failure

Code scanning / CodeQL

Hard-coded cryptographic value Critical

This hard-coded value is used as
an initialization vector
.
@axkurcom axkurcom merged commit 4f55d08 into telemt:flow-sec Mar 17, 2026
8 of 10 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Security hardening and anti-fingerprinting updates across the proxy data plane and handshake/TLS validation paths, with accompanying security-focused regression tests.

Changes:

  • Updated masking fallback relay to avoid TCP half-close prematurely terminating backend→client responses (reduces proxy fingerprinting).
  • Hardened pre-auth rate limiting against IPv6 /64 churn and cache-eviction attacks (normalized bucketing + forced eviction).
  • Reworked middle relay client payload handling to use pooled buffers end-to-end (zero-copy/zero-allocation intent) and added extensive regression tests; adjusted TLS validation to avoid per-secret allocations in the match loop (timing side-channel reduction).

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/proxy/middle_relay_security_tests.rs Adds regression tests for pooled-buffer lifecycle, desync dedup suppression, and frame parsing behaviors.
src/proxy/middle_relay.rs Switches C→ME payloads to PooledBuffer, adds forced dedup eviction, and rewrites payload reads to fill pooled buffers directly.
src/proxy/masking_security_tests.rs Adds tests covering half-close behavior, backend response preservation, and timeout-driven cancellation/drop semantics.
src/proxy/masking.rs Replaces tokio::select! relay loop with concurrent tokio::join! + tokio::io::copy to preserve backend→client flow under client half-close/stall.
src/proxy/handshake_security_tests.rs Expands tests for forced eviction, IPv6 bucketing, and concurrent failure accounting under load.
src/proxy/handshake.rs Implements IPv6 /64 normalization, forced eviction when auth-probe map is full, and adds test-only lock for warned-secrets global state.
src/protocol/tls_security_tests.rs Adds edge-case tests for session_id lengths, TLS record header version validation, and SNI/ALPN overflow handling.
src/protocol/tls.rs Avoids per-secret allocations inside TLS secret validation loop by deferring TlsValidation construction.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +798 to +799
if current_cap < len {
payload.reserve(len - current_cap);
Comment on lines +795 to +802
let mut payload = buffer_pool.get();
payload.clear();
let current_cap = payload.capacity();
if current_cap < len {
payload.reserve(len - current_cap);
}
payload.resize(len, 0);
read_exact_with_timeout(client_reader, &mut payload[..len], frame_read_timeout).await?;
Comment on lines 146 to 165
@@ -123,23 +156,27 @@ fn auth_probe_record_failure_with_state(
state.remove(&stale_key);
}
if state.len() >= AUTH_PROBE_TRACK_MAX_ENTRIES {
return;
if eviction_candidates.is_empty() {
return;
}
let idx = auth_probe_eviction_offset(peer_ip, now) % eviction_candidates.len();
let evict_key = eviction_candidates[idx];
state.remove(&evict_key);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants