Synchronous Access of Remote Resource without Timeout

Description

Synchronous Access of Remote Resource without Timeout occurs when code makes a synchronous call to a remote resource without implementing a timeout mechanism, or sets the timeout to infinite. When a remote server becomes unresponsive, slow, or unreachable, the calling thread blocks indefinitely waiting for a response. This can cause application hangs, thread pool exhaustion, and denial of service conditions.

Risk

Missing timeouts on remote calls have direct security implications. Attackers can cause denial of service by making remote services unresponsive. Thread pool exhaustion prevents legitimate requests from being processed. Connection pool exhaustion blocks all remote connectivity. Cascading failures can bring down entire systems when one service hangs. Attackers can exploit slow or hanging endpoints to consume server resources. The predictable blocking behavior enables resource exhaustion attacks.

Solution

Always set appropriate timeouts on all remote resource access. Configure both connection timeout (time to establish connection) and read timeout (time to receive response). Set reasonable timeout values based on expected response times and SLAs. Implement circuit breakers to prevent repeated calls to failing services. Use asynchronous I/O with timeouts where possible. Implement retry logic with exponential backoff. Monitor for timeout events to detect service issues. Configure timeouts at multiple levels (application, framework, OS). Test timeout behavior explicitly.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS: Resource Consumption - Blocked threads consume resources and exhaust thread pools.
AvailabilityScope: Availability

DoS: Hang - Application becomes unresponsive waiting for remote resource.
OtherScope: Other

Reduce Reliability - Cascading failures occur when services hang waiting for other services.

Example Code

Vulnerable Code

// Vulnerable: HTTP request without timeout
public class VulnerableHttpClient {

    public String fetchData(String url) throws Exception {
        // Vulnerable: No timeout configured
        URL urlObj = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();

        // Vulnerable: No connect or read timeout set!
        // If server hangs, this thread blocks forever

        conn.setRequestMethod("GET");

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream()))) {

            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        }
    }

    // Vulnerable: Socket without timeout
    public String sendTcpRequest(String host, int port, String message)
            throws Exception {
        // Vulnerable: No connection timeout
        Socket socket = new Socket(host, port);

        // Vulnerable: No read timeout
        // socket.setSoTimeout(0) is default - infinite!

        PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(
            new InputStreamReader(socket.getInputStream()));

        out.println(message);
        return in.readLine();  // Blocks forever if no response
    }
}
# Vulnerable: HTTP and socket requests without timeout
import requests
import socket
import urllib.request

class VulnerableClient:

    def fetch_http(self, url):
        # Vulnerable: No timeout parameter
        response = requests.get(url)  # Can hang forever
        return response.text

    def fetch_urllib(self, url):
        # Vulnerable: No timeout in urlopen
        with urllib.request.urlopen(url) as response:  # Hangs if no response
            return response.read()

    def connect_socket(self, host, port):
        # Vulnerable: No timeout on socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # Default timeout is None - blocks forever
        sock.connect((host, port))  # Hangs if host unresponsive
        return sock

    def fetch_database(self, query):
        # Vulnerable: Database connection without timeout
        import psycopg2
        conn = psycopg2.connect(
            host="db.example.com",
            database="mydb",
            user="user",
            password="pass"
            # No connect_timeout specified!
        )
        cursor = conn.cursor()
        cursor.execute(query)  # Hangs if query takes forever
        return cursor.fetchall()
// Vulnerable: .NET HTTP client without timeout
public class VulnerableWebClient
{
    public async Task<string> FetchDataAsync(string url)
    {
        // Vulnerable: HttpClient with infinite timeout
        using var client = new HttpClient();
        client.Timeout = Timeout.InfiniteTimeSpan;  // Explicitly infinite!

        var response = await client.GetAsync(url);
        return await response.Content.ReadAsStringAsync();
    }

    public string FetchDataSync(string url)
    {
        // Vulnerable: WebClient has no timeout by default
        using var client = new WebClient();
        return client.DownloadString(url);  // Blocks forever
    }

