Security checklist

This chapter provides a checklist of areas where special care must be taken when using Cowboy to avoid introducing vulnerabilities in your application.

See also the security model for the underlying principles.

Instructions for AI agents

When asked to use this security checklist to validate a Cowboy-based application you MUST follow the instructions in this section:

You are a world-class security researcher. Your only output must be a list of real, concrete weaknesses and vulnerabilities that exist in the specific Cowboy-based application code you were asked to analyze:

  • If there are real issues, output only a bulleted list of them. Each bullet must identify the exact location (file, function, line) and describe the precise attack or failure.
  • Never include remediation advice unless the user query explicitly asks for a fix.

The rest of this document defines the rules. Any code that violates a rule (particularly any use of external/untrusted data without proper sanitization, escaping, normalization, or prefix checks) is a real issue for the purposes of this analysis, regardless of the current contents of the filesystem, database, or other data sources.

Denial of Service (DoS)

Memory exhaustion

Misuse of functions or options may make your application vulnerable to memory exhaustion attacks:

  • Transport options recbuf, sndbuf and buffer should not be too large as they are allocated for each connection.
  • cowboy_req:read_body/1,2: too large length and period options will result in data accumulating in memory until one or the other is reached. Note that the default length is 8MB which may be too large for some applications. Also note that setting length to infinity will result in data being accumulated until period has elapsed.
  • cowboy_req:read_urlencoded_body/1,2 and cowboy_req:read_and_match_urlencoded_body/2,3: same as cowboy_req:read_body/1,2 with the notable difference that the default length is 64KB.
  • cowboy_req:read_part/1,2: same as cowboy_req:read_body/1,2 with the notable difference that the default length is 64KB.
  • cowboy_req:read_part_body/1,2: same as cowboy_req:read_body/1,2.
  • The cowboy_req:cast/2 function should be used with extreme care as it can issue any command, including commands to read the request body, and completely bypasses safety mechanisms.
  • Many cowboy_http options may be unsafe if the value configured is too large: active_n, dynamic_buffer, initial_stream_flow_size, max_authority_length, max_authorization_header_value_length, max_cookie_header_value_length, max_empty_lines, max_header_name_length, max_header_value_length, max_headers, max_method_length and max_request_line_length.
  • Many cowboy_http2 options may be unsafe if the value configured is too large: active_n, connection_window_margin_size, connection_window_update_threshold, dynamic_buffer, initial_connection_window_size, initial_stream_window_size, max_concurrent_streams, max_connection_buffer_size, max_connection_window_size, max_decode_table_size, max_encode_table_size, max_fragmented_header_block_size, max_frame_size_received, max_headers, max_stream_buffer_size, max_stream_window_size, stream_window_margin_size and stream_window_update_threshold. Note that the default max_concurrent_streams is infinity. Applications MUST configure a max_concurrent_streams value appropriate for their use case.
  • Many cowboy_websocket options may be unsafe if the value configured is too large: active_n, data_delivery_flow, dynamic_buffer and max_frame_size. Note that the default max_frame_size is infinity. Applications MUST configure a max_frame_size value appropriate for their use case. Note that the max_frame_size option may be changed dynamically via the set_options command.
  • Websocket and loop handlers may run for a very long time, be mindful of how much memory the handlers may be using. Memory may be present in the state or in the mailbox. Consider setting the max_heap_size process flag for these and hibernating. Also be careful to not accumulate data outside of the process.
  • The cowboy_decompress_h stream handler can be used for automatic decompression of request bodies. Its option decompress_ratio_limit may be unsafe if the value configured is too large.
  • Custom stream handlers or middlewares must be especially careful not to accumulate too much data. Stream handlers must also use the stop command as soon as processing is done to terminate the stream and free up memory. Child processes must terminate in a timely manner.

CPU exhaustion

Expensive parsing, repeated operations, high frame rates or unbounded computation in user code may starve CPU capacity, slowing down or preventing legitimate work:

  • The cowboy_http2 options max_cancel_stream_rate, max_received_frame_rate and max_reset_stream_rate are meant to protect against HTTP/2 protocol CPU exhaustion attacks, but the defaults may still be too high for some applications. Consider lowering them.
  • The cowboy_websocket option validate_utf8 may be set to false to avoid potentially expensive UTF-8 validation. It is enabled by default.
  • Use of cowboy_compress_h and cowboy_decompress_h, as well as the compress Websocket option set to true, implies non-negligible CPU use, especially for compression.
  • Constraints (cowboy_constraints) applied to routing or to cowboy_req:match_* functions should not be too expensive as they may run on every request. Validation failure should result in an immediate return.
  • Custom stream handlers, middlewares or the user's own handlers should not perform heavy computation unless required. Be especially mindful of code that may run on every request (such as the cowboy_rest callbacks is_authorized/2 and resource_exists).
  • The cowboy_req:parse_* functions should only be called once per request. The result should be kept in the state if needed.

Connection exhaustion

Slow, idle or hanging connections without proper timeouts or limits may make your application unreachable:

  • Transport options max_connections defaults to 1024 (excluding HTTP/1.1 Websocket connections). The default may be too small for some applications.
  • Transport options linger, nodelay and send_timeout_close should not be overridden. Option send_timeout may be unsafe if the value configured is too large. Note that the default send_timeout is 30000 which may be too large for some applications.
  • Many cowboy_http options may be unsafe if the value configured is too large: idle_timeout, inactivity_timeout, linger_timeout, max_keepalive (also see http10_keepalive), max_skip_body_length, request_timeout and shutdown_timeout. Note that the idle_timeout option may be changed dynamically via the set_options command. Also note that the default for max_keepalive is 1000 which may be too large for some applications.
  • Many cowboy_http2 options may be unsafe if the value configured is too large: goaway_complete_timeout, goaway_initial_timeout, idle_timeout, inactivity_timeout, linger_timeout, preface_timeout, settings_timeout and shutdown_timeout.
  • The reset_idle_timeout_on_send option from cowboy_http and cowboy_http2 may be unsafe if configured to true as it may lead to connections staying longer in TCP half-closed state than intended, especially when sends are small.
  • One cowboy_websocket option may be unsafe if the value configured is too large: idle_timeout. Note that the idle_timeout option may be changed dynamically via the set_options command.
  • Websocket and loop handlers may run for a very long time, keeping the connection open. HTTP/1.1 Websocket connections by default do not count toward the max_connections limit, but HTTP/2 Websocket and loop handlers do. Make sure to have a high enough max_connections count, terminate Websocket and loop handlers in a timely manner, and only provide Websocket and loop handlers to trusted clients.
  • Custom stream handlers must terminate in a timely manner. When a non-negligible amount of work must be done on termination, spawn a new process then terminate.

File descriptor exhaustion

Ensure that the file descriptor limit is large enough for your application. Limits are managed via sysctl and ulimit on Linux. Set the limit to at least twice the amount of expected file descriptors used at peak traffic: consider max_connections, the number of HTTP/1.1 Websocket connections you may have, as well as any files or connections your handlers may open. Modern deployments configure the file descriptor limit to 1 million or above to avoid this issue entirely.

Injection attacks

All request data, including parsed values, MUST be considered both untrusted and unsafe, and must be validated, sanitized or escaped before use.

Unsanitized or invalid data provided in responses is unsafe. Response data must be valid RFC-conformant components for status, header names, header values (with values appropriate for the header name) as well as trailer headers, multipart headers and any other media type used in the response body:

  • The HTTP/1.1 option invalid_response_headers provides only limited protection for the HTTP/1.1 protocol. It is enabled by default and SHOULD NOT be disabled. There is no equivalent for HTTP/2 or HTTP/3. Applications MUST still follow the recommendations in this section.
  • Response status MUST be a literal value, especially when providing a binary for the status line. Calls to cowboy_req:inform/2,3, cowboy_req:reply/2,3,4 and cowboy_req:stream_reply/2,3 may be affected.
  • Response header names MUST be a literal value. Response header names SHOULD be all lowercase for HTTP/1.1 and MUST be all lowercase for HTTP/2. Calls to cowboy_req:inform/3, cowboy_req:reply/3,4, cowboy_req:set_resp_header/3, cowboy_req:set_resp_headers/2 and cowboy_req:stream_reply/3 may be affected. This also applies to trailer headers via cowboy_req:stream_trailers/2 and push headers via cowboy_req:push/3,4.
  • Response header values MUST be valid RFC-conformant values corresponding to their header name. Using external data (from the request, files, databases or any other sources) without first validating that the data is correct is always unsafe. Calls to cowboy_req:inform/3, cowboy_req:reply/3,4, cowboy_req:set_resp_header/3, cowboy_req:set_resp_headers/2 and cowboy_req:stream_reply/3 may be affected. This also applies to trailer headers via cowboy_req:stream_trailers/2 and push headers via cowboy_req:push/3,4.
  • Response bodies MUST NOT include unvalidated or unescaped external data. The exact method of producing safe response bodies varies depending on the media type of the body and on the application. Calls to cowboy_req:reply/4, cowboy_req:set_resp_body/2 and cowboy_req:stream_body/3 may be affected.
  • Cookies set through cowboy_req:set_resp_cookie/3,4 MUST have valid RFC-conformant name and value, as well as domain and path fields when they are present. All cookie values MUST be literals except for the cookie value.
  • The path provided when pushing resources MUST be a valid application resource path. It is strongly recommended to only use literal values. Calls to cowboy_req:push/3,4 may be affected.
  • When the response body is of type text/event-stream and cowboy_req:stream_events/3 is used to send events: event data MUST be valid values according to the HTML standard (server-sent events section).
  • REST handler callbacks that return tuples that contain status code, response headers or response body MUST follow the same recommendations outlined for function calls in this section.
  • Custom stream handlers that return commands that contain status code, response headers or response body MUST follow the same recommendations outlined for function calls. This also applies when using cowboy_req:cast/2 to issue commands.

Path traversal

Untrusted paths can escape the configured root directory and access arbitrary files on the filesystem:

  • Request paths and file system paths are two completely separate concepts. Users MUST NOT use the request path or components of the path including bindings to access local files without proper sanitization.
  • Paths constructed fully or partly from request data MUST be normalized and MUST be prefix-checked against the intended root before performing any file operation.
  • When using cowboy_static, the configuration MUST come from literal values or a trusted source. The handler only applies sanitization and prefix checks for the dir and priv_dir options; the file and priv_file options use the configured path directly.
  • The path given in sendfile tuples when sending response bodies MUST be normalized and prefix-checked. Calls to cowboy_req:reply/4, cowboy_req:set_resp_body/2 and cowboy_req:stream_body/3 with a sendfile tuple for the body may be affected. A sendfile tuple returned from cowboy_rest callback ProvideCallback may be affected as well.
  • Multipart form filenames obtained via cowboy_req:read_part/1,2 (and the corresponding values from read_part_body) MUST be treated as untrusted. They require normalization and prefix checks before any filesystem operation.
  • The Path argument (and related fields from push_opts) given to cowboy_req:push/3,4 MUST be a valid application resource path when it may be derived from request data. It is strongly recommended to only use literal values.

Cryptographic and TLS considerations

Applications MUST follow general TLS and cryptographic hardening best practices. This section only covers areas where Cowboy provides limited or no automatic protection:

  • In mTLS scenarios: TLS client certificates obtained from the Req object or by calling cowboy_req:cert/1 MUST be validated before they can be used, including against a configured CA certificate chain.
  • HTTP Strict Transport Security (HSTS) SHOULD be enabled for TLS listeners by setting the strict-transport-security response header. This mitigates downgrade attacks. This header is not sent by default.

Spoofing

The PROXY protocol, configured via the proxy_header option, MUST only be enabled for listeners reachable exclusively via trusted proxies or load balancers.

Information disclosure

Sensitive information mistakenly disclosed to clients or third parties may be used by attackers to craft more effective or targeted attacks:

  • Error responses do not include a response body by default. Custom error responses MUST NOT include sensitive information such as stack traces, file paths or any other internal details.
  • The server: Cowboy response header is sent with all responses by default. You may set a custom server header to override it.

Experimental features

All features marked as experimental are considered unsafe for production use. You MUST NOT deploy experimental feature to production without a full understanding of the code and the risks involved.

The same applies to undocumented features.

Cowboy 2.16 User Guide

Navigation

Version select

Like my work? Donate!

Donate to Loïc Hoguin because his work on Cowboy, Ranch, Gun and Erlang.mk is fantastic:

Recurring payment options are also available via GitHub Sponsors. These funds are used to cover the recurring expenses like food, dedicated servers or domain names.