Risk-based authN

Philip Brusten philip.brusten at kuleuven.be
Thu Jun 1 04:32:45 EDT 2017


Hi,

This is a high-level-summary of what we achieved till now. We had to add 
certain things to the system configs to hook in to the authentication 
flow, so that is not ideal, but it is a proof-of-concept.

  - REST-service:
     - responsible for collecting audit logs from trusted sources. We 
also have a registry of people who go abroad (e.g. congress) which we 
included as a source information. The service will calculate the GeoInfo 
based on the IP (for now country, longitude, latitude, accuracyradius & 
lookuptime)
     - endpoint to calculate the risk of a idp login event. The service 
responds with a score between 0-100. The higher, the better and thus 
more trusted.

  - IdP modifications:
     - Read browser fingerprint (using 
https://github.com/Valve/fingerprintjs2).
         - Added fingerprint flow & view which is called everytime just 
before "PopulateClientStorageLoadContext" in the sso-abstract-flow. The 
view creates the fingerprint in the browser and it will be added to a 
FingerprintContext. I added the flow at that location because we needed 
Javascript interation with the browser everytime the user communicates 
with the IdP (e.g. new login, but also SSO).
         - Added the fingerprint to the audit log
     - Added a Java-REST-client to communicate with the above REST-service
     - Calculated the risk at:
         - MFA-authentication flow after we know who the user is 
(authn/Password of authn/SPNEGO) we check if the user did an opt-in for 
our strong authentication solution, if so it will always be triggered. 
If not, we check if there is any risk envolved, if so, we trigger strong 
authentication. I added our TransitionMap an nextFlowStrategy below.
         - We disabled to consistentAddress-check 
(idp.session.consistentAddress = false), so sessions could roam accross 
different IPs. As a countermeasure, we analyze the risk in the 
authn-flow.xml (right after "PopulateSessionContext"). I the risk is to 
high (or trust is to low), we remove the IdP session, so the user must 
authenticate, otherwise the user could just use its existing session.

JSON-payload we send from the IdP to our risk calculator:

{
"uid":"user1",
"app":"entityID_of_SP",
"fingerprint":"d8290e99db4af4632fba4b3ed09872",
"auth":"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"ip":"8.8.8.8"
}

We calculate risk (or trust, depends on how you look at it) based on:
  - known fingerprint?
  - known IP?
  - realistic traveltime (from location A to B in X time)?
  - normal time?
  - normal location?
  - more trusted IP-ranges (e.g. from managed devices)
  - user resides in country which we knew already (congress)
  - blacklisting
  - etc

It would be nice to get some comments on this design. Did we hook in at 
the right places or not?

If we have this solution in place, I think we could better sell our 
strong authentication solution (https://www.n-auth.com) to our users, as 
we can offer them session roaming and perhaps even longer session duration.

Regards,

Philip


MFA-authentication flow:

<util:map id="shibboleth.authn.MFA.TransitionMap">
     <entry key="">
         <bean parent="shibboleth.authn.MFA.Transition" 
p:nextFlowStrategy-ref="selectFirstFactor" />
     </entry>
     <entry key="authn/SPNEGO">
         <bean parent="shibboleth.authn.MFA.Transition">
             <property name="nextFlowStrategyMap">
                 <map>
                     <entry key="ReselectFlow" value="authn/Password" />
                     <entry key="proceed" value-ref="checkSecondFactor" />
                 </map>
             </property>
         </bean>
     </entry>
     <entry key="authn/Password">
         <bean parent="shibboleth.authn.MFA.Transition" 
p:nextFlowStrategy-ref="checkSecondFactor" />
     </entry>
     <!-- An implicit final rule will return whatever the final flow 
returns. -->
</util:map>

     <bean id="checkSecondFactor" 
parent="shibboleth.ContextFunctions.Scripted" factory-method="inlineScript"
         p:customObject-ref="customObjectsToSelectSecondFactor">
         <constructor-arg>
             <value>
             <![CDATA[
             nextFlow = 'authn/multifactor';

             // Go straight to second factor if we have to, or set up 
for an attribute lookup first.
             authCtx = 
input.getSubcontext("net.shibboleth.idp.authn.context.AuthenticationContext");
             mfaCtx = 
authCtx.getSubcontext("net.shibboleth.idp.authn.context.MultiFactorAuthenticationContext");
             //check is the current MFA-conftext is already acceptable, 
if so check if the user did an opt-in, otherwise finish (with value 'null')
             if (mfaCtx.isAcceptable()) {
                     logger.debug('MFA context is already acceptable but 
now we will check if there is an opt-in set for multifactor');
                     // Attribute check is required to decide if first 
factor alone is enough.
                     resCtx = 
input.getSubcontext("net.shibboleth.idp.attribute.resolver.context.AttributeResolutionContext", 
true);
resCtx.setPrincipal(input.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext").getPrincipalName());
                     logger.debug('resolving attrs for ' + 
input.getSubcontext("net.shibboleth.idp.authn.context.SubjectCanonicalizationContext").getPrincipalName());
resCtx.getRequestedIdPAttributeNames().add("MFAPreferredMethod");
resCtx.resolveAttributes(custom.get("resolverservice"));
                     // Check for an attribute that authorizes use of 
first factor.
                     attribute = 
resCtx.getResolvedIdPAttributes().get("MFAPreferredMethod");

                     // set the nextFlow to null and exit MFA-flow when:
                     // a: There are no values for MFAPreferredMethod 
(attribute==null) -> not true in case of opt-in
                     // b: MFAPreferredMethod is set, but does not 
contain 'MobileTwoFactorContract' -> not true in case of opt-in
                     if (attribute == null || (attribute != null && ! 
attribute.getValues().contains(new 
valueType("urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract")))) 
{
                         //calculate risk
                         risk = custom.get("riskPredicate").apply(input);
                         logger.debug('Risk check result: ' + risk);

                          if ( risk == false ) {
                               nextFlow = null;
                         }
                     }
                     input.removeSubcontext(resCtx);   // cleanup
                 }
                 nextFlow;   // pass control to second factor or end 
with the first
             ]]>
             </value>
         </constructor-arg>
     </bean>





On 9/05/2017 16:39, Philip Brusten wrote:
> Hi,
>
> we are thinking about making a service which collects information from 
> trusted sources with information like: timestamp, application, userid, 
> user-agent, browser-fingerprint, IP (+geo-ip), etc. We could then ask 
> that service during a login to calculate the risk involved for that 
> login (e.g. geo-distance, same browser, etc). If the IdP decides the 
> risk is too high it could enforce multi-factor-authentication.
>
> If we could get enough assurance that the IdP session comes from the 
> same user/browser, we could perhaps disable the 
> consistentAddress-check, and elevate the authentication level when 
> necessary.
>
> Is anyone doing the same thing?
> Are there any existing services out there which we could use?
>
> Kind regards,
>
> Philip
>
>



More information about the users mailing list