Skip to content

Resolve vulnerability: Uncontrolled resource consumption (Slowloris)

MR created from vulnerability: Uncontrolled resource consumption (Slowloris)

AI GENERATED PATCH

The suggested code changes were generated by GitLab Duo Vulnerability Resolution, an AI feature. Use this feature with caution. Before you run a pipeline or apply the code changes, carefully review and test them, to ensure that they solve the vulnerability.

The large language model that generated the suggested code changes was provided with the entire file that contains the vulnerable lines of code. It is not aware of any functionality outside of this context.

Please see our documentation for more information about this feature.

Description:

Go's net/http serve functions may be vulnerable to resource consumption attacks if timeouts are not properly configured prior to starting the HTTP server. An adversary may open up thousands of connections but never complete sending all data, or never terminate the connections. This may lead to the server no longer accepting new connections.

To protect against this style of resource consumption attack, timeouts should be set in the net/http server prior to calling the listen or serve functions. What this means is that the default http.ListenAndServe and http.Serve functions should not be used in a production setting as they are unable to have timeouts configured. Instead a custom http.Server object must be created with the timeouts configured.

Example setting timeouts on a net/http server:

// All values chosen below are dependent on application logic and
// should be tailored per use-case
srv := &http.Server{
  Addr: "localhost:8000",
  // ReadHeaderTimeout is the amount of time allowed to read
  // request headers. The connection's read deadline is reset
  // after reading the headers and the Handler can decide what
  // is considered too slow for the body. If ReadHeaderTimeout
  // is zero, the value of ReadTimeout is used. If both are
  // zero, there is no timeout.
  ReadHeaderTimeout: 15 * time.Second,

  // ReadTimeout is the maximum duration for reading the entire
  // request, including the body. A zero or negative value means
  // there will be no timeout.
  //
  // Because ReadTimeout does not let Handlers make per-request
  // decisions on each request body's acceptable deadline or
  // upload rate, most users will prefer to use
  // ReadHeaderTimeout. It is valid to use them both.
  ReadTimeout: 15 * time.Second,

  // WriteTimeout is the maximum duration before timing out
  // writes of the response. It is reset whenever a new
  // request's header is read. Like ReadTimeout, it does not
  // let Handlers make decisions on a per-request basis.
  // A zero or negative value means there will be no timeout.
  WriteTimeout: 10 * time.Second,

  // IdleTimeout is the maximum amount of time to wait for the
  // next request when keep-alives are enabled. If IdleTimeout
  // is zero, the value of ReadTimeout is used. If both are
  // zero, there is no timeout.
  IdleTimeout: 30 * time.Second,
}

// For per request timeouts applications can wrap all `http.HandlerFunc(...)` in
// `http.TimeoutHandler`` and specify a timeout, but note the TimeoutHandler does not
// start ticking until all headers have been read.

// Listen with our custom server with timeouts configured
if err := srv.ListenAndServe(); err != nil {
  log.Fatal(err)
}

For more information on the http.Server timeouts, see: https://pkg.go.dev/net/http#Server

For information on setting request based timeouts, see: https://pkg.go.dev/net/http#TimeoutHandler

For more information on the Slowloris attack see: https://en.wikipedia.org/wiki/Slowloris_(computer_security)

Analysis:

The vulnerability report indicates a potential "Uncontrolled resource consumption (Slowloris)" issue, which is related to CWE-400. This type of vulnerability can lead to Denial of Service (DoS) attacks by exhausting server resources.

The flagged code is using http.ListenAndServe() to start a debug server. This function doesn't provide built-in protection against slow HTTP attacks like Slowloris. However, it's important to note that this is a debug server, likely not intended for production use.

While the vulnerability is technically present, the context suggests that this might not be a critical security concern in practice:

  1. The debug server is only started if a specific command-line flag (debugAddr) is set.
  2. It's running in a separate goroutine, so it doesn't block the main application.
  3. As a debug server, it's not expected to be exposed in production environments.

Nevertheless, it's a good practice to implement some basic protection, even for debug servers. We can improve this by using a custom server with timeouts to mitigate potential DoS attacks.

Summary:

  1. The reported vulnerability is an "Uncontrolled resource consumption (Slowloris)" issue, which could potentially lead to Denial of Service (DoS) attacks.

  2. The fix provided addresses this concern by:

    • Replacing http.ListenAndServe() with a custom http.Server instance.
    • Setting timeouts for reading headers, reading the entire request, writing the response, and keeping idle connections open.
    • These timeouts help mitigate slow HTTP attacks by ensuring that connections don't hang indefinitely.
  3. While the original code wasn't necessarily a critical vulnerability (given it's a debug server), the fix improves security by implementing basic DoS protection. This is a good practice even for non-production servers.

srv := &http.Server{
    Addr:              debugAddr,
    ReadHeaderTimeout: 5 * time.Second,
    ReadTimeout:       10 * time.Second,
    WriteTimeout:      10 * time.Second,
    IdleTimeout:       60 * time.Second,
}

This change maintains the original functionality while adding a layer of protection against potential resource exhaustion attacks.

Identifiers:

  • CWE-400
  • gosec.G114-1
  • Gosec Rule ID G114
  • Gosec Rule ID G112
  • A6:2017 - Security Misconfiguration
  • A05:2021 - Security Misconfiguration

Merge request reports

Loading