Skip to content

Instantly share code, notes, and snippets.

@julik
Forked from guerilla-di/recaptcha.rb
Created April 14, 2010 22:04
Show Gist options
  • Save julik/366398 to your computer and use it in GitHub Desktop.
Save julik/366398 to your computer and use it in GitHub Desktop.
require "net/http"
# Veeery simple recaptcha client.
# In your controller or filter
# @recaptcha = Recaptcha.new(:pubkey => your_public_key, :privkey => your_private_key)
# In the view
# <%= @recaptcha.form %>
# In the accepting action, rescue from Recaptcha::Failed
# begin
# @recaptcha.verify!(:env => env, :params => params)
# rescue Recaptcha::Failed
# # this is probably a bot
# end
#
class Recaptcha
TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
RECAPTCHA_VERIFY_SERVER = 'api-verify.recaptcha.net'
FORM_TEMPLATE = %{
<script type="text/javascript" src="http://api.recaptcha.net/challenge?k=%s">
</script>
<noscript>
<iframe src="http://api.recaptcha.net/noscript?k=%s"
height="300" width="500" frameborder="0"></iframe>
</noscript>
}
class Failed < RuntimeError; end
def initialize(options)
ensure_has_keys!(options, :pubkey, :privkey)
@pubkey = options[:pubkey]
@privkey = options[:privkey]
end
# Outputs a Recaptcha form
def form
FORM_TEMPLATE % [@pubkey, @pubkey]
end
def in_production?
[ENV['RACK_ENV'], ENV['RAILS_ENV']].include?("production")
end
def verify!(options = {})
ensure_has_keys!(options, :env, :params)
recaptcha = Net::HTTP.post_form( URI.parse("http://#{RECAPTCHA_VERIFY_SERVER}/verify"), {
"privatekey" => @privkey,
"remoteip" => extract_remote_from(options[:env]),
"challenge" => options[:params][:recaptcha_challenge_field],
"response" => options[:params][:recaptcha_response_field]
})
answer, error = recaptcha.body.split.map { |s| s.chomp }
raise Failed unless answer == 'true'
return true
end
private
def ensure_has_keys!(options, *c)
c.each { |o| raise "Need to know #{o}" unless options.has_key?(o) }
end
# Stolen from Rails
def extract_remote_from(env)
remote_addr_list = env['REMOTE_ADDR'] && env['REMOTE_ADDR'].split(',').map{|e| e.strip }
unless remote_addr_list.empty?
not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES}
return not_trusted_addrs.first unless not_trusted_addrs.empty?
end
remote_ips = env['HTTP_X_FORWARDED_FOR'] && env['HTTP_X_FORWARDED_FOR'].split(',')
if env.include? 'HTTP_CLIENT_IP'
if remote_ips && !remote_ips.include?(env['HTTP_CLIENT_IP'])
# We don't know which came from the proxy, and which from the user
raise RuntimeError.new("IP spoofing attack?!\nHTTP_CLIENT_IP=\#{env['HTTP_CLIENT_IP'].inspect}\nHTTP_X_FORWARDED_FOR=\#{env['HTTP_X_FORWARDED_FOR'].inspect}\n")
end
return env['HTTP_CLIENT_IP']
end
if remote_ips
while remote_ips.size > 1 && TRUSTED_PROXIES =~ remote_ips.last.strip
remote_ips.pop
end
return remote_ips.last.strip
end
env['REMOTE_ADDR']
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment