Skip to content

Conversation

rdlowrey
Copy link
Contributor

Client-Initiated Renegotiation DoS Vulnerability

The idea of using SSL/TLS renegotiation as a DoS attack vector was first widely publicized in 2011 with the release of the TLC-SSL-DOS tool. The attack vector is plausible because establishing SSL connections requires disproportionately more processing resources for servers than for clients (Bernat). This gap in resource consumption creates an easy target for nefarious clients looking to exhaust server resources through DoS attacks in which the TLS handshake is repeatedly renegotiated.

At this time encrypted stream servers have no defense against malicious client-initiated TLS renegotiation.

Patch

This patch uses a standard leaky bucket algorithm to automatically rate-limit client-initiated TLS renegotiations in encrypted stream servers (allowing two renegs every 300 seconds by default). This feature can be customized using the following new SSL context options:

  • "reneg_limit" (the number of allowed renegotiations per time window)
  • "reneg_window" (the size of the renegotiation time window in seconds)

Most servers shouldn't need to customize these values as limiting applies automatically. Additionally, users may specify the following context option to receive notifications when a stream client is rate-limited due to a reneg policy violation:

  • "reneg_limit_callback"

Servers can prevent automatic closing of a client stream upon reneg policy violation by returning true from the callback. In real DoS situations this can be useful to shutdown reads on the client socket without immediately closing it, add the client IP to a blocklist, etc. If no callback is specified or if the callback returns any value other than true PHP will shutdown the client connection immediately. Note that if no callback is assigned an E_WARNING is generated to inform the script of the violation.

Example

<?php
$bindTo = 'tls://127.0.0.1:443';
$flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$ctx = stream_context_create(['ssl' => [
    "local_cert" => "/path/to/cert.pem",
    "reneg_limit" => 0, // don't allow any renegotiations
    "reneg_limit_callback" => function($client) {
        printf("Client socket at %s violated the reneg policy!\n", 
            stream_socket_get_name($client, $want_peer = true));

        stream_socket_shutdown($client, STREAM_SHUT_RD);

        // Tell PHP not to close the client (we should schedule an
        // event or something to handle this later)
        return true;
    }
]]);
$server = stream_socket_server($bindTo, $errNo, $errStr, $flags, $ctx);

@php-pulls php-pulls merged commit b6edbd5 into php:master Feb 21, 2014
@rdlowrey rdlowrey deleted the reneg-limit branch February 21, 2014 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants