1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# Based on 'Mitigating Slow HTTP Post DDoS Attacks With iRules' from George Watkins # # Requires LTM v10.0+ for the after command when RULE_INIT { # This iRule enforces a minimum length of time ($static::timeout) for a client to send POST request data. # The initial values are 2Kb / 2 sec = 1 Kb/s for the first 2Kb. These values should be tailored for the client base. # Default amount of request payload to collect (in bytes). # This is the maximum amount of content we will collect. # Clients will still be able to send unlimited payload sizes. set static::collect_length 2048 # Default timeout, for POST requests, to send $collect_length bytes (in milliseconds) set static::timeout 2000 # HTML response to send for blocked requests set static::block_html {Your POST request is not being received quickly enough. Please retry.} # Log debug messages to /var/log/ltm? 1=yes, 0=no. set static::post_debug 1 } when HTTP_REQUEST { # Only check POST requests. If the application supports other request methods with payloads, add them in a switch statement here. if { [HTTP::method] equals "POST"} { if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: POST to [HTTP::host][HTTP::uri]" } # Create a local variable copy of the collection amount set collect_length $static::collect_length # Create a local variable copy of the static timeout set timeout $static::timeout # Check for a non-existent Content-Length header if {[HTTP::header Content-Length] eq ""}{ # Use default collect length for POSTs without a Content-Length header set collect_length $static::collect_length if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: No Content-Length value" } } elseif {[HTTP::header Content-Length] <= 0}{ # Don't try to collect a payload if there isn't one unset collect_length if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: Content-Length: 0." } } elseif {[HTTP::header Content-Length] > $static::collect_length}{ # Use the default collect length set collect_length $static::collect_length if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: Content-Length: [HTTP::header Content-Length], collecting $collect_length" } } else { # Collect the actual payload length set collect_length [HTTP::header Content-Length] # Calculate a custom timeout based on the same ratio we use for the default collect length and default timeout set timeout [expr {[HTTP::header Content-Length] * $static::timeout / $static::collect_length }] if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: Content-Length: [HTTP::header Content-Length], collecting $collect_length bytes with timeout $timeout ms" } } # If the POST Content-Length isn't 0, collect (a portion of) the payload if {[info exists collect_length]}{ # If the entire request hasn't been received within X seconds, send a 408, and close the connection set id [after $timeout { if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: $timeout ms reached. Closing connection" } HTTP::respond 408 content $static::block_html Connection Close TCP::close }] # Trigger collection of the request payload HTTP::collect $collect_length if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: Collecting $collect_length" } } } } when HTTP_REQUEST_DATA { if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: Collected [HTTP::payload length] bytes." } # Check if the 'after' ID exists if {[info exists id]} { # If all the POST data has been received, cancel the connection closure if { $static::post_debug } { log local0. "[IP::client_addr]:[TCP::client_port]: Canceling \$id: $id" } after cancel $id } } |
文章评论