denoland / deno

A modern runtime for JavaScript and TypeScript.
https://deno.com
MIT License
98.05k stars 5.39k forks source link

Default timeouts and size limits for `Deno.serve` #3515

Open anjmao opened 4 years ago

anjmao commented 4 years ago

Deno tries to have similar standard library as Go which is great, but... Keep in mind that event Go authors made some mistakes when initially developed std. For example by default HTTP server and client doesn't add any timeouts. In order to have production ready HTTP server in Go you would want to add Read/Write timeouts.

s := &http.Server{
    Addr:           ":8080",
    Handler:        myHandler,
    ReadTimeout:    10 * time.Second,
    WriteTimeout:   10 * time.Second,
    MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())

Back to deno. For now there is only few options available.

interface ServerConfig {
  port: number;
  hostname?: string;
}

Example for slow client attack which creates new TCP connections on the server and by slowly reading response body doesn't close it which would eventually lead to server out of file descriptors errors.

Deno server

import { serve } from "https://deno.land/std@v0.24.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");

async function main() {
    for await (const req of s) {
        console.log('request:', req);
        const body = new TextEncoder().encode(`Hello World ${Date.now()}\n`);
        req.respond({ body });
        console.log('response:', body.length);
    }
}

main()

Go test client

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    client := &http.Client{}
    for {
        go func() {
            req, err := http.NewRequest("POST", "http://localhost:8000", nil)
            if err != nil {
                panic(err)
            }
            res, err := client.Do(req)
            if err != nil {
                panic(err)
            }
            defer res.Body.Close()
            // Simulate slow clean attack by reading response body slowly.
            buf := make([]byte, 1)
            for {
                _, err := res.Body.Read(buf)
                if err != nil {
                    fmt.Println(err)
                    break
                }
                time.Sleep(100 * time.Second)
            }
        }()

        time.Sleep(1 * time.Second)
    }
}

Check established connections with lsof -p <PID>

// 53
deno    19997 anjmao  177u    IPv4 0x35f50d8024691719      0t0      TCP localhost:irdmi->localhost:61961 (ESTABLISHED)
deno    19997 anjmao  178u    IPv4 0x35f50d80249ec3b1      0t0      TCP localhost:irdmi->localhost:61963 (ESTABLISHED)
deno    19997 anjmao  179u    IPv4 0x35f50d80249eb0a1      0t0      TCP localhost:irdmi->localhost:61965 (ESTABLISHED)
deno    19997 anjmao  180u    IPv4 0x35f50d8024a4a0a1      0t0      TCP localhost:irdmi->localhost:61967 (ESTABLISHED)
// 58 
// ...
// 209
F3n67u commented 3 years ago

@anjmao Can you confirm this is still an issue?

dkfdkdodkms commented 1 year ago

Anyone know if this is still an issue? Seems like a security risk to me..

petamoriken commented 1 year ago

Does AbortSignal.timeout resolve this issue?

https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static

dkfdkdodkms commented 1 year ago

I don't believe so, since that would be on the client to do. I appears this issues has been mislabeled as a feature. Instead, this is a significant DOS vulnerability.

dkfdkdodkms commented 1 year ago

Amazing this has been open since 2019: https://www.cloudflare.com/learning/ddos/ddos-attack-tools/slowloris/

lucacasonato commented 2 months ago

Deno.serve should have default limits for both accept timeout, TLS handshake timeout, and header read timeout, and response write timeout.

Deno.serve should also have default size limits for req.text, req.bytes, req.arrayBuffer, req.formData, and req.blob. When a user makes a request with a larger body than this default value, we'd error the request. req.body would not be subject to these limits.

The user could customize both timeouts and the size limit in Deno.serve:

Deno.serve({
  maxBodySize: 16 * 1024 * 1024, // 16MiB,
  acceptTimeout: 10000, // 10s
  tlsHandshakeTimeout: 20000, // 20s
  headerReadTimeout: 10000, // 10s,
  responseWriteTimeout: null, // no default
});
fonteneu commented 3 weeks ago

Timeouts for TCP are similarly missing, making Deno a complete no-go for secure and robust networking applications