Supporting reCaptcha with NetScaler nFactor

Supporting reCaptcha with NetScaler nFactor

book

Article ID: CTX216091

calendar_today

Updated On:

Description

Introduction

NetScaler product supports nFactor authentication from version 11.0. nFactor provides various possibilities, including; fine grained authentication based on user groups, location, etc. This article describes captcha for NetScaler login using Google’s reCaptcha.

Scope

This article describes configuring captcha with single factor. This is demonstrated for an authentication vserver. However, it is applicable for Gateway vserver using nFactor for Gateway, described in addendum.

Requirements

NetScaler version 11.1 (preferred), 11.0 66.x+ (with additional steps described in addendum).

Details

This article is organized into two parts

  1. Configuration on Google for registering captcha.
  2. Configuration on NetScaler to use captcha as part of login flow.

Captcha Configuration

Captcha configuration on Google

Administrator would have to register a domain for captcha at https://www.google.com/recaptcha/admin#list.

Once administrator navigates to the above page, below screenshot appears that can be used to add a new domain/fqdn for captcha. This fqdn must be authentication/gateway vserver’s fqdn.
NOTE: Use reCAPTCHA V2 only. Invisible reCAPTCHA is still in Beta.
 

User-added image

Once a domain is registered, administrator is shown the “site key” and “secret key” like shown below:

 User-added image

SiteKey and SecretKey are grayed out for security reasons. SecretKey is supposed to be kept safe.

This completes the configuration on Google

Configuration on NetScaler

NetScaler configuration can be broken down into these parts

  • Configuration to show the captcha page to users.
  • Configuration to post the captcha response to google server for verification.
  • LDAP configuration in second factor for user login (optional).

Configuration to show captcha to users

Login form customization is done through “LoginSchema” entity on NetScaler. This is specified at authentication vserver, and is sent to UI for rendering the login form.

Step 1:
There is a built-in loginschema called “SingleAuthCaptcha.xml” located in /nsconfig/loginSchema/LoginSchema directory on NetScaler in latest 11.1 versions. For older 11.1 versions or 11.0 versions, this can be copied from addendum section below. When copying from the browser, care should be taken to normalize special character encodings.
It is recommended that all loginSchemas be copied from /nsconfig/loginschema/LoginSchema directory to parent directory, /nsconfig/loginschema.

Assuming administrator has copied SingleAuthCaptcha.xml to desired location, below commands add a loginSchema and bind it to authentication vserver. These commands are issued in command line interface.

Step 2:
Run the following command:
add authentication loginSchema singleauthcaptcha -authenticationSchema /nsconfig/loginschema/SingleAuthCaptcha.xml

Step 3:
Run the following command:
add authentication loginSchemaPolicy singleauthcaptcha -rule true -action singleauthcaptcha

Step 4:
Run the following command:
add authentication vserver auth SSL <IP> <Port>
add ssl certkey vserver-cert –cert <path-to-cert-file> -key <path-to-key-file>
bind ssl vserver auth –certkey vserver-cert
bind authentication vserver auth -policy singleauthcaptcha -priority 5 -gotoPriorityExpression END

There is one final step before captcha can be shown to user. This pertains to customizing the UI on NetScaler to load captcha. This is done by customizing the javascript that loads the UI.

Step 5:
The script to be modified is at /var/NetScaler/logon/LogonPoint/custom/script.js
In latest versions of NetScaler, a new script /var/netscaler/logon/LogonPoint/plugins/ns-gateway/ns-nfactor.js already has the required code for running re-CAPTCHA.
Copy the script from addendum section into script.js, if ns-nfactor.js is not present.

Once this script is copied, edit the script in the editor of choice (or edit it prior to copying it onto NetScaler) as shown below.

Step 5.1
Navigate to the section that begins with
CTXS.ExtensionAPI.addCustomCredentialHandler({
                                getCredentialTypeName: function () { return "nf-recaptcha"; },
 
You can do text search for “nf-recaptcha” to locate this.
Locate below line and replace the sitekey with what you obtained from google
var reCaptchaSiteKey = "XXXXX" //Replace this with the SiteKey from Google ReCaptcha

This completes the configuration needed to show the captcha. Let’s move on to the verification of captcha response.

Configuration to post the captcha response to Google server

Now that we added necessary configuration to display captcha, let’s add configuration to verify captcha response from browser. For this we use “webAuth” authentication policy.

Sample webauth action is defined in comments section in ns-nfactor.js script itself to make it simple for administrators. Text search for “webAuthAction” in ns-nfactor.js will find desired configuration. This is described below in step 6.

Step 6:
add authentication webAuthAction recaptcha_aaatm -serverIP <google.com ip address> -serverPort 443 -fullReqExpr q{"POST /recaptcha/api/siteverify HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 10000"+"\r\n\r\n" + "secret=SiteSecretObtainedAsPartOfRegistration&response=" + http.req.body(10000).after_str("&recaptcha=")} -scheme https -successRule "http.RES.body(1000).REGEX_MATCH(re/\"success\": true.*\"hostname\": \"FQDNRegisteredAtGoogle\"/)"

In the above action, there are some pieces that are to be filled in. These are highlighted in yellow.

  • Value of this field is IP address that is returned when resolving www.google.com.
  • is the site secret that is obtained when registering authentication vserver domain at google
  • is the FQDN of authentication/gateway vserver that is given when registering at captcha.

Step 7:
add authentication policy recaptcha_aaatm –rule true –action recaptcha_aaatm

Step 8:
bind authentication vserver auth –policy recaptcha_aaatm –priority 1

Step 9:
If Active Directory authentication is desired, this step is needed. Otherwise this step can be ignored.
add authentication ldapAction ldap-new -serverIP X.X.X.X -serverPort 636 -ldapBase "cn=users,dc=aaatm,dc=com" -ldapBindDn adminuser@aaatm.com -ldapBindDnPassword <password> -encrypted -encryptmethod ENCMTHD_3 -ldapLoginName sAMAccountName -groupAttrName memberof -subAttributeName CN -secType SSL -passwdChange ENABLED -defaultAuthenticationGroup ldapGroup

add authenticationpolicy ldap-new –rule true –action ldap-new

Since LDAP authentication happens after captcha, we add it to second factor:
add authentication policylabel second-factor
bind authentication policylabel second-factor –policy ldap-new –priority 10

bind authentication vserver auth –policy recaptcha_aaatm –priority 1 –nextFactor second-factor

Step 10:
Depending on whether LB vserver is used for access or gateway, we need to add appropriate vservers.
Let’s assume we need LB vserver. If so, administrator needs to add one.
Add lb vserver lbtest HTTP <IP> <Port> -authentication ON –authenticationHost nssp.aaatm.com

In the above command, “nssp.aaatm.com” resolves to authentication vserver.

User experience

This completes the run time flow as well. Let’s see the screenshots for the user flow.

User navigates to LB vserver and is redirected to authentication vserver. Once authentication vserver loads the login page, it appears as shown below. X1 portal theme is used on authentication vserver. For other portal themes, UI looks as configured.

User-added image

As can be noticed, “Log On” button is disabled until captcha is completed. Once user clicks “I’m not a robot”, below captcha widget appears.

User-added image

User would be navigated through series of captcha images before completion page is shown below

User-added image

At this point, user enters his AD credentials and submits them to NetScaler. If authentication succeeds, user is redirected to desired resource.

Logs

Below logs can be found during captcha:

Aug 18 02:26:13 <local0.debug> 10.217.208.250 08/18/2016:00:26:13 GMT NetsScaler 0-PPE-0 : default AAATM Message 1427 0 :  "Authentication delegated to Packet engine for user1, chosen action is recaptcha_aaatm, aaad resp 9 "
Aug 18 02:26:13 <local0.debug> 10.217.208.250 08/18/2016:00:26:13 GMT NetsScaler 0-PPE-0 : default SSLVPN Message 1428 0 :  "WEBAUTH: Primary webauth request being tried, action: recaptcha_aaatm "
Aug 18 02:26:13 <local0.debug> 10.217.208.250 08/18/2016:00:26:13 GMT NetsScaler 0-PPE-0 : default SSLVPN Message 1429 0 :  "WEBAUTH: Successfully sent webauth request, POST /recaptcha/api/siteverify HTTP/1.1^M Host: www.google.com^M Content-Type: application/x-www-form-urlencoded^M Content-Length: 2319 ^M ^M secret=deleted&response=03AHJ_VuupLQ113EEBg57YJaVbqHI6dsfqc7Sz0posASFrpTQC13M8qYvs8eT4X4W45zFKEvc0tsZh7_tkQEU90CxqHD3zW07tOtGAV_LNiQqlftDcIR-iO2JXSFDnWXuXSzOPldhrgE3EIx6qLqai54EkVky_DdBN5mny_P6g9gGOLJ7Ul0MRgF9dUqiAR6n7XzKwmA0AcdT_UQNHzCN91ENbzVQimq4amRbS7vPzySK3WsAUFyTVG4OC5V__G6I9xAWaSq5nqw6tX3ryVyNe8g8xHxMIkVcafawDJGEUqgUMq_eweW8cpwSjxfmZpN9pJUsOV4KSmYP0qONwFUPkOgQAxcjYqHVFW6mmZi3aWnKxmyz12GAwo8uksgCb2tHEPTtzRzAwlZ51fGRyXy61Xh7Pz96onz68a9xqr3uOMkDGVQ-N17sTewoBcxLmsZbJhTA3QuaJGTmBPOsM6hYtBa5ZouofGlr9rWTnRY2OQGYkT47tQsmeZumwv-27zhLahU9qCSUtr7GS6tx_ZGWuTqGs6l-dZhdSPadPewHFBjK-O5Fy8H_pOKS53Hwt-gkxvk15MhalM51BqQZWEFIcoWw4y58AT9ljTleWkoOu_A2ciozR1gE-8Wb-MucEoRzEG0yPPxQ_Tk3Mt3nUcsYhGmFtwG39oP5ETJ5atR0IyMv4n0ftw4c
Aug 18 02:26:13 <local0.debug> 10.217.208.250 08/18/2016:00:26:13 GMT NetsScaler 0-PPE-0 : default SSLVPN Message 1430 0 :  "WEBAUTH: Evaluating success rule for primary webauth action recaptcha_aaatm "
Aug 18 02:26:13 <local0.debug> 10.217.208.250 08/18/2016:00:26:13 GMT NetsScaler 0-PPE-0 : default AAATM Message 1432 0 :  "(14679-1852854087) Authentication succeeded, current factor: auth, for user: user1 "
Aug 18 02:26:13 <local0.debug> 10.217.208.250 08/18/2016:00:26:13 GMT NetsScaler 0-PPE-0 : default AAA Message 1435 0 :  "nFactor: Next factor passthrough1 is configured as passthough/implicit, loginschema LSCHEMA_INT"
Aug 18 02:26:13 <local0.debug> 10.217.208.250 08/18/2016:00:26:13 GMT NetsScaler 0-PPE-0 : default SSLVPN Message 1436 0 :  "ns_aaa_advance_authn_policyeval: copying policylabel name passthrough1 to aaa info, type 65 for auth "
Aug 18 02:26:14 <local0.debug> 10.217.208.250 08/18/2016:00:26:14 GMT NetsScaler 0-PPE-0 : default AAATM Message 1437 0 :  "(0-72) Authentication succeeded, current factor: passthrough1, for user: user1 "
 

Addendum

Below is the LoginSchema that’s used to render captcha

<?xml version="1.0" encoding="UTF-8"?>
<AuthenticateResponse xmlns="http://citrix.com/authentication/response/1">
<Status>success</Status>
<Result>more-info</Result>
<StateContext></StateContext>
<AuthenticationRequirements>
<PostBack>/nf/auth/doAuthentication.do</PostBack>
<CancelPostBack>/nf/auth/doLogoff.do</CancelPostBack>
<CancelButtonText>Cancel</CancelButtonText>
<Requirements>
<Requirement><Credential><ID>login</ID><SaveID>ExplicitForms-Username</SaveID><Type>username</Type></Credential><Label><Text>User name:</Text><Type>plain</Type></Label><Input><AssistiveText>Please supply either domain\username or user@fully.qualified.domain</AssistiveText><Text><Secret>false</Secret><ReadOnly>false</ReadOnly><InitialValue></InitialValue><Constraint>.+</Constraint></Text></Input></Requirement>
<Requirement><Credential><ID>passwd</ID><SaveID>ExplicitForms-Password</SaveID><Type>password</Type></Credential><Label><Text>Password:</Text><Type>plain</Type></Label><Input><Text><Secret>true</Secret><ReadOnly>false</ReadOnly><InitialValue></InitialValue><Constraint>.+</Constraint></Text></Input></Requirement>
<Requirement><Credential><ID>saveCredentials</ID><Type>savecredentials</Type></Credential><Label><Text>Remember my password</Text><Type>plain</Type></Label><Input><CheckBox><InitialValue>false</InitialValue></CheckBox></Input></Requirement>
<Requirement><Credential><ID>nf-recaptcha</ID><SaveID>ExplicitForms-Username</SaveID><Type>nf-recaptcha</Type></Credential><Label><Text>Captcha:</Text><Type>plain</Type></Label></Requirement>
<Requirement><Credential><Type>none</Type></Credential><Label><Text>Please enter your credentials and verify captcha</Text><Type>information</Type></Label><Input /></Requirement>
<Requirement><Credential><ID>loginBtn</ID><Type>none</Type></Credential><Label><Type>none</Type></Label><Input><Button>Log On</Button></Input></Requirement>
</Requirements>
</AuthenticationRequirements>
</AuthenticateResponse>
 

Below is the ns-nfactor.js (or script.js) that’s needed for captcha customization:
 
(function ($) {
 
        CTXS.ExtensionAPI.addPlugin({
                name: "ns-nfactor", // Name of plugin - must match name sent in configuration
                initialize: function () {
 
                        /*
                         * Custom credential handler for Google ReCaptcha.
                         * If a credential of type "nf-recaptcha" is sent in any factor, this code
                         * will be executed. The "submit" button of the form will be disabled
                         * by default until the captcha is completed correctly.
                         *
                         * Use with the below WebAuth action:
                         * add authentication webAuthAction recaptcha -serverIP <IP Address of google.com> -serverPort 443 -fullReqExpr q{"POST /recaptcha/api/siteverify HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Content-Type: applica
tion/x-www-form-urlencoded\r\n" + "Content-Length: 10000"+"\r\n\r\n" + "secret=<Secret key from Google Recaptcha>&response=" + http.req.body(10000).after_str("&recaptcha=")} -scheme https -successRule "http.RES.body(1000).REGEX_MATCH(re/
\"success\": true.*\"hostname\": \"<FQDN of the Gateway/Auth vServer>\"/)"
                         *
                         */
                        CTXS.ExtensionAPI.addCustomCredentialHandler({
                                getCredentialTypeName: function () { return "nf-recaptcha"; },
 
                                getCredentialTypeMarkup: function (requirements) {
                                        var reCaptchaSiteKey = "6LfFyScTAAAAAPdfN29CmnhUEybgrISKAXelYiqB"; //Replace this with the SiteKey from Google ReCaptcha
                                        var div = $("<div></div>");
                                        var captcha = $('<div id="g-recaptcha" class="g-recaptcha" data-callback="captchaSuccess"></div>').attr("data-sitekey", reCaptchaSiteKey);
                                        var input = $('<input id="captchaResponse" name="recaptcha" type="hidden" value=""/>');
                                        div.append(captcha, input);
                                        var script = document.createElement("script");
                                        script.type = "text/javascript";
                                        script.src = "https://www.google.com/recaptcha/api.js";
                                        script.onerror = function(){
                                                var $form = $(".credentialform")[0];
                                                var fieldDiv = $('<div class="field CredentialTypenone"></div>');
                                                var leftDiv = $('<div class="left"></div>');
                                                var label = $('<div class="standaloneText label error" role="alert"></div>').text("Could not load Google ReCaptcha. Please contact your administrator.");
                                                leftDiv.append(label);
                                                fieldDiv.append(leftDiv);
                                                $($form).append(fieldDiv);
                                        };
                                        $("head")[0].appendChild(script);
 
                                        $(document).on('ctxsformsauthenticationdisplayform', function (e, data) {
                                                var $form = data.authenticationElement.find('.credentialform');
                                                var $captcha = $form.find('#g-recaptcha');
                                                $loginButton = $form.find('#loginBtn');
                                                if ($captcha.length && $loginButton.length) {
                                                        // This is a Captcha form
                                                        // Disable the submit button by default
                                                        disableFormsButton($loginButton);
                                                }
                                        });
 
                                        return div;
                                }
                        });
 
 
                        /* End ReCaptcha code */
 
                }
        });
})(jQuery);
var $loginButton;
function captchaSuccess(data) {
        $("#captchaResponse").val(data);
        enableFormsButton($loginButton);
}
 
function disableFormsButton($button) {
        // Disable the button and stop it appearing clickable
        $button.addClass('disabled').removeAttr('href');
}
function enableFormsButton($button) {
        // Enable the button and make it clickable again
        $button.removeClass('disabled').attr('href', '#');
}


  
2. Below is the script.js that’s needed for CAPTCHA customization:
/*
 * Custom credential handler for Google ReCaptcha.
 * If a credential of type "nf-recaptcha" is sent in any factor, this code
 * will be executed. The "submit" button of the form will be disabled
 * by default until the CAPTCHA is completed correctly.
 *
 * Use with the below WebAuth action:
 * add authentication webAuthAction recaptcha -serverIP <IP Address of google.com> -serverPort 443 -fullReqExpr q{"POST /recaptcha/api/siteverify HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Content-Type: applica
tion/x-www-form-urlencoded\r\n" + "Content-Length: 10000"+"\r\n\r\n" + "secret=<Secret key from Google Recaptcha>&response=" + http.req.body(10000).after_str("&recaptcha=")} -scheme https -successRule "http.RES.body(1000).REGEX_MATCH(re/
\"success\": true.*\"hostname\": \"<FQDN of the Gateway/Auth vServer>\"/)"
 *
 */
CTXS.ExtensionAPI.addCustomCredentialHandler({
                  getCredentialTypeName: function () { return "nf-recaptcha"; },
 
                  getCredentialTypeMarkup: function (requirements) {
                                    var reCaptchaSiteKey = "XXXXX"; //Replace this with the SiteKey from Google ReCaptcha
                                    var div = $("<div></div>");
                                    var CAPTCHA = $('<div id="g-recaptcha" class="g-recaptcha" data-callback="captchaSuccess"></div>').attr("data-sitekey", reCaptchaSiteKey);
                                    var input = $('<input id="captchaResponse" name="recaptcha" type="hidden" value=""/>');
                                    div.append(CAPTCHA, input);
                                    var script = document.createElement("script");
                                    script.type = "text/javascript";
                                    script.src = "https://www.google.com/recaptcha/api.js";
                                    script.onerror = function(){
                                                      var $form = $(".credentialform")[0];
                                                      var fieldDiv = $('<div class="field CredentialTypenone"></div>');
                                                      var leftDiv = $('<div class="left"></div>');
                                                      var label = $('<div class="standaloneText label error" role="alert"></div>').text("Could not load Google ReCaptcha. Please contact your administrator.");
                                                      leftDiv.append(label);
                                                      fieldDiv.append(leftDiv);
                                                      $($form).append(fieldDiv);
                                    };
                                    $("head")[0].appendChild(script);
 
                                    $(document).on('ctxsformsauthenticationdisplayform', function (e, data) {
                                                      var $form = data.authenticationElement.find('.credentialform');
                                                      var $CAPTCHA = $form.find('#g-recaptcha');
                                                      $loginButton = $form.find('#loginBtn');
                                                      if ($CAPTCHA.length && $loginButton.length) {
                                                                        // This is a CAPTCHA form
                                                                        // Disable the submit button by default
                                                                        disableFormsButton($loginButton);
                                                      }
                                    });
 
                                    return div;
                  }
});
/* End ReCaptcha code */
 
var $loginButton;
function captchaSuccess(data) {
        $("#captchaResponse").val(data);
        enableFormsButton($loginButton);
}
 
function disableFormsButton($button) {
        // Disable the button and stop it appearing clickable
        $button.addClass('disabled').removeAttr('href');
}
function enableFormsButton($button) {
        // Enable the button and make it clickable again
        $button.removeClass('disabled').attr('href', '#');
}
 

3. nFactor for Gateway

If captcha is desired to work with Gateway vserver, then following commands can be used to leverage nFactor. Please note that all the existing commands above are needed, and they should not be changed. Only change needed is that the FQDN that is registered should now resolve to Gateway vserver.
add authnProfile nfactor_auth_profile –authnvsName auth
add vpn vserver sslvpn SSL <IP> <Port> -authnProfile nfactor_auth_profile

All the other commands remain unchanged.

Environment

The above mentioned sample code is provided to you as is with no representations, warranties or conditions of any kind. You may use, modify and distribute it at your own risk. CITRIX DISCLAIMS ALL WARRANTIES WHATSOEVER, EXPRESS, IMPLIED, WRITTEN, ORAL OR STATUTORY, INCLUDING WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT. Without limiting the generality of the foregoing, you acknowledge and agree that (a) the sample code may exhibit errors, design flaws or other problems, possibly resulting in loss of data or damage to property; (b) it may not be possible to make the sample code fully functional; and (c) Citrix may, without notice or liability to you, cease to make available the current version and/or any future versions of the sample code. In no event should the code be used to support ultra-hazardous activities, including but not limited to life support or blasting activities. NEITHER CITRIX NOR ITS AFFILIATES OR AGENTS WILL BE LIABLE, UNDER BREACH OF CONTRACT OR ANY OTHER THEORY OF LIABILITY, FOR ANY DAMAGES WHATSOEVER ARISING FROM USE OF THE SAMPLE CODE, INCLUDING WITHOUT LIMITATION DIRECT, SPECIAL, INCIDENTAL, PUNITIVE, CONSEQUENTIAL OR OTHER DAMAGES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Although the copyright in the code belongs to Citrix, any distribution of the sample code should include only your own standard copyright attribution, and not that of Citrix. You agree to indemnify and defend Citrix against any and all claims arising from your use, modification or distribution of the sample code.

Issue/Introduction

NetScaler product supports nFactor authentication from version 11.0. nFactor provides various possibilities, including; fine grained authentication based on user groups, location, etc. This article describes captcha for NetScaler login using Google’s reCaptcha.