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

Was this article helpful?

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".