
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
« ... Read full posting ... »