aaCustomDecision.js
/*
Risk Policy - Signal Scores
*/
var aa_impossible_travel_score = 40;
var aa_credential_stuffing_score = 100;
var aa_automated_user_agent_score = 25;
var aa_brute_force_score = 100;
var aa_suspicious_ip_score = 35;
/*
Risk Policy - Custom Signals
*/
var aa_use_anonymizer_detection = 1;
var aa_use_hibp_detection = 1;
var custom_aa_tor_detected_score = 50;
var custom_aa_vpn_detected_score = 20;
var custom_aa_proxy_detected_score = 5;
var custom_aa_hibp_score = 40;
/*
Risk Policy - Thresholds And Extra Features
*/
var aa_medium_risk_threshold = 30;
var aa_high_risk_threshold = 75;
var aa_max_signal_count_high_risk_override = 99;
/*
Risk Policy - Method
0 - highest score out of all triggered signals
1 - summary of all triggered signals
*/
var aa_risk_method=1;
/*
Risk Policy - UEBA method
0 - highest score out of 3 models
1 - average score out of 3 models
*/
var aa_ueba_method=0;
/*
Risk Policy - Overrides
Whitelist - false positive control
Blacklist - preventative block
VPN whitelist - overrides Impossible Travel signal if moved to own VPN range
Example - ip_whitelist or ip_blacklist = ["62.21.63.30-62.21.63.30","82.21.168.1-82.21.168.255"];
*/
var ip_whitelist = [];
var ip_blacklist = [];
var ip_vpn_whitelist = [];
/*
Custom signals parameters
*/
//IPQualityScore API
var USER_AGENT = "IPQS User agent"
var API_KEY = "Insert your IPQS API Key here"
var HIBP_API_KEY = "HIBP API KEY HERE"
/********************************************************
The engine *
*/
outcome = "failed"; //default outcome
//Define variables
var signal_count = 0;
var pos = 0;
var arr_scores = [];
var arr_scores_models = [];
var score = 0;
var predictionResultChopped;
var predictionResultChoppedVal;
//Define signal variables and assign defaults (negative)
var is_impossible_travel = 0;
var is_credential_stuffing = 0;
var is_automated_user_agent = 0;
var is_brute_force = 0;
var is_suspicious_ip = 0;
var model1_score = 0;
var model2_score = 0;
var model3_score = 0;
var isAnonymizedResult;
var isPwnedResult;
var impossibleTravellerOverride = 0;
//Get risk data
var predictionResultRaw = sharedState.get("predictionResult");
var predictionResultString = predictionResultRaw.toString();
var result;
function inet_aton (ip) {
return ip.split(".").reduce((int, v) => int * 256 + +v);
}
function haveIBP() {
isPwnedResult = "error";
var password = transientState.get("password");
if (password) {
var DIGITS_UPPER = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
var md = java.security.MessageDigest.getInstance("SHA-1");
var passwordObject = {password: java.lang.String(password)};
var hashedPasswordBytes = md.digest(passwordObject.password.getBytes("utf8"));
var l = hashedPasswordBytes.length;
var out = [];
for (var i = 0, j = 0; i < l; i++) {
out[j++] = DIGITS_UPPER[(0xF0 & hashedPasswordBytes[i]) >>> 4];
out[j++] = DIGITS_UPPER[0x0F & hashedPasswordBytes[i]];
};
var hash = out.join("")
var lookup = hash.slice(0, 5)
var suffix = hash.slice(5)
var requestHIBP = new org.forgerock.http.protocol.Request();
requestHIBP.setMethod('GET');
requestHIBP.setUri('https://api.pwnedpasswords.com/range/' + lookup);
requestHIBP.getHeaders().add("Accept","*/*");
requestHIBP.getHeaders().add("Content-Type","application/json");
requestHIBP.getHeaders().add("User-Agent", USER_AGENT);
requestHIBP.getHeaders().add("hibp-api-key", HIBP_API_KEY);
var responseHIBP = httpClient.send(requestHIBP).get();
if (responseHIBP.getStatus().getCode() === 200) {
var payloadHIBP = responseHIBP.getEntity().getString();
if (payloadHIBP.indexOf(suffix) == -1) {
isPwnedResult = "clear";
} else {
isPwnedResult = "failed";
}
}
}
}
function isAnonymized() {
var payload = sharedState.get("IPQualityScore")
if (payload) {
var jsonResult = JSON.parse(payload);
}
else {
var ipaddress = requestHeaders.get("X-FORWARDED-FOR").get(0).split(",")[0].trim();
var request = new org.forgerock.http.protocol.Request();
request.setMethod("GET");
request.setUri("https://ipqualityscore.com/api/json/ip/" + API_KEY + "/" + ipaddress + "?strictness=0&allow_public_access_points=false&fast=false&lighter_penalties=false&mobile=false");
request.getHeaders().add("Accept","application/json");
request.getHeaders().add("User-Agent", USER_AGENT);
var response = httpClient.send(request).get();
if (response.getStatus().getCode() === 200) {
var payload = response.getEntity().getString();
var jsonResult = JSON.parse(payload)
if (jsonResult.success === true) {
sharedState.put("Debug-IPQualityScore", payload);
}
}
}
if (jsonResult) {
if (jsonResult.tor === true) {
isAnonymizedResult = "tor";
} else if (jsonResult.vpn === true) {
isAnonymizedResult= "vpn";
} else if (jsonResult.proxy === true) {
isAnonymizedResult = "proxy";
} else {
isAnonymizedResult = "not_detected";
}
}
}
if(aa_use_anonymizer_detection==1)
{
isAnonymized();
sharedState.put("custom_aa_isAnonymized",isAnonymizedResult);
if(isAnonymizedResult=="vpn")
{
arr_scores.push(custom_aa_vpn_detected_score);
}
else if(isAnonymizedResult=="proxy")
{
arr_scores.push(custom_aa_proxy_detected_score);
}
else if(isAnonymizedResult=="tor")
{
arr_scores.push(custom_aa_tor_detected_score);
}
else
{
arr_scores.push(0);
}
}
if(aa_use_hibp_detection==1) {
haveIBP();
sharedState.put("custom_aa_isPwned",isPwnedResult);
if(isPwnedResult!="error" && isPwnedResult!="clear") {
arr_scores.push(0);
}
else {
arr_scores.push(custom_aa_hibp_score);
}
}
if(predictionResultString.search("risk_score_data")>=0)
{
outcome = "low"; //default if there's data from risk API
}
//Check if we're assessing impossible travel and assign result
pos = predictionResultString.search("impossibleTravellerCheck=true");
if(pos>0)
{
pos = predictionResultString.search("is_impossible_travel=false");
if(pos<0)
{
//Check if it's a whitelisted VPN
pos = predictionResultString.search("dest_ip=");
predictionResultChopped=predictionResultString.substring(pos);
predictionResultChoppedVal=predictionResultChopped.substring(8,predictionResultChopped.search(","));
src_ipaddress_dec = inet_aton(predictionResultChoppedVal);
if(ip_vpn_whitelist.length>0)
{
for (var i = 0; i < ip_vpn_whitelist.length; i++)
{
list_entry = ip_vpn_whitelist[i].split("-");
list_first_ipaddress_dec=inet_aton(list_entry[0]);
list_last_ipaddress_dec=inet_aton(list_entry[1]);
if(src_ipaddress_dec>=list_first_ipaddress_dec && src_ipaddress_dec<=list_last_ipaddress_dec)
{
impossibleTravellerOverride=1;
sharedState.put("debug-whitelist-vpn","condition met for: " + predictionResultChoppedVal + ", outcome->no impossible travel");
}
}
}
if(impossibleTravellerOverride==0)
{
signal_count++;
is_impossible_travel=1;
arr_scores.push(aa_impossible_travel_score);
}
}
}
//Check if we're assessing credential stuffing and assign result
pos = predictionResultString.search("credentialStuffing=true");
if(pos>0)
{
pos = predictionResultString.search("is_credential_stuffing=false");
if(pos<0)
{
signal_count++;
is_credential_stuffing=1;
arr_scores.push(aa_credential_stuffing_score);
}
}
//Check if we're assessing automated user angent (antibot) and assign result
pos = predictionResultString.search("automatedUserAgentsFilter=true");
if(pos>0)
{
pos = predictionResultString.search("is_automated_user_agent=false");
if(pos<0)
{
signal_count++;
is_automated_user_agent=1;
arr_scores.push(aa_automated_user_agent_score);
}
}
//Check if we're assessing brute-force and assign result
pos = predictionResultString.search("bruteForcePreventionCheck=true");
if(pos>0)
{
pos = predictionResultString.search("is_brute_force=false");
if(pos<0)
{
signal_count++;
is_brute_force=1;
arr_scores.push(aa_brute_force_score);
}
}
//Check if we're assessing suspicious IP and assign result
pos = predictionResultString.search("suspiciousIPCheck=true");
if(pos>0)
{
pos = predictionResultString.search("is_suspicious_ip=false");
if(pos<0)
{
signal_count++;
is_suspicious_ip=1;
arr_scores.push(aa_suspicious_ip_score);
}
}
//Check if we're assessing UEBA and assign result
pos = predictionResultString.search("anomalyDetection=true");
if(pos>0)
{
pos = predictionResultString.search("clustering_result=");
if(pos>0)
{
predictionResultChopped=predictionResultString.substring(pos);
predictionResultChopped=predictionResultChopped.substring(predictionResultChopped.search("risk_score="));
predictionResultChoppedVal=predictionResultChopped.substring(11,predictionResultChopped.search(","));
predictionResultChopped=predictionResultChopped.substring(11);
model1score=parseInt(predictionResultChoppedVal,10);
predictionResultChopped=predictionResultChopped.substring(predictionResultChopped.search("risk_score="));
predictionResultChoppedVal=predictionResultChopped.substring(11,predictionResultChopped.search(","));
predictionResultChopped=predictionResultChopped.substring(11);
model2score=parseInt(predictionResultChoppedVal,10);
predictionResultChopped=predictionResultChopped.substring(predictionResultChopped.search("risk_score="));
predictionResultChoppedVal=predictionResultChopped.substring(11,predictionResultChopped.search(","));
predictionResultChopped=predictionResultChopped.substring(11);
model3score=parseInt(predictionResultChoppedVal,10);
arr_scores_models.push(model1score,model2score,model3score);
}
}
//Deliver risk score
if(aa_ueba_method==0)
{
score = Math.max.apply(null, arr_scores_models);
}
else if(aa_ueba_method==1)
{
score = arr_scores_models.reduce((a, b) => a + b, 0)/arr_scores_models.length;
}
arr_scores.push(score);
if(aa_risk_method==0)
{
score = Math.max.apply(null, arr_scores);
}
else if (aa_risk_method===1)
{
score = arr_scores.reduce((a, b) => a + b, 0);
}
//Deliver risk outcome
if(score>aa_medium_risk_threshold)
{
outcome="medium";
}
if(score>aa_high_risk_threshold)
{
outcome="high";
}
if(signal_count>=aa_max_signal_count_high_risk_override && outcome=="medium")
{
outcome="high";
sharedState.put("debug-signal-count-override","true");
}
//process the blacklist and whitelist
var src_ipaddress;
var src_ipaddress_dec;
var list_first_ipaddress_dec;
var list_last_ipaddress_dec;
var list_entry;
var logmessage;
src_ipaddress = requestHeaders.get("X-FORWARDED-FOR").get(0).split(",")[0].trim();
src_ipaddress_dec = inet_aton(src_ipaddress);
if(ip_blacklist.length>0)
{
for (var i = 0; i < ip_blacklist.length; i++)
{
list_entry = ip_blacklist[i].split("-");
list_first_ipaddress_dec=inet_aton(list_entry[0]);
list_last_ipaddress_dec=inet_aton(list_entry[1]);
if(src_ipaddress_dec>=list_first_ipaddress_dec && src_ipaddress_dec<=list_last_ipaddress_dec)
{
sharedState.put("debug-blacklist","condition met for: " + src_ipaddress + ", " + outcome + "->high");
outcome="high";
}
}
}
if(ip_whitelist.length>0)
{
for (var i = 0; i < ip_whitelist.length; i++)
{
list_entry = ip_whitelist[i].split("-");
list_first_ipaddress_dec=inet_aton(list_entry[0]);
list_last_ipaddress_dec=inet_aton(list_entry[1]);
if(src_ipaddress_dec>=list_first_ipaddress_dec && src_ipaddress_dec<=list_last_ipaddress_dec)
{
sharedState.put("debug-whitelist","condition met for: " + src_ipaddress + ", " + outcome + "->low");
outcome = "low";
}
}
}
sharedState.put('debug-score',score.toString());
sharedState.put('debug-signal-count',signal_count.toString());
sharedState.put('debug-outcome',outcome);