    public string ConnectTcp(string host, int port)
    {
        // Vulnerable: TcpClient without timeout
        using var client = new TcpClient();
        client.Connect(host, port);  // No timeout - hangs forever

        using var stream = client.GetStream();
        using var reader = new StreamReader(stream);
        return reader.ReadLine();  // No read timeout
    }
}
// Vulnerable: Node.js HTTP without timeout
const http = require('http');
const net = require('net');

class VulnerableClient {

    fetchData(url) {
        return new Promise((resolve, reject) => {
            // Vulnerable: No timeout configured
            http.get(url, (res) => {
                let data = '';
                res.on('data', chunk => data += chunk);
                res.on('end', () => resolve(data));
            }).on('error', reject);
            // Request can hang forever!
        });
    }

    connectSocket(host, port) {
        return new Promise((resolve, reject) => {
            // Vulnerable: Socket without timeout
            const socket = net.connect({ host, port }, () => {
                resolve(socket);
            });
            // No timeout set - connection attempt hangs forever
        });
    }

    async fetchWithAxios(url) {
        const axios = require('axios');
        // Vulnerable: No timeout in config
        const response = await axios.get(url);
        return response.data;
    }
}

Fixed Code

// Fixed: HTTP and socket requests with proper timeouts
public class FixedHttpClient {

    private static final int CONNECT_TIMEOUT_MS = 5000;  // 5 seconds
    private static final int READ_TIMEOUT_MS = 30000;    // 30 seconds

    public String fetchData(String url) throws Exception {
        URL urlObj = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();

        // Fixed: Set connection and read timeouts
        conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
        conn.setReadTimeout(READ_TIMEOUT_MS);

        conn.setRequestMethod("GET");

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream()))) {

            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        }
    }

    // Fixed: Socket with proper timeouts
    public String sendTcpRequest(String host, int port, String message)
            throws Exception {

        // Fixed: Connection with timeout
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(host, port), CONNECT_TIMEOUT_MS);

        // Fixed: Read timeout
        socket.setSoTimeout(READ_TIMEOUT_MS);

        try {
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));

            out.println(message);
            return in.readLine();  // Will throw SocketTimeoutException if timeout
        } finally {
            socket.close();
        }
    }

    // Fixed: Using OkHttp with timeouts
    public String fetchWithOkHttp(String url) throws Exception {
        OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
            .readTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS)
            .writeTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS)
            .build();

        Request request = new Request.Builder()
            .url(url)
            .build();

        try (Response response = client.newCall(request).execute()) {
            return response.body().string();
        }
    }
}
# Fixed: Python requests with proper timeouts
import requests
import socket
import urllib.request

class FixedClient:

    CONNECT_TIMEOUT = 5    # 5 seconds
    READ_TIMEOUT = 30      # 30 seconds
    TOTAL_TIMEOUT = (CONNECT_TIMEOUT, READ_TIMEOUT)

    def fetch_http(self, url):
        # Fixed: Tuple timeout (connect, read)
        response = requests.get(url, timeout=self.TOTAL_TIMEOUT)
        return response.text

    def fetch_urllib(self, url):
        # Fixed: timeout parameter
        with urllib.request.urlopen(url, timeout=self.READ_TIMEOUT) as response:
            return response.read()

    def connect_socket(self, host, port):
        # Fixed: Socket with timeout
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(self.CONNECT_TIMEOUT)
        sock.connect((host, port))
        sock.settimeout(self.READ_TIMEOUT)  # Read timeout
        return sock

    def fetch_database(self, query):
        import psycopg2
        # Fixed: Connection timeout and statement timeout
        conn = psycopg2.connect(
            host="db.example.com",
            database="mydb",
            user="user",
            password="pass",
            connect_timeout=self.CONNECT_TIMEOUT,
            options=f"-c statement_timeout={self.READ_TIMEOUT * 1000}"
        )
        cursor = conn.cursor()
        cursor.execute(query)
        return cursor.fetchall()

    # Fixed: Async with timeout
    async def fetch_async(self, url):
        import aiohttp
        import asyncio

        timeout = aiohttp.ClientTimeout(
            total=self.READ_TIMEOUT,
            connect=self.CONNECT_TIMEOUT
        )

        async with aiohttp.ClientSession(timeout=timeout) as session:
            async with session.get(url) as response:
                return await response.text()
// Fixed: .NET HTTP client with proper timeouts
public class FixedWebClient
{
    private static readonly TimeSpan ConnectTimeout = TimeSpan.FromSeconds(5);
    private static readonly TimeSpan ReadTimeout = TimeSpan.FromSeconds(30);

    // Fixed: Reusable HttpClient with timeout
    private static readonly HttpClient _httpClient = new HttpClient
    {
        Timeout = ReadTimeout
    };

    public async Task<string> FetchDataAsync(string url,
        CancellationToken cancellationToken = default)
    {
        // Fixed: HttpClient with timeout and cancellation
        using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        cts.CancelAfter(ReadTimeout);

        var response = await _httpClient.GetAsync(url, cts.Token);
        return await response.Content.ReadAsStringAsync();
    }

    public async Task<string> ConnectTcpAsync(string host, int port)
    {
        using var client = new TcpClient();

        // Fixed: Connect with timeout using Task.WhenAny
        var connectTask = client.ConnectAsync(host, port);
        var timeoutTask = Task.Delay(ConnectTimeout);

        var completedTask = await Task.WhenAny(connectTask, timeoutTask);
        if (completedTask == timeoutTask)
        {
            throw new TimeoutException($"Connection to {host}:{port} timed out");
        }

        await connectTask;  // Propagate any exceptions

        // Fixed: Read with timeout
        using var stream = client.GetStream();
        stream.ReadTimeout = (int)ReadTimeout.TotalMilliseconds;

        using var reader = new StreamReader(stream);
        return await reader.ReadLineAsync();
    }
}
// Fixed: Node.js HTTP with timeouts
const http = require('http');
const https = require('https');
const net = require('net');
const axios = require('axios');

class FixedClient {

    constructor() {
        this.connectTimeout = 5000;   // 5 seconds
        this.readTimeout = 30000;     // 30 seconds
    }

    fetchData(url) {
        return new Promise((resolve, reject) => {
            const request = http.get(url, (res) => {
                let data = '';
                res.on('data', chunk => data += chunk);
                res.on('end', () => resolve(data));
            });

            // Fixed: Connection timeout
            request.setTimeout(this.connectTimeout, () => {
                request.destroy();
                reject(new Error('Connection timeout'));
            });

            request.on('error', reject);
        });
    }

    connectSocket(host, port) {
        return new Promise((resolve, reject) => {
            const socket = net.connect({ host, port });

            // Fixed: Connection timeout
            socket.setTimeout(this.connectTimeout);

            socket.on('connect', () => {
                // Fixed: Switch to read timeout
                socket.setTimeout(this.readTimeout);
                resolve(socket);
            });

            socket.on('timeout', () => {
                socket.destroy();
                reject(new Error('Socket timeout'));
            });

            socket.on('error', reject);
        });
    }

    async fetchWithAxios(url) {
        // Fixed: Axios with timeout
        const response = await axios.get(url, {
            timeout: this.readTimeout,
            // Optional: AbortController for cancellation
            signal: AbortSignal.timeout(this.readTimeout)
        });
        return response.data;
    }
}

CVE Examples

Missing timeout vulnerabilities have caused denial-of-service conditions in many applications, though specific CVEs typically describe the resulting impact rather than specifically citing missing timeouts.


  • CWE-821: Incorrect Synchronization (parent)
  • CWE-400: Uncontrolled Resource Consumption (can lead to)
  • CWE-835: Loop with Unreachable Exit Condition (similar concept)

References

  1. MITRE Corporation. "CWE-1088: Synchronous Access of Remote Resource without Timeout." https://cwe.mitre.org/data/definitions/1088.html
  2. OWASP. "Testing for Denial of Service."
  3. Circuit Breaker Pattern. "Release It!" by Michael Nygard.