Adding a CAPTCHA to your Oracle APEX application
Haven't had the requirement to add one in my applications until now, but after reading Dan's posting about integrating reCAPTCHA with Oracle SSO, I thought that shouldn't be to hard to integrate into an Oracle Application Express (APEX) application too! A nice challenge for the evening ;-)I know that there are some similar solutions out there on the OTN forum and on the unOfficial Wiki page, but the SVG solution isn't very secure in my opinion and the other solution uses an unsupported non-public package of Oracle APEX. So how can we integrate the reCAPTCHA widget instead?
Just follow my step by step instruction and you are done in minutes!
- Sign up at reCAPTCHA to get the private and public key.
- Load the stored function into your application schema which you can find at the end of the article.
- Go to the page where you want to integrate the CAPTCHA
- Create three page items
- Name: P<your page>_RECAPTCHA_CHALLENGE_FIELD, P<your page>_RECAPTCHA_RESPONSE_FIELD and P<your page>_RECAPTCHA_ERROR
- Type: Hidden
- Sequence: Should be before all other items
- Item Source: Always Null
- Put the following JavaScript code into the "Post Element Text" property of an existing page item or create a new region of type "HTML". Replace P7 by your own page number and also replace "your_public_key" with your public key you got during the registration.
<script type="text/javascript"
src="http://api.recaptcha.net/challenge?k=your_public_key&error=&P7_RECAPTCHA_ERROR.">
</script>
<script type="text/javascript">
function renameRecaptchaField(pRecaptchaFieldName, pApexFieldName)
{
var vRecaptchaField = $x(pRecaptchaFieldName);
var vApexField = $x(pApexFieldName);
// Set for the dynamically generated Recaptcha field the same name as for
// our APEX field (eg. t02), so that wwv_flow.accept puts the value into the APEX field.
// After that remove the APEX field, because wwv_flow.accept can't handle
// two fields with the same name.
vRecaptchaField.name = vApexField.name;
vApexField.parentNode.removeChild(vApexField);
};
renameRecaptchaField('recaptcha_challenge_field', 'P7_RECAPTCHA_CHALLENGE_FIELD');
renameRecaptchaField('recaptcha_response_field', 'P7_RECAPTCHA_RESPONSE_FIELD' );
</script> - Create a page level validation
- Type: PL/SQL - Function Returning Boolean
- Sequence: 1
- Validation Name: Recaptcha Check
- Error Display Location: Inline in Notification
- Validation:
BEGIN
:P7_RECAPTCHA_ERROR := checkRecaptcha
( pPrivateKey => 'your_private_key'
, pChallenge => :P7_RECAPTCHA_CHALLENGE_FIELD
, pResponse => :P7_RECAPTCHA_RESPONSE_FIELD
);
RETURN (:P7_RECAPTCHA_ERROR IS NULL);
END; - Error Message: Recapture verification failed!
That's all, you are done!
So what was the challenge?
The JavaScript code provided by reCAPTURE creates two new fields on the fly. But because they do not comply with the names of the wwv_flow.accept package, which is called when a page is submitted, you will get an error during submit. So I had to use a trick. The nice side effect was that I can use the regular referencing in Oracle APEX, because they are just normal page items. See the comment text of the JavaScript code for details.
And here comes the stored function:
CREATE OR REPLACE FUNCTION checkRecaptcha
( pPrivateKey IN VARCHAR2
, pRemoteIP IN VARCHAR2 := OWA_Util.get_cgi_env('REMOTE_ADDR')
, pChallenge IN VARCHAR2
, pResponse IN VARCHAR2
)
RETURN VARCHAR2
IS
--
vRequest Utl_Http.req;
vPostText VARCHAR2(500);
vResponse Utl_Http.resp;
vResponseText VARCHAR2(2000);
vError VARCHAR2(200);
BEGIN
----------------------------------------------------------------------------
-- Build text for the post action.
-- For a field description, see
-- http://recaptcha.net/apidocs/captcha/
----------------------------------------------------------------------------
vPostText :=
'privatekey='||Utl_Url.escape(pPrivateKey, TRUE)||CHR(38)||
'remoteip=' ||Utl_Url.escape(pRemoteIP, TRUE)||CHR(38)||
'challenge=' ||Utl_Url.escape(pChallenge, TRUE)||CHR(38)||
'response=' ||Utl_Url.escape(pResponse, TRUE)||CHR(38);
----------------------------------------------------------------------------
-- if you need to set a proxy, uncomment next line.
----------------------------------------------------------------------------
/* Utl_Http.set_proxy('proxy.it.my-company.com', 'my-company.com'); */
----------------------------------------------------------------------------
-- Send data to reCAPTCHA.
----------------------------------------------------------------------------
vRequest := Utl_Http.begin_request
( url => 'http://api-verify.recaptcha.net/verify'
, method => 'POST'
);
Utl_Http.set_header
( r => vRequest
, name => 'Content-Type'
, value => 'application/x-www-form-urlencoded'
);
Utl_Http.set_header
( r => vRequest
, name => 'Content-Length'
, value => LENGTH(vPostText)
);
Utl_Http.write_text
( r => vRequest
, data => vPostText
);
vResponse := Utl_Http.get_response(vRequest);
IF vResponse.status_code = '200' -- the HTTP call was successful
THEN
Utl_Http.read_text(vResponse, vResponseText);
-- Has the user entered a correct solution?
IF vResponseText LIKE 'false%'
THEN
vError := SUBSTR(vResponseText, 7);
END IF;
ELSE
vError := 'HTTP status: '||vResponse.status_code||'-'||vResponse.reason_phrase;
END IF;
--
Utl_Http.end_response(vResponse);
--
RETURN vError;
END checkRecaptcha;
Labels: Oracle APEX, security
















9 Comments:
Very cool Patrick.
Glad you enjoyed your weekend like I did mine!
By
Paul, at 07 September, 2007 13:17
Hi Patrick,
Just so no one is confused (you wouldn't be if you followed the link), I didn't actually write anything about actually implementing SSO with reCAPTCHA. My contribution was to link to Paul Gallagher's great article on how to configure everything. Thanks for the link!
By
Dan, at 07 September, 2007 14:59
Looks like that I was a little bit confused yesterday evening :-)
Patrick
By
Patrick Wolf, at 07 September, 2007 15:20
Hi Patrick
For point number 5 :
"Put the following JavaScript code into the "Post Element Text" property of an existing page item"
should I put it in all items in the page or just the new items I created (P7_RECAPTCHA_CHALLENGE_FIELD, P7_RECAPTCHA_RESPONSE_FIELD and P7_RECAPTCHA_ERROR)
regards
raied
By
raied, at 13 July, 2008 11:41
Raied,
put it into just one item, eg. P7_RECAPTCHA_ERROR or create a new region of type HTML of put it into the Footer property of your region. There are a lot of places where you can put the code, just make sure that the code is put somewhere after the 3 new fields. So that they already exist in the DOM tree when the Javascript code is accessing it.
Patrick
By
Patrick Wolf, at 13 July, 2008 18:06
Thanks a lot Patrick, it is working fine now.
By
raied, at 14 July, 2008 10:57
Hi again Patrick,
Is there a way to let the cursor be inside the CAPTCHA image (in the text field) not in the first APEX text filed item?
Thanks
By
raied, at 15 July, 2008 14:03
Try the following Javascript code after the other Javascript code.
first_field('recaptcha_response_field');
That should do it.
Patrick
By
Patrick Wolf, at 15 July, 2008 20:29
Thanks Patrick for your response.
I tried yours put with no luck.
This one works fine:
http://www.itechies.net/dev/details_faq-sbres_id-222.htm
Thanks again
By
raied, at 16 July, 2008 11:38
Post a Comment
<< Home