For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Google reCAPTCHA Challenge iRule

Problem this snippet solves:

This iRule adds captcha verification to a virtual server.

The implementation is described in George Watkins' article: Google reCAPTCHA Verification With Sideband Connections

This iRule can be added to a virtual server. Each initial request will receive a captcha challenge prior to being allowed access to protected resources.

  • dns_server - DNS server address for which the BIG-IP has a direct route
  • recaptcha_public_key - reCAPTCHA public key obtained from Google; https://www.google.com/recaptcha/admin/list
  • recaptcha_private_key - reCAPTCHA private key obtained from Google; https://www.google.com/recaptcha/admin/list
  • recaptcha_approval_table - tracks captcha challenge approvals by user IP and source port; default = "recaptcha_approvals"
  • recaptcha_redirect_table - maintains redirect state so user sees their previously requested resource after the captcha challenge; default = "recaptcha_redirects"
  • recaptcha_approval_timeout - timeout of captcha approval; default = 3600s
  • recaptcha_approval_lifetime - lifetime of captcha approval; default = 3600s
  • logging - log level, 0 = silent, 1 = log client interaction, 2 = log all interaction with client and Google

Code :

when RULE_INIT {
  # set DNS server address to resolve www.google.com
  set static::dns_server 10.0.0.254
  
  # set public and private reCAPTCHA keys (obtain from www.recaptcha.com)
  set static::recaptcha_public_key "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  set static::recaptcha_private_key "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  
  # set name of table to track reCAPTCHA approvals
  set static::recaptcha_approval_table "recaptcha_approvals"
  
  # set name of table to track last requested URL
  set static::recaptcha_redirect_table "recaptcha_redirects"
  
  # timeout and/or lifetime for reCAPTCHA approval
  set static::recaptcha_approval_timeout 3600
  set static::recaptcha_approval_lifetime 3600

  # log level, 0 = silent, 1 = log client interaction, 2 = log all interaction with client and Google
  set static::logging 1

  # begin - HTML for reCAPTCHA form page 
  set static::recaptcha_challenge_form {
  
    Hold up there!  
  
  
    

Hold up there!

We want to make sure that you're a human before you can use this virtual server!

} # end - HTML for reCAPTCHA form page } when CLIENT_ACCEPTED { set session_identifier "[IP::client_addr]:[TCP::client_port]/[IP::local_addr]:[TCP::local_port]" } when HTTP_REQUEST { if { [HTTP::path] equals "/verify_recaptcha" } { set recaptcha_challenge_field [URI::query [HTTP::uri] "recaptcha_challenge_field"] set recaptcha_response_field [URI::query [HTTP::uri] "recaptcha_response_field"] if { $static::logging >= 1 } { log local0. "Session \"$session_identifier\": user responded with \"$recaptcha_response_field\"" } # assemble body of reCAPTCHA verification POST set recaptcha_post_data "privatekey=$static::recaptcha_private_key&" append recaptcha_post_data "remoteip=[IP::remote_addr]&" append recaptcha_post_data "challenge=$recaptcha_challenge_field&" append recaptcha_post_data "response=$recaptcha_response_field" # calculate Content-length header value set recaptcha_post_content_length [string length $recaptcha_post_data] # assemble reCAPTCHA verification POST request set recaptcha_verify_request "POST /recaptcha/api/verify HTTP/1.1\r\n" append recaptcha_verify_request "Host: www.google.com\r\n" append recaptcha_verify_request "Accept: */*\r\n" append recaptcha_verify_request "Content-length: $recaptcha_post_content_length\r\n" append recaptcha_verify_request "Content-type: application/x-www-form-urlencoded\r\n\r\n" append recaptcha_verify_request "$recaptcha_post_data" # resolve Google's IP address and stuff it into a variable set google_ip [lindex [RESOLV::lookup @$static::dns_server -a "www.google.com"] 0] # establish connection to Google set conn [connect -timeout 1000 -idle 30 $google_ip:80] # send reCATPCHA verification request to Google send -timeout 1000 -status send_status $conn $recaptcha_verify_request if { $static::logging >= 2 } { log local0. "Session \"$session_identifier\": sending reCAPTCHA verification to Google: \"$recaptcha_verify_request\"" } # receive reCAPTCHA verification response from Google set recaptcha_verify_response [recv -timeout 1000 -status recv_info $conn] if { $static::logging >= 2 } { log local0. "Session \"$session_identifier\": received verification response from Google: \"$recaptcha_verify_reponse\"" } close $conn # process reCAPTCHA verification response and remove user session from trigger table if successful if { $recaptcha_verify_response contains "success" } { set redirect_url [table lookup -subtable $static::recaptcha_redirect_table -notouch $session_identifier] table add -subtable $static::recaptcha_approval_table $session_identifier 1 \ $static::recaptcha_approval_timeout $static::recaptcha_approval_lifetime if { $static::logging >= 1 } { log local0. "Session \"$session_identifier\": passed captcha and was added to approval table" log local0. "Session \"$session_identifier\": redirecting to \"$redirect_url\"" } HTTP::redirect $redirect_url } else { HTTP::respond 200 content $static::recaptcha_challenge_form if { $static::logging >= 1 } { log local0. "Session \"$session_identifier\": failed captcha" } } } elseif { [table lookup -subtable $static::recaptcha_approval_table $session_identifier] eq ""} { table add -subtable $static::recaptcha_redirect_table $session_identifier "http://[HTTP::host][HTTP::uri]" 3600 3600 HTTP::respond 200 content $static::recaptcha_challenge_form } }
Published Mar 17, 2015
Version 1.0

2 Comments

  • Trying to adapt this rule to work for only a specific URI; not having much luck. Has anyone successfully done this?
  • Typo in line 101. "recaptcha_verify_reponse" should read "recaptcha_verify_response".