Adding CAPTCHA to Force.com Sites
Adding CAPTCHA to Force.com Sites
Abstract
A CAPTCHA is a challenge-response test used in applications to determine whether a human or a computer is interacting with the application. You've probably seen them — colorful images with distorted text at the bottom of Web registration forms. CAPTCHAs are used by many websites to prevent abuse from "bots," or automated programs usually written to generate spam. No computer program can read distorted text as well as humans can, so bots cannot navigate sites protected by CAPTCHAs.
This tutorial describes how to add a CAPTCHA challenge to your Force.com Site. It uses the popular reCAPTCHA technology to implement the challenge. A small demonstration application is also available, and this tutorial provides the source code for adding such a feature to your own applications.
Why reCAPTCHA?
To archive human knowledge and to make information more accessible to the world, multiple projects are currently digitizing physical books that were written before the computer age. The book pages are being photographically scanned, and then transformed into text using Optical Character Recognition (OCR). The transformation into text is useful because scanning a book produces images, which are difficult to store on small devices, expensive to download, and difficult to search. The problem is that OCR is not perfect.
reCAPTCHA improves the process of digitizing books by sending words that cannot be read by computers to the Web in the form of CAPTCHAs for humans to decipher. More specifically, each word that cannot be read correctly by OCR is placed on an image and used as a CAPTCHA.
reCAPTCHA in Action
Imagine create a web site where you needed to ensure a human was responding. For example, you want to ensure that a human was filling in a form. You could do this by adding a CAPTCHA to the page, to challenge the user as shown in the following figure:
I've put together a small demonstration application that illustrates this in action - it tests your humanity. It's not a complete application, but it should give you the gist.
Using the methods described in this tutorial you will easily be able to code such CAPTCHAs into your own sites.
Understanding CAPTCHA
The following diagram can be found on the reCAPTCHA site, which I've included it here for your reference.
As described in the RECAPTCHA API Documentation:
- The user loads the web page with the reCAPTCHA challenge JavaScript embedded.
- The user's browser requests a challenge from reCAPTCHA. reCAPTCHA gives the user a challenge and a token that identifies the challenge.
- The user fills out the web page form, and submits the result to your application server, along with the challenge token.
- reCAPTCHA checks the user's answer, and gives you back a response.
- If true, generally you will allow the user access to some service or information. E.g. allow them to comment on a forum, register for a wiki, or get access to an email address. If false, you can allow the user to try again.
Enough of the theory, let's get to the practice!
Getting Started
Your first step is to sign up for a CAPTCHA key - you will need that in the code below. When I signed up for the key, I used the global key option.
Now you need to modify a few security settings in your environment. In particular, you want to be enable callouts from Apex Code to a remote site - api-veryify.recaptcha.net. Our code does an HTTP.send() to this site. To do this:
- Under Setup | Security Controls find Remote Site Settings , and add the following URL as allowed,
http://api-verify.recaptcha.net. You will need to disable protocol security.
The figure above shows how we set it up for our developer environment.
An Apex Code Helper
I've created a simple helper class that we'll use when implementing the reCAPTCHA. It's listed below. This code is free to use, under the new BSD open source license. Add your public and private key to this class by replacing the placeholders (see the first few lines) with the keys you got when you signed up for reCAPTCHA account .
public class reCAPTCHA {
private static string secret = 'your secret key';
public string publicKey { get { return 'your public key' ; }}
private static string baseUrl = 'http://api-verify.recaptcha.net/verify';
/*
reCAPTCHA API Documentation
reCAPTCHA is a freely available CAPTCHA implementation. It distinguishes humans from computers. This may be useful for:
* A registration page for a forum or wiki.
* A comment form.
* Hiding information that could be useful to spammers (we recommend the reCAPTCHA Mailhide API for this).
How the reCAPTCHA API Works
API diagram
1. The user loads the web page with the reCAPTCHA challenge JavaScript embedded.
2. The users browser requests a challenge from reCAPTCHA. reCAPTCHA gives the user a challenge and a token that identifies the challenge.
3. The user fills out the web page form, and submits the result to your application server, along with the challenge token.
4. reCAPTCHA checks the users answer, and gives you back a response.
5. If true, generally you will allow the user access to some service or information. E.g. allow them to comment on a forum, register for a wiki, or get access to an email address. If false, you can allow the user to try again.
This document describes the validation steps in the process. Our Client API is used to embed the reCAPTCHA on your website.
Signing up for a reCAPTCHA Key
In order to use reCAPTCHA, you need a public/private API key pair. This key pair helps to prevent an attack where somebody hosts a reCAPTCHA on their website, collects answers from their visitors and submits the answers to your site. You can sign up for a key on the reCAPTCHA Administration Portal.
Key Scope
Your reCAPTCHA token is valid only at the domain you sign up for and any subdomains (due to the potential attack mentioned above). Some users require keys for multiple sites, for example, a development server and a production server or simply multiple sites hosted on the same server. Three techniques can be used to work around this:
* If one of your servers is "localhost" or "127.0.0.1", reCAPTCHA will not enforce the same-domain rule. Just use the same key as for the production server.
* Generate a key for a broader scope. For example, if your application is a.example.com and your test server is test.corp.example.com, generate a key for example.com.
* Generate a different key for each domain.
Getting a reCAPTCHA Challenge
Your application will need to display a reCAPTCHA challenge on your web page. This can be done with the Client API, a piece of JavaScript code which lets you embed the CAPTCHA on your website.
Verifying the reCAPTCHA Solution
URL http://api-verify.recaptcha.net/verify
Parameters (sent via POST)
privatekey (required) Your private key
remoteip (required) The IP address of the user who solved the CAPTCHA.
challenge (required) The value of "recaptcha_challenge_field" sent via the form
response (required) The value of "recaptcha_response_field" sent via the form
Response The response from verify is a series of strings separated by \n. To read the string, split the line and read each field. New lines may be added in the future. Implementations should ignore these lines
Line 1 "true" or "false". True if the reCAPTCHA was successful
Line 2 if Line 1 is false, then this string will be an error code. reCAPTCHA can display the error to the user (through the error parameter of api.recaptcha.net/challenge). Implementations should not depend on error code names, as they may change in the future.
Example: If your response looks like this:
false
incorrect-captcha-sol
... you can add '&error=incorrect-captcha-sol' to the challenge request URL, and the user will get an error code.
*/
public string challenge {get; set;} { challenge = null; }
public string response {get; set; } { response = null; }
public Boolean correctResponse { get; private set; } { correctResponse = false; }
public PageReference verify() {
system.debug( 'verify called ');
// first time thru, this parameter is null, so no request to make yet
if ( challenge == null || response == null ) {
system.debug( 'verify called null ');
return null;
}
HttpResponse r = makeRequest( baseUrl ,
'privatekey='+ secret +
'&remoteip=' + remoteHost +
'&challenge=' + challenge +
'&response=' + response +
'&error=incorrect-captcha-sol'
);
if ( r != null ) { // is null when test methods run
correctResponse = ( r.getBody().contains('true') );
}
return null;
}
public PageReference reset() {
challenge = null;
response = null;
return null;
}
public static HttpResponse makeRequest(string url, string body) {
HttpRequest req = new HttpRequest();
HttpResponse response = null;
req.setEndpoint( url );
req.setMethod('POST');
req.setBody ( body);
try {
Http http = new Http();
response = http.send(req);
System.debug('response: '+ response);
System.debug('body: '+ response.getBody());
} catch( System.Exception e) {
System.debug('ERROR: '+ e);
}
return response;
}
public string remoteHost { get { string ret = '127.0.0.1';
// also could use x-original-remote-host
map<string , string> hdrs = ApexPages.currentPage().getHeaders();
if ( hdrs.get('x-original-remote-addr') != null)
ret = hdrs.get('x-original-remote-addr');
else if ( hdrs.get('X-Salesforce-SIP') != null)
ret = hdrs.get('X-Salesforce-SIP');
return ret;
} }
public static testmethod void test_1() {
reCaptcha re = new reCaptcha();
string href = baseUrl ;
re.challenge = re.response = 'foo';
string publick = re.publicKey;
string host = re.remoteHost;
re.verify();
}
public static testmethod void test_2() {
reCaptcha re = new reCaptcha();
re.verify();
}
}
The key to this code is the verify() method, which makes the HTTP POST to the reCAPTCHA site. Note that it stores the result of the challenge in the correctResponse property.
Example Visualforce Page
Here is the Visualforce Page used in the sample application. It will only test your humanity - it does not integrate with any other forms or input functionality. You will have to integrate this source code into your own application as you see best. Normally you would pass your input elements in this same submit, and then inside the Apex Code (in the method verify) you would perform the captcha and then take actions depending on the value of the boolean correctResponse.
You may also note the composition and define components, these are only required to get the same look as the rest of the pages at your site and are not part of the captcha challenge code.
<apex:page showheader="false" standardstylesheets="true" controller="reCAPTCHA" >
<apex:composition template="{!$Site.Template}"><apex:define name="content">
<script src="http://api.recaptcha.net/js/recaptcha_ajax.js" type="text/javascript" />
<script>
function showRecaptcha(element) {
Recaptcha.create("{!publicKey}", element, {
theme: 'red',
tabindex: 0,
callback: Recaptcha.focus_response_field
});
}
</script>
<!-- display the challenge form in this output panel -->
<apex:outputPanel id="captcha" rendered="{!ISNULL(challenge)}">
<apex:form >
<apex:inputhidden value="{!challenge}" id="challenge" />
<apex:inputhidden value="{!response}" id="response" />
<script type="text/javascript">
function captureResponse(ele) {
document.getElementById('{!$Component.challenge}').value =
document.getElementById('recaptcha_challenge_field').value;
document.getElementById('{!$Component.response}').value =
document.getElementById('recaptcha_response_field').value;
}
</script>
<div id="dynamic_recaptcha_1"></div>
<br />
<apex:commandButton value="Submit"
onclick="javascript:captureResponse(this);"
action="{!verify}" />
</apex:form>
<!-- display the image using the reCAPTCHA AJAX API -->
<script type="text/javascript">showRecaptcha('dynamic_recaptcha_1');</script>
</apex:outputPanel>
<!-- display the response from the verify API in this panel -->
<apex:outputPanel rendered="{!NOT(ISNULL(challenge))}">
<h3>Correct Human Response : {!correctResponse}</h3><br />
<!-- debugging
<br />Challenge : {!challenge}<br />Response : {!response}
-->
<apex:form > <apex:commandButton value="Try Again ?"
action="{!reset}" />
</apex:form>
</apex:outputPanel>
</apex:define>
</apex:composition>
</apex:page>
Summary
In summary, this is an easy and fun project to add a human challenge-response system to your Force.com Sites input forms. It should dramatically reduce the amount of form spam you get.
On the do-it-yourself difficulty scale (1-easy, 5-complex), adding this component to your application is about a 3 out of 5, only because you need to add your form fields in the correct place and then avoid creating records in your database if the correctResponse expression is false in your controller.
Good luck!
References
- See An Introduction to Force.com Sites to learn more about Force.com Sites.
- The Force.com Sites home page in the Technical Library contains a comprehensive set of pointers on all things Force.com Sites related.
- Visit the reCAPTCHA home to learn more about reCAPTCHA.
About the Author
Ron Hess is a Developer Evangelist at Salesforce.com and has written numerous articles and toolkits for the Force.com platform. June 15 2009

