Skip to content

[security] Relay bootstrap HTTP endpoint susceptible to slow-read DoS #176

@intendednull

Description

@intendednull

Problem

The relay's bootstrap ID HTTP handler (crates/relay/src/lib.rs:196-222) reads the request line byte-by-byte with a per-read timeout of BOOTSTRAP_IO_TIMEOUT = 5 seconds but no total connection time limit. The connection count is bounded by MAX_CONCURRENT_BOOTSTRAP_CONNECTIONS = 1024 via a semaphore.

A Slowloris-style attack can exhaust all 1024 slots:

  1. Attacker opens 1024 TCP connections to the bootstrap port
  2. Each connection sends 1 byte every ~4.9 seconds (just under the 5s per-read timeout)
  3. All connections stay alive indefinitely, consuming semaphore permits
  4. Legitimate clients cannot fetch the bootstrap node ID → new peers cannot discover the relay

Severity

MEDIUM — DoS on bootstrap discovery. Does not affect already-connected peers, but prevents new peers from joining.

Fix

Add a total connection deadline separate from the per-read timeout:

let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
// In the read loop:
let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
if remaining.is_zero() {
    return Err(std::io::Error::new(
        std::io::ErrorKind::TimedOut,
        "total connection time exceeded",
    ));
}
let timeout = remaining.min(BOOTSTRAP_IO_TIMEOUT);

Also consider reducing MAX_CONCURRENT_BOOTSTRAP_CONNECTIONS and adding per-IP connection limits.

Locations

  • crates/relay/src/lib.rs:59MAX_CONCURRENT_BOOTSTRAP_CONNECTIONS = 1024
  • crates/relay/src/lib.rs:75BOOTSTRAP_IO_TIMEOUT = 5 seconds
  • crates/relay/src/lib.rs:196-222 — request reading loop

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions