Implement a nonce
Summarize
Summary of Implement a nonce
This guide explains how to implement a cryptographic nonce in the authentication header within ServiceNow to ensure one-time use of authentication tokens. Adding a nonce enhances security by preventing replay attacks during external authentication.
Show less
Configuration Steps
- Create a system property glide.authenticate.header.noncekey and set its value to your chosen nonce variable name (e.g., NONCE or NCE).
- Create a new custom table named uauthenticationnonce with a field unonce to store used nonce values.
- Under System Properties > Installation Exits, create a new item called DigestSingleSignOnNonce to override ExternalAuthentication (property glide.authenticate.externalproperty).
- Implement the provided server-side script in DigestSingleSignOnNonce to process authentication headers, verify the nonce, and validate encrypted credentials.
- Deactivate the existing DigestSingleSignOn installation exit by setting its Active status to false.
Key Features of the Script
- Extracts authentication data, encrypted digest, and nonce from request headers, URL parameters, or cookies.
- Generates a digest by encrypting the combination of username and nonce with a secret key using HmacSHA1.
- Validates the encrypted digest against the received data to authenticate the user.
- Checks the nonce against the uauthenticationnonce table to ensure it has not been used before, preventing replay.
- Inserts new nonce values into the table upon successful validation to track usage.
- Logs relevant information and returns appropriate authentication status messages.
Benefits for ServiceNow Customers
Implementing this nonce mechanism strengthens external authentication security by ensuring each authentication attempt is unique and cannot be reused. This reduces the risk of replay attacks and unauthorized access when integrating with external single sign-on or digest authentication systems.
Following these steps enables customers to customize their authentication flow securely while maintaining compatibility with existing user identification fields and keys.
Add a cryptographic nonce to the authentication header to ensure that it can only be used once.
- Create a system property called glide.authenticate.header.nonce_key and set its value to whatever variable name you're using for the nonce, such as NONCE or NCE.
- Create a new table called
u_authentication_nonce. Add a field to the table calledu_nonce. - Go to and create an item called DigestSingleSignOnNonce which overrides ExternalAuthentication (glide.authenticate.external_property).
- Add the following code to the script portion of the newly created DigestSingleSignOnNonce.
gs.include("PrototypeServer"); var DigestSingleSignOnNonce = Class.create(); DigestSingleSignOnNonce.prototype = { process : function() { var headerKey = GlideProperties.get("glide.authenticate.header.key", "SM_USER"); var headerDigestKey = GlideProperties.get("glide.authenticate.header.encrypted_key", "DIGEST"); var headerNonceKey = GlideProperties.get("glide.authenticate.header.nonce_key", "NCE"); var fieldName = GlideProperties.get("glide.authenticate.header.value", "user_name"); var fkey = GlideProperties.get("glide.authenticate.secret_key"); // Look in the Headers var data = request.getHeader(headerKey); var encdata = request.getHeader(headerDigestKey); var nonce = request.getHeader(headerNonceKey); // If not, then check the URL Parameters if (data == null || encdata == null || nonce == null) { data = request.getParameter(headerKey); encdata = request.getParameter(headerDigestKey); nonce = request.getParameter(headerNonceKey); } // then maybe its a cookie if (data == null || encdata == null || nonce == null) { var cookies = request.getCookies(); data = GlideCookieMan.getCookieValue(cookies, headerKey); encdata = GlideCookieMan.getCookieValue(cookies, headerDigestKey); nonce = GlideCookieMan.getCookieValue(cookies, headerNonceKey); } // if found run encryption if (data != null && encdata != null && nonce != null) { try { // Replace all spaces with plus(+)'s, converted in url encdata = encdata.replaceAll(' ', '+'); // ----- Encrypt the username|nonce var key = this.getDigest( data + "|" + nonce, fkey); // Check for match of received encoded data // and your encoding of user name if (encdata == key) { var ugr = new GlideRecord("sys_user"); ugr.initialize(); if (!ugr.isValidField(fieldName)) { GlideLog.warn("External authorization is set to use field: '"+ fieldName + "' which doesn't exist"); return "failed_missing_requirement"; } ugr.addQuery(fieldName, data); ugr.query(); if (!ugr.next()) { var userLoad = GlideUser.getUser(data); if (userLoad == null) return "failed_authentication"; ugr.initialize(); ugr.addQuery(fieldName, data); ugr.query(); if (!ugr.next()) return "failed_authentication"; } if (this.processNonce(nonce)){ var userName = ugr.getValue("user_name"); return userName; } else return "failed_missing_requirement"; } else { return "failed_authentication"; } } catch(e) { gs.log(e); return "failed_authentication"; } // Encoded data didn't match recieved Encoded data } else { return "failed_missing_requirement"; } }, getDigest : function( data, fkey ) { try { // default to something JDK 1.4 has var MAC_ALG = "HmacSHA1"; return SncAuthentication.encode(data, fkey, MAC_ALG); } catch (e) { gs.log(e.toString()); throw 'failed_missing_requirement'; } } , processNonce : function( sentNonce ) { var ngr = new GlideRecord("u_authentication_nonce"); ngr.addQuery("u_nonce", sentNonce); ngr.query(); if (ngr.next()) { gs.log("This SSO entry has already been processed! (Nonce: " + sentNonce + ")"); return false; } var ngrNew = new GlideRecord("u_authentication_nonce"); ngrNew.initialize(); ngrNew.u_nonce = sentNonce; ngrNew.insert(); gs.log("Inserted new nonce: " + sentNonce); return true; } }; - Once you've saved your new installation exit, go to the DigestSingleSignOn installation exit and make sure that it is set Active=false.
Your instance should now be configured to implement a nonce.