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.0George_Watkins_
Historic F5 Account
Joined September 17, 2008
George_Watkins_
Historic F5 Account
Joined September 17, 2008
2 Comments
- rlwoodfin
Nimbostratus
Trying to adapt this rule to work for only a specific URI; not having much luck. Has anyone successfully done this? - Dicky_S_22615
Nimbostratus
Typo in line 101. "recaptcha_verify_reponse" should read "recaptcha_verify_response".