Wednesday, November 25, 2015

Implementing no CAPTCHA reCAPTCHA in PeopleSoft

This post is based on a great discussion concerning security on the OTN forums.

The requirement was to solve the problem of bots/hacker websites hijacking users to their malicious locations (masquerading the legitimate PeopleSoft site), scraping the credentials from the users and then posting the data into the PeopleSoft signin page in a typical phishing attack scenario.

If you read through the discussion, you will notice some very valid questions, concerns and suggestions provided by Greg Kelly from Oracle and others around how to ensure that such phishing emails and attacks can be prevented.

Outside of that discussion, traditionally CAPTCHA has been used in several websites to prevent bot attacks. Now Google's reCAPTCHA project is being used more commonly since its inception in 2009. The latest version (no CAPTCHA reCAPTCHA) has significant user experience improvements. Additionally, the reCAPTCHA API version 2.0 is now a RESTful API which makes server side validation a lot easier. Another great incentive to use Google's no CAPTCHA reCAPTCHA is the fact that it is FREE!

Before going down the path of implementing reCAPTCHA, please consider accessibility and user experience implications and test accordingly. Click here for an article that talks about accessibility features of reCAPTCHA.

Here are the steps to implement Google reCAPTCHA API version 2.0 in PeopleSoft.

Note: I am currently using a HCM 9.2 PUM Image 12 - PeopleTools 8.54.08.
Reference Document: Developer's Guide

Step 1: Sign up for an API key pair

Please click here to sign up on the Goolge reCAPTCHA website. Once you sign up for an API key pair to your site, you will be provided with two keys - secret key and site key.

Step 2: Client side integration

In this case, let us assume that we want to add reCAPTCHA to the PeopleSoft signin page. So the signin page will be our client side content where we would add the reCAPTCHA code.

Here are the instructions from the Google reCAPTCHA documentation:


Let us add the above code snippet to the PeopleSoft signin page (signin.html).

Note: We can locate the signin.html file in the following directory on the web server.
<PIA_HOME>/webserv/<DOMAIN>/applications/peoplesoft/PORTAL.war/WEB-INF/psftdocs/<site_name>/

Add the following script inclusion code just before </HEAD> as shown:
<!-- CSK reCAPTCHA - Start -->
<script src='https://www.google.com/recaptcha/api.js'></script>
<!-- CSK reCAPTCHA - End -->


 Add the following div (reCAPTCHA) in an appropriate location on the content page. I chose to add it right before the submit button which could be referenced by <input name="Submit" type="submit" title="<%=137%>" class="ps-button" value="<%=137%>">.

<!-- CSK reCAPTCHA - Start -->
    <div class="g-recaptcha" data-sitekey="<ENTER_YOUR_SITE_KEY_HERE>" align="center"></div>
    <br/>
<!-- CSK reCAPTCHA - End -->


Note: If you are on PeopleTools 8.54 then you might also want to perform the same customizations to signin_fmode.html file as well. This is due to a known issue that will be fixed in PeopleTools 8.55. Refer: E-FLUID - Fluid HomePage Sign Off Link Calls fmode=1 in URL, Skips Custom signout.html Page (Doc ID 2001761.1).

Please bounce and clear the cache on your web server domains. Let us now see the reCAPTCHA plugin in action on the client side.






We are not done yet! Once verified, the reCAPTCHA integration would additionally add a string with the name "g-recaptcha-response" to the payload when the user submits (form post). This string would be a key that is generated specifically for our site. The string needs to be validated on the server side which we will explore in the next section.

Step 3: Server side integration/validation

This is the step where we verify the string provided by reCAPTCHA for validity by making a REST API call. Instructions from Google reCAPTCHA documentation.


For more details: Refer API documentation.

In this case, since we are trying to validate the reCAPTCHA response string provided by the PeopleSoft signin page, the best place to add the validation logic would be in the SignOn PeopleCode.

Let us now add a new row/entry in the sequence of Signon PeopleCode events as shown in the following screenshot. I am referencing a custom function name which I will explain shortly.


Record: CSK_RECAPTCHA
Field Name: FUNCLIB
Event Name: FieldFormula
Function Name: CSK_RECAPTCHA_CHECK

Note: After making any changes to Signon PeopleCode we must bounce the app servers for them to take effect.

Now let us see how to write some custom code to validate the reCAPTCHA response string. For reference, I pasted the entire peoplecode at the very end of this post.

Click on image to zoom in!


Note: In this case, I am using Apache Commons HttpClient Java Libraries to make the REST API call. Please refer to my previous blog posts that have more details on this topic (Part I and Part II).

Details on custom function validate_reCAPTCHA_JSON is provided below. For the purposes of JSON parsing, I am using a server side scripting technique described by Jim Marion in his blog post

Click on image to zoom in!


Message Catalog Entry that contains the JSON Parsing Script:


Script for reference:

var result = (function() {

     var json = JSON.parse(json_string);

     if (json.success) {
          return "true";
     } else {
          return "false";
     }

}());


This completes our server side integration/validation. Now we are relatively free of bot attacks! :)

Note: The same feature can be applied to any content page within the PeopleSoft application as well. If not easier, the effort should be the same as what we just went through in this post.

PeopleCode Functions for reference:

Function validate_reCAPTCHA_JSON(&json As string) Returns boolean;
  
   Local string &success;
  
   /* Instantiate ScriptEngine Manager and Engine objects */
   Local JavaObject &manager = CreateJavaObject("javax.script.ScriptEngineManager");
   Local JavaObject &engine = &manager.getEngineByName("JavaScript");
  
   /* Pass JSON string */
   &engine.put("json_string", &json);
   /* Get JSON Parse JavaScript from Message Catalog */
   Local string &jsFunction = MsgGetExplainText(26000, 1, "NONE");
  
   If &jsFunction <> "NONE" Then
      &engine.eval(&jsFunction);
      &success = &engine.get("result").toString();
   End-If;
  
   If Upper(&success) = "TRUE" Then
      Return True;
   Else
      Return False;
   End-If;
  
End-Function;

Function CSK_RECAPTCHA_CHECK()
  
   /* Get reCaptcha parameter from the Request */
   Local string &reCAPTCHA = %Request.GetParameter("g-recaptcha-response");
  
   If &reCAPTCHA = "" Then
      /* Fail Authentication */
      SetAuthenticationResult( False, %SignonUserId, "", False);
   Else
      /* Do further validation if required. Refer: https://developers.google.com/recaptcha/docs/verify */
      /* Post user's reCAPTCHA response and server's private key to API: https://www.google.com/recaptcha/api/siteverify */
      try
        
         /* Using Apache HttpClient for REST - Post Method */
         Local JavaObject &jHttp, &jMethod, &filePart, &partArray, &mPartReqEntity;
        
         /* Initialize HttpClient and set parameters */
         &jHttp = CreateJavaObject("org.apache.commons.httpclient.HttpClient");
         &jHttp.getHttpConnectionManager().getParams().setConnectionTimeout(20000);
        
         /* Initialize PostMethod */
         /* !!!!!! Avoid hardcoding !!!!!!!! Replace below URL with a URL definition that is configurable. */
         Local string &sURL = "https://www.google.com/recaptcha/api/siteverify";
         &jMethod = CreateJavaObject("org.apache.commons.httpclient.methods.PostMethod", &sURL);
         &jMethod.setFollowRedirects( False);
        
         /* Add Multi-Part Message - Start */
         /* Create String Part - secret */
         &secretPart = CreateJavaObject("org.apache.commons.httpclient.methods.multipart.StringPart", "secret", "<ENTER_YOUR_SECRET_KEY_HERE>");
         /* Create String Part - response */
         &responsePart = CreateJavaObject("org.apache.commons.httpclient.methods.multipart.StringPart", "response", &reCAPTCHA);
         /* Add Parts to Part Array */
         &partArray = CreateJavaObject("org.apache.commons.httpclient.methods.multipart.Part[]", &secretPart, &responsePart);
         /* Create Multi-Part Request Entity */
         &mPartReqEntity = CreateJavaObject("org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity", &partArray, &jMethod.getParams());
         /* Add Multi-Part Request Entity to the Post Method */
         &jMethod.setRequestEntity(&mPartReqEntity);
         /* Add Multi-Part Message - End */
        
         /* Invoke PostMethod */
         Local integer &return = &jHttp.executeMethod(&jMethod);
         Local string &responseBody = &jMethod.getResponseBodyAsString();
        
         If (validate_reCAPTCHA_JSON(&responseBody)) Then
            /* reCaptcha success: allow user */
            SetAuthenticationResult( True, %SignonUserId, "", False);
         Else
            /* reCaptcha failure: deny */
            SetAuthenticationResult( False, %SignonUserId, "", False);
         End-If;
        
      catch Exception &ex
         /* Unknown Exception */
         SetAuthenticationResult( False, %SignonUserId, "", False);
      end-try;
   End-If;
End-Function;

 

10 comments:

  1. Hi Sasank,

    What is the minimum version of tools/application that we need to be to implement reCaptch and is there any restrictions as such. please let us know. thank you.

    ReplyDelete
    Replies
    1. Hi Raj - I don't think there is any tools/application dependency.

      For making the REST call in the above proof of concept, I used Apache HttpClient which is Tools independent. Although, you might have to load the jar files on the App Server.

      For the JSON parsing, I used Jim Marion's server side scripting technique which you can find here:
      http://jjmpsj.blogspot.com/2015/09/javascript-on-app-server-scripting.html

      Hope this helps!

      Delete
  2. Hi Sasank

    The above example show how Captcha/ReCaptcha can be implemented on the sign-in page. Can we do the same thing on a PeopleSoft Fluid page. We have a requirement where people need to register for an account and we want to put in Captcha on this page.

    Please do let me know.

    Regards
    Arshad

    ReplyDelete
    Replies
    1. Hi Arshad - I don't have a working example of this use case but I am certain that the same can be applied to any PIA page (Classic/Fluid) as well.

      The only differences would be:
      - The signin.html customizations should be done on the page. This includes adding the recaptcha api js source and adding the recaptcha DIV to the page.
      - The server side signon peoplecode function can be placed in an appropriate event that fires before the Save (e.g.: SavePreChange, etc.).

      Thanks!

      Delete
  3. Could you tell me please where I can download a version of reCAPTCHA API version 2.0?
    Regards, Alfredo

    ReplyDelete
    Replies
    1. Hi Alfredo,

      There is no need to download anything. You just need to register for a google recaptcha account by going here:
      https://www.google.com/recaptcha/admin

      Once you complete registration, you will receive two keys (site key and secret key). This completes Step 1 in this blog.

      Then you can proceed with the rest of the steps (starting from step 2) detailed in this post.

      Delete
  4. Hi Sasank,

    Thanks for your response.
    I'm working on a Campus Solution 9.0 with a PeopleTools 8.53.
    I put the snippet code in a HTML area on a pia page, and using function CSK_RECAPTCHA_CHECK and I got the next error message:

    Java Exception: java.lang.NoClassDefFoundError: org/apache/commons/httpclient/HttpClient: finding class org.apache.commons.httpclient.HttpClient (2,725) V_SCC_NUR_WRK.V_HELP1.FieldFormula Name:CSK_RECAPTCHA_CHECK PCPC:1279 Statement:24
    Called from:V_SCC_NUR_REG.GBL.SAD_OLA_WRK.ADM_NEXT_BTN.FieldChange Statement:4

    Regards, Alfredo

    ReplyDelete
    Replies
    1. Hi Alfredo - I am using Apache Commons HttpClient to do a REST call from PeopleCode. This is the step where we send the g-recaptcha-response parameter to Google for verification (https://developers.google.com/recaptcha/docs/verify).

      You don't necessarily have to use Apache HttpClient if you choose to POST the data to Google some other way (using IB, etc.). If you want to use Apache Commons HttpClient then you can refer to these blog (which I have also mentioned in this post).
      https://pe0ples0ft.blogspot.com/2014/10/using-apache-commons-httpclient-for.html
      https://pe0ples0ft.blogspot.com/2014/10/apache-commons-httpclient-for-rest-in.html

      Delete
  5. Hi Sasank, Kindly advice on Implementing no-captcha re-captcha using integration Broker.
    Is there a way to do this without having to place code in the webserver?

    ReplyDelete
    Replies
    1. No. ReCaptcha does not work with web services (Integration Broker).

      ReCaptcha is specifically designed to determine that the end-user is not a bot. It is also designed to work only with website that render HTML. ReCaptha involves the end-user interacting with the page to prove that he/she is not a bot.

      In short, it will not work with Integration Broker. You may want to review the following PeopleBook if you want to learn more about Securing Integration Broker.
      https://docs.oracle.com/cd/E58500_01/pt854pbh1/eng/pt/tiba/concept_UnderstandingSecuringIntegrationEnvironments-fe7ea9.html#topofpage

      Delete