You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@shiro.apache.org by Richard Wheeldon <ri...@voxsmart.com> on 2017/01/06 18:27:41 UTC

MFA - Possible Solution?

Hi,

As a few of you may know, I posted many, many months ago about trying to get Multi Factor Authentication working on a Shiro-based app. I think I have a plan that isn't totally crazy and doesn't involve stupid levels of custom code or changing any of the core Shiro libs. However, I'd like it run it by you guys to see what you think and poke holes in it before I turn it into prod-ready code. Maybe there's something here that folks can adapt in the future if they need to do something similar.

I started off with a pre-existing config with a DefaultWebSessionManager and a JDBC realm reading username / password and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it's fairly bog-standard stuff.

What I'm doing now is to split the JDBC logic into two realms. The first is the same password stuff:

passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
passwordJdbcRealm.permissionsLookupEnabled = true
passwordJdbcRealm.authenticationQuery = select password from users where ...
passwordJdbcRealm.userRolesQuery = select ...
passwordJdbcRealm.dataSource = $dataSource
passwordJdbcRealm.credentialsMatcher = $passwordMatcher
passwordJdbcRealm.permissionsQuery = select ...

The second is for accessing one-time authentication tokens:

tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
tokenJdbcRealm.permissionsLookupEnabled = false
tokenJdbcRealm.authenticationQuery = select token from auth_tokens where ...
tokenJdbcRealm.userRolesQuery = select null where ? is not null and false (must be a neater way just to disable role lookups but this works)
tokenJdbcRealm.dataSource = $dataSource
tokenJdbcRealm.credentialsMatcher = $tokenMatcher

Both of these are now bound to the same old security manager:

securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm

I've now also got two form filters - one for the password and a new one for the one-time token:
passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
passwordFormAuth.loginUrl = /login.jsp

tokenFormAuth = com.voxsmart....TokenFormAuthenticationFilter
tokenFormAuth.loginUrl = /login2.jsp

login2.jsp redirects to login.jsp if the user isn't authenticated.

These form filters are wired up like so:

/login.jsp = passwordFormAuth
/login2.jsp = tokenFormAuth
/logout = logout
/** = tokenFormAuth

The first is basic except for sending out a token if the login succeeds:
@Override
public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
                                // Generate a one-time token, write it to the DB and send it to the user via SMS / e-mail / carrier pigeon
}

The second checks that an MFA attribute is set on the session in order to allow access and sets it if / only if the token match is found:

private boolean isMfaOk(Object mfaStatus) {
                               return "ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaStatus);
                }

@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional check for MFA
(!isLoginRequest(request, response) && isPermissive(mappedValue)); // Copy-n-paste from AuthenticatingFilter
                }

@Override
                public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
boolean ret = super.onLoginSuccess(token, subject, request, response);
subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
// write some audit log entries and other fluff
return ret;
}

Does this seem sane? Is there a better way to do it? Is there something I'm missing or forgotten that will cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to make it easier in the future?

Regards,

Richard


RE: MFA - Possible Solution?

Posted by Richard Wheeldon <ri...@voxsmart.com>.
Got it. My solution gives you something approaching incremental auth but at the cost of additional complexity – and it’s arguably a bit hacky. Doing a single page with a bit of JS before login is viable providing that generated a token for a user who’s provided a wrong password isn’t an issue. For SMS I think that’s nasty.

My first thought was to use a permission as Brian suggested rather than a session property but it didn’t work quite as neatly as I hoped. Partly because I’d assume that a permission returned by the JdbcRealm would only be returned if the authentication had been approved by that same realm. This doesn’t appear to be the case.

Richard

From: Björn Raupach [mailto:raupach@me.com]
Sent: Monday, January 9, 2017 4:38 PM
To: user@shiro.apache.org
Subject: Re: MFA - Possible Solution?

Hello Brian, hello Richard

my shiro.ini is actually rather simple:

  fauthc = org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter
  fauthc.loginUrl = /index.jsf
  fauthc.successUrl = /secure/index.jsf

  authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
  securityManager.authenticator.authenticationStrategy = $authcStrategy
  securityManager.realms = $jdbcRealm, $u2fRealm

  [users]

  [roles]

  [urls]
  /index.jsf = ssl
  /login/** = ssl
  /secure/** = ssl, fauthc
  /logout = ssl, logout

The folder /login just has some pages that act like a wizard. First you enter username and password, then next page you toggle the U2F device then we create an AuthenticationToken and submit it. So both factors are stored
together in one token. I did not know how to do it otherwise.

There is clunky user experience within the wizard since it is a all or nothing approach. All realms have to commit to one single token. Eventually it all boils down to this:

  Subject currentUser = SecurityUtils.getSubject();
  currentUser.login(token);

I just accept every username and password and can only verify at the last page if the credentials are correct. It would be nice to authenticate UsernamePasswordToken first and then proceed to the second page and create a dedicated token for U2FRealm or some MFA realm in general. I found no way to do it. You can’t do incremental authentication in Shiro. Maybe a SecurityUtils.getSubject().tryLogin(AuthenticationToken) would be useful. So
you could at least try the first factor and then proceed to another factor.

Permissions I really don’t use much. The path access is all I care about. This is a rather simple application that I can use to try out things.

/Björn
On 9 Jan 2017, at 16:01, Brian Demers <br...@gmail.com>> wrote:

This is a great thread, thanks Björn and Richard!

These two approaches are very similar. The AllSuccessfulStrategy (or a custom one) would be important here if you want to ensure both realms have been queried, but I'm assuming this will fail the initial authentication request due to the 2nd factor not being present (Björn, it sounds like you have something working, so please correct me if i'm wrong).

Checking to see if the 2nd factor was checked 'isMfaOk()' could also be an Authorization check (a permission check of 'user:2factor', or something)
Your 2nd realm would still need to verify the AuthC for the token.  But your application's resources could require the 2nd factor or not by using: subject.checkPermission("user:2factor"), or equivalent annotation.



On Mon, Jan 9, 2017 at 6:40 AM, Richard Wheeldon <ri...@voxsmart.com>> wrote:
So I’m guessing in your example the auths are wired more like this:
/password-login = passwordFormAuth
/token-login = passwordFormAuth, tokenFormAuth
/** = passwordFormAuth, tokenFormAuth

Is that right? If so, it makes sense and is probably neater that my solution. It might also give a point for wiring in IP address checking. e.g.
/** = ipRangeAuth, passwordFormAuth, tokenFormAuth

Slightly OT but here’s my take on SMS auth in general:

  *   It has a weakness because if you have the ability to reset the password via e-mail and an end-user has inappropriately secured e-mail on their phone (pretty common) then the possession of the phone becomes a single factor for authentication. If you don’t allow password resets via e-mail you wind up with another usability issue.
  *   I agree with your points on the reliability and 3rd party concerns.
  *   NIST have deprecated it largely because of the VOIP concerns. In our scenario we can be almost certain that the number in question will represent a physical mobile phone. This is not true for many use-cases so there’s another potential weakness. However, imperfect SMS-based 2FA is still better than nothing.
  *   It’s better for usability purely because a user already knows how to receive a text message 99% of the time and they tend to carry phones around with them already. Hence if you’ve got a lot of users who use the site / app rarely it’s a simpler option.
  *   If you have a lot of users but access is rare, SMS is much cheaper than hard-token. If you have a few users making regular access hard-token is much cheaper than SMS.

From a Shiro point of view:

  *   It’s largely irrelevant as the workflow for a soft-token like Google authenticator would (AIUI) work in pretty much the same way.
  *   Using JdbcRealm for obtaining an SMS token is a bit dubious because the realm only returns one token and it’s quite likely if there is an SMS delay that the user will retry and we’re then left with two tokens generated and the user may try either. It wasn’t something I thought of when I wrote the original mail so I think I’ll have to have to consider this carefully.

Thanks for the U2F link and I’ll be sure to take a look,

Richard

From: Björn Raupach [mailto:raupach@me.com<ma...@me.com>]
Sent: Monday, January 9, 2017 7:20 AM

To: user@shiro.apache.org<ma...@shiro.apache.org>
Subject: Re: MFA - Possible Solution?


Interesting. We were looking into SMS, too but I don’t find it user friendly at all. You rely on a third party. SMS often don’t arrive within seconds and there is an additional cost in every sms you send.

I don’t get your difference in workflows. AllSuccessfulStrategy says all realms have to authenticate. Thats why we needed to create a AuthenticationToken that works on both realms. It has the username and password and the challenge from the token generator. First page collects username and password, second page creates token. Then submit them all. You can’t generate the token in advance because you need to find the matching user first.

Have a look at U2F! It is a rather cheap hardware token generator. You can get one for less than 20 EUR. Use it on many sites and there are no additional license costs. Only downside is it works currently only Chrome and Firefox (with an extension).

https://www.yubico.com/about/background/fido/
http://www.howtogeek.com/232314/u2f-explained-how-google-microsoft-and-others-are-creating-universal-two-factor-authentication-tokens/
https://fidoalliance.org/specifications/overview/



On 9 Jan 2017, at 01:43, Richard Wheeldon <ri...@voxsmart.com>> wrote:

I think your idea seems equally reasonable. I considered something similar. However, we’re using SMS as the 2nd factor, which is imperfect from a security point of view but much better from a distribution and usability point of view. I’m guessing you have some sort of token generator for the 2nd factor. The key point being that there’s a difference in workflow:
                User supplies username + password -> System sends token to user -> User inputs token
vs.
                User generates token -> User supplies username, password and token

Richard

From: Björn Raupach [mailto:raupach@me.com]
Sent: Sunday, January 8, 2017 12:02 PM
To: user@shiro.apache.org<ma...@shiro.apache.org>
Subject: Re: MFA - Possible Solution?

Hi Richard,

I am currently working on an implementation for U2F  with Apache Shiro. It is still not officially
rolled out but it works and I am also not sure if I did it correct.

Like you I created two realms. Then however I changed the authentication strategy in the
security manager.

authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
securityManager.realms = $jdbcRealm, $u2fRealm

I used U2FAuthenticationToken that extends UsernamePasswordToken
that works on both realms. The custom U2FRealm overrides supports and checks that the token
is an instance of U2FAuthenticationToken.


On 6 Jan 2017, at 19:27, Richard Wheeldon <ri...@voxsmart.com>> wrote:

Hi,

As a few of you may know, I posted many, many months ago about trying to get Multi Factor Authentication working on a Shiro-based app. I think I have a plan that isn’t totally crazy and doesn’t involve stupid levels of custom code or changing any of the core Shiro libs. However, I’d like it run it by you guys to see what you think and poke holes in it before I turn it into prod-ready code. Maybe there’s something here that folks can adapt in the future if they need to do something similar.

I started off with a pre-existing config with a DefaultWebSessionManager and a JDBC realm reading username / password and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it’s fairly bog-standard stuff.

What I’m doing now is to split the JDBC logic into two realms. The first is the same password stuff:

passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
passwordJdbcRealm.permissionsLookupEnabled = true
passwordJdbcRealm.authenticationQuery = select password from users where …
passwordJdbcRealm.userRolesQuery = select …
passwordJdbcRealm.dataSource = $dataSource
passwordJdbcRealm.credentialsMatcher = $passwordMatcher
passwordJdbcRealm.permissionsQuery = select …

The second is for accessing one-time authentication tokens:

tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
tokenJdbcRealm.permissionsLookupEnabled = false
tokenJdbcRealm.authenticationQuery = select token from auth_tokens where …
tokenJdbcRealm.userRolesQuery = select null where ? is not null and false (must be a neater way just to disable role lookups but this works)
tokenJdbcRealm.dataSource = $dataSource
tokenJdbcRealm.credentialsMatcher = $tokenMatcher

Both of these are now bound to the same old security manager:

securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm

I’ve now also got two form filters - one for the password and a new one for the one-time token:
passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
passwordFormAuth.loginUrl = /login.jsp

tokenFormAuth = com.voxsmart….TokenFormAuthenticationFilter
tokenFormAuth.loginUrl = /login2.jsp

login2.jsp redirects to login.jsp if the user isn’t authenticated.

These form filters are wired up like so:

/login.jsp = passwordFormAuth
/login2.jsp = tokenFormAuth
/logout = logout
/** = tokenFormAuth

The first is basic except for sending out a token if the login succeeds:
@Override
public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
                                // Generate a one-time token, write it to the DB and send it to the user via SMS / e-mail / carrier pigeon
}

The second checks that an MFA attribute is set on the session in order to allow access and sets it if / only if the token match is found:

private boolean isMfaOk(Object mfaStatus) {
                               return "ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaStatus);
                }

@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional check for MFA
(!isLoginRequest(request, response) && isPermissive(mappedValue)); // Copy-n-paste from AuthenticatingFilter
                }

@Override
                public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
boolean ret = super.onLoginSuccess(token, subject, request, response);
subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
// write some audit log entries and other fluff
return ret;
}

Does this seem sane? Is there a better way to do it? Is there something I’m missing or forgotten that will cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to make it easier in the future?

Regards,

Richard




Re: MFA - Possible Solution?

Posted by Björn Raupach <ra...@me.com>.
Hello Brian, hello Richard

my shiro.ini is actually rather simple:

  fauthc = org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter
  fauthc.loginUrl = /index.jsf
  fauthc.successUrl = /secure/index.jsf

  authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
  securityManager.authenticator.authenticationStrategy = $authcStrategy
  securityManager.realms = $jdbcRealm, $u2fRealm

  [users]

  [roles]

  [urls]
  /index.jsf = ssl
  /login/** = ssl
  /secure/** = ssl, fauthc
  /logout = ssl, logout

The folder /login just has some pages that act like a wizard. First you enter username and password, then next page you toggle the U2F device then we create an AuthenticationToken and submit it. So both factors are stored
together in one token. I did not know how to do it otherwise. 

There is clunky user experience within the wizard since it is a all or nothing approach. All realms have to commit to one single token. Eventually it all boils down to this:

  Subject currentUser = SecurityUtils.getSubject();
  currentUser.login(token);

I just accept every username and password and can only verify at the last page if the credentials are correct. It would be nice to authenticate UsernamePasswordToken first and then proceed to the second page and create a dedicated token for U2FRealm or some MFA realm in general. I found no way to do it. You can’t do incremental authentication in Shiro. Maybe a SecurityUtils.getSubject().tryLogin(AuthenticationToken) would be useful. So
you could at least try the first factor and then proceed to another factor.

Permissions I really don’t use much. The path access is all I care about. This is a rather simple application that I can use to try out things.

/Björn

> On 9 Jan 2017, at 16:01, Brian Demers <br...@gmail.com> wrote:
> 
> This is a great thread, thanks Björn and Richard!
> 
> These two approaches are very similar. The AllSuccessfulStrategy (or a custom one) would be important here if you want to ensure both realms have been queried, but I'm assuming this will fail the initial authentication request due to the 2nd factor not being present (Björn, it sounds like you have something working, so please correct me if i'm wrong).
> 
> Checking to see if the 2nd factor was checked 'isMfaOk()' could also be an Authorization check (a permission check of 'user:2factor', or something)
> Your 2nd realm would still need to verify the AuthC for the token.  But your application's resources could require the 2nd factor or not by using: subject.checkPermission("user:2factor"), or equivalent annotation.
> 
> 
> 
> On Mon, Jan 9, 2017 at 6:40 AM, Richard Wheeldon <richard.wheeldon@voxsmart.com <ma...@voxsmart.com>> wrote:
> So I’m guessing in your example the auths are wired more like this:
> 
> /password-login = passwordFormAuth
> 
> /token-login = passwordFormAuth, tokenFormAuth
> 
> /** = passwordFormAuth, tokenFormAuth
> 
>  
> 
> Is that right? If so, it makes sense and is probably neater that my solution. It might also give a point for wiring in IP address checking. e.g.
> 
> /** = ipRangeAuth, passwordFormAuth, tokenFormAuth
> 
>  
> 
> Slightly OT but here’s my take on SMS auth in general:
> 
> It has a weakness because if you have the ability to reset the password via e-mail and an end-user has inappropriately secured e-mail on their phone (pretty common) then the possession of the phone becomes a single factor for authentication. If you don’t allow password resets via e-mail you wind up with another usability issue.
> I agree with your points on the reliability and 3rd party concerns.
> NIST have deprecated it largely because of the VOIP concerns. In our scenario we can be almost certain that the number in question will represent a physical mobile phone. This is not true for many use-cases so there’s another potential weakness. However, imperfect SMS-based 2FA is still better than nothing.
> It’s better for usability purely because a user already knows how to receive a text message 99% of the time and they tend to carry phones around with them already. Hence if you’ve got a lot of users who use the site / app rarely it’s a simpler option.
> If you have a lot of users but access is rare, SMS is much cheaper than hard-token. If you have a few users making regular access hard-token is much cheaper than SMS.
>  
> 
> From a Shiro point of view:
> 
> It’s largely irrelevant as the workflow for a soft-token like Google authenticator would (AIUI) work in pretty much the same way.
> Using JdbcRealm for obtaining an SMS token is a bit dubious because the realm only returns one token and it’s quite likely if there is an SMS delay that the user will retry and we’re then left with two tokens generated and the user may try either. It wasn’t something I thought of when I wrote the original mail so I think I’ll have to have to consider this carefully.
>  
> 
> Thanks for the U2F link and I’ll be sure to take a look,
> 
>  
> 
> Richard
> 
>  
> 
> From: Björn Raupach [mailto:raupach@me.com <ma...@me.com>] 
> Sent: Monday, January 9, 2017 7:20 AM
> 
> 
> To: user@shiro.apache.org <ma...@shiro.apache.org>
> Subject: Re: MFA - Possible Solution?
> 
>  
> 
> Interesting. We were looking into SMS, too but I don’t find it user friendly at all. You rely on a third party. SMS often don’t arrive within seconds and there is an additional cost in every sms you send.
> 
>  
> 
> I don’t get your difference in workflows. AllSuccessfulStrategy says all realms have to authenticate. Thats why we needed to create a AuthenticationToken that works on both realms. It has the username and password and the challenge from the token generator. First page collects username and password, second page creates token. Then submit them all. You can’t generate the token in advance because you need to find the matching user first.
> 
>  
> 
> Have a look at U2F! It is a rather cheap hardware token generator. You can get one for less than 20 EUR. Use it on many sites and there are no additional license costs. Only downside is it works currently only Chrome and Firefox (with an extension).
> 
>  
> 
> https://www.yubico.com/about/background/fido/ <https://www.yubico.com/about/background/fido/>
> http://www.howtogeek.com/232314/u2f-explained-how-google-microsoft-and-others-are-creating-universal-two-factor-authentication-tokens/ <http://www.howtogeek.com/232314/u2f-explained-how-google-microsoft-and-others-are-creating-universal-two-factor-authentication-tokens/>
> https://fidoalliance.org/specifications/overview/ <https://fidoalliance.org/specifications/overview/>
>  
> 
>  
> 
>  
> 
> On 9 Jan 2017, at 01:43, Richard Wheeldon <richard.wheeldon@voxsmart.com <ma...@voxsmart.com>> wrote:
> 
>  
> 
> I think your idea seems equally reasonable. I considered something similar. However, we’re using SMS as the 2nd factor, which is imperfect from a security point of view but much better from a distribution and usability point of view. I’m guessing you have some sort of token generator for the 2nd factor. The key point being that there’s a difference in workflow:
> 
>                 User supplies username + password -> System sends token to user -> User inputs token
> 
> vs.
> 
>                 User generates token -> User supplies username, password and token
> 
>  
> 
> Richard
> 
>  
> 
> From: Björn Raupach [mailto:raupach@me.com <ma...@me.com>] 
> Sent: Sunday, January 8, 2017 12:02 PM
> To: user@shiro.apache.org <ma...@shiro.apache.org>
> Subject: Re: MFA - Possible Solution?
> 
>  
> 
> Hi Richard,
> 
>  
> 
> I am currently working on an implementation for U2F  with Apache Shiro. It is still not officially
> 
> rolled out but it works and I am also not sure if I did it correct.
> 
>  
> 
> Like you I created two realms. Then however I changed the authentication strategy in the 
> 
> security manager.
> 
>  
> 
> authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
> 
> securityManager.authenticator.authenticationStrategy = $authcStrategy
> 
> securityManager.realms = $jdbcRealm, $u2fRealm
> 
>  
> 
> I used U2FAuthenticationToken that extends UsernamePasswordToken 
> 
> that works on both realms. The custom U2FRealm overrides supports and checks that the token
> 
> is an instance of U2FAuthenticationToken.
> 
>  
> 
>  
> 
> On 6 Jan 2017, at 19:27, Richard Wheeldon <richard.wheeldon@voxsmart.com <ma...@voxsmart.com>> wrote:
> 
>  
> 
> Hi,
> 
>  
> 
> As a few of you may know, I posted many, many months ago about trying to get Multi Factor Authentication working on a Shiro-based app. I think I have a plan that isn’t totally crazy and doesn’t involve stupid levels of custom code or changing any of the core Shiro libs. However, I’d like it run it by you guys to see what you think and poke holes in it before I turn it into prod-ready code. Maybe there’s something here that folks can adapt in the future if they need to do something similar.
> 
>  
> 
> I started off with a pre-existing config with a DefaultWebSessionManager and a JDBC realm reading username / password and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it’s fairly bog-standard stuff.
> 
>  
> 
> What I’m doing now is to split the JDBC logic into two realms. The first is the same password stuff:
> 
>  
> 
> passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
> 
> passwordJdbcRealm.permissionsLookupEnabled = true
> 
> passwordJdbcRealm.authenticationQuery = select password from users where …
> 
> passwordJdbcRealm.userRolesQuery = select …
> 
> passwordJdbcRealm.dataSource = $dataSource
> 
> passwordJdbcRealm.credentialsMatcher = $passwordMatcher
> 
> passwordJdbcRealm.permissionsQuery = select …
> 
>  
> 
> The second is for accessing one-time authentication tokens:
> 
>  
> 
> tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
> 
> tokenJdbcRealm.permissionsLookupEnabled = false
> 
> tokenJdbcRealm.authenticationQuery = select token from auth_tokens where …
> 
> tokenJdbcRealm.userRolesQuery = select null where ? is not null and false (must be a neater way just to disable role lookups but this works)
> 
> tokenJdbcRealm.dataSource = $dataSource
> 
> tokenJdbcRealm.credentialsMatcher = $tokenMatcher
> 
>  
> 
> Both of these are now bound to the same old security manager:
> 
>  
> 
> securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm
> 
>  
> 
> I’ve now also got two form filters - one for the password and a new one for the one-time token:
> 
> passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
> 
> passwordFormAuth.loginUrl = /login.jsp
> 
>  
> 
> tokenFormAuth = com.voxsmart….TokenFormAuthenticationFilter
> 
> tokenFormAuth.loginUrl = /login2.jsp
> 
>  
> 
> login2.jsp redirects to login.jsp if the user isn’t authenticated.
> 
>  
> 
> These form filters are wired up like so:
> 
>  
> 
> /login.jsp = passwordFormAuth
> 
> /login2.jsp = tokenFormAuth
> 
> /logout = logout
> 
> /** = tokenFormAuth
> 
>  
> 
> The first is basic except for sending out a token if the login succeeds:
> 
> @Override
> 
> public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
> 
>                                 // Generate a one-time token, write it to the DB and send it to the user via SMS / e-mail / carrier pigeon
> 
> }
> 
>     
> 
> The second checks that an MFA attribute is set on the session in order to allow access and sets it if / only if the token match is found:
> 
>  
> 
> private boolean isMfaOk(Object mfaStatus) {
> 
>                                return "ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaStatus);
> 
>                 }
> 
>  
> 
> @Override
> 
> public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
> 
> Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
> 
> return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional check for MFA
> 
> (!isLoginRequest(request, response) && isPermissive(mappedValue)); // Copy-n-paste from AuthenticatingFilter
> 
>                 }
> 
>  
> 
> @Override
> 
>                 public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
> 
> boolean ret = super.onLoginSuccess(token, subject, request, response);
> 
> subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
> 
> // write some audit log entries and other fluff
> 
> return ret;
> 
> }    
> 
>  
> 
> Does this seem sane? Is there a better way to do it? Is there something I’m missing or forgotten that will cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to make it easier in the future?
> 
>  
> 
> Regards,
> 
>  
> 
> Richard
> 
>  
> 
> 


Re: MFA - Possible Solution?

Posted by Brian Demers <br...@gmail.com>.
This is a great thread, thanks Björn and Richard!

These two approaches are very similar. The AllSuccessfulStrategy (or a
custom one) would be important here if you want to ensure both realms have
been queried, but I'm assuming this will fail the initial
authentication request due to the 2nd factor not being present (Björn, it
sounds like you have something working, so please correct me if i'm wrong).

Checking to see if the 2nd factor was checked 'isMfaOk()' could also be an
Authorization check (a permission check of 'user:2factor', or something)
Your 2nd realm would still need to verify the AuthC for the token.  But
your application's resources could require the 2nd factor or not by using:
subject.checkPermission("user:2factor"), or equivalent annotation.



On Mon, Jan 9, 2017 at 6:40 AM, Richard Wheeldon <
richard.wheeldon@voxsmart.com> wrote:

> So I’m guessing in your example the auths are wired more like this:
>
> /password-login = passwordFormAuth
>
> /token-login = passwordFormAuth, tokenFormAuth
>
> /** = passwordFormAuth, tokenFormAuth
>
>
>
> Is that right? If so, it makes sense and is probably neater that my
> solution. It might also give a point for wiring in IP address checking. e.g.
>
> /** = ipRangeAuth, passwordFormAuth, tokenFormAuth
>
>
>
> Slightly OT but here’s my take on SMS auth in general:
>
>    - It has a weakness because if you have the ability to reset the
>    password via e-mail and an end-user has inappropriately secured e-mail on
>    their phone (pretty common) then the possession of the phone becomes a
>    single factor for authentication. If you don’t allow password resets via
>    e-mail you wind up with another usability issue.
>    - I agree with your points on the reliability and 3rd party concerns.
>    - NIST have deprecated it largely because of the VOIP concerns. In our
>    scenario we can be almost certain that the number in question will
>    represent a physical mobile phone. This is not true for many use-cases so
>    there’s another potential weakness. However, imperfect SMS-based 2FA is
>    still better than nothing.
>    - It’s better for usability purely because a user already knows how to
>    receive a text message 99% of the time and they tend to carry phones around
>    with them already. Hence if you’ve got a lot of users who use the site /
>    app rarely it’s a simpler option.
>    - If you have a lot of users but access is rare, SMS is much cheaper
>    than hard-token. If you have a few users making regular access hard-token
>    is much cheaper than SMS.
>
>
>
> From a Shiro point of view:
>
>    - It’s largely irrelevant as the workflow for a soft-token like Google
>    authenticator would (AIUI) work in pretty much the same way.
>    - Using JdbcRealm for obtaining an SMS token is a bit dubious because
>    the realm only returns one token and it’s quite likely if there is an SMS
>    delay that the user will retry and we’re then left with two tokens
>    generated and the user may try either. It wasn’t something I thought of
>    when I wrote the original mail so I think I’ll have to have to consider
>    this carefully.
>
>
>
> Thanks for the U2F link and I’ll be sure to take a look,
>
>
>
> Richard
>
>
>
> *From:* Björn Raupach [mailto:raupach@me.com]
> *Sent:* Monday, January 9, 2017 7:20 AM
>
> *To:* user@shiro.apache.org
> *Subject:* Re: MFA - Possible Solution?
>
>
>
> Interesting. We were looking into SMS, too but I don’t find it user
> friendly at all. You rely on a third party. SMS often don’t arrive within
> seconds and there is an additional cost in every sms you send.
>
>
>
> I don’t get your difference in workflows. AllSuccessfulStrategy says all
> realms have to authenticate. Thats why we needed to create a
> AuthenticationToken that works on both realms. It has the username and
> password and the challenge from the token generator. First page collects
> username and password, second page creates token. Then submit them all. You
> can’t generate the token in advance because you need to find the matching
> user first.
>
>
>
> Have a look at U2F! It is a rather cheap hardware token generator. You can
> get one for less than 20 EUR. Use it on many sites and there are no
> additional license costs. Only downside is it works currently only Chrome
> and Firefox (with an extension).
>
>
>
> https://www.yubico.com/about/background/fido/
>
> http://www.howtogeek.com/232314/u2f-explained-how-
> google-microsoft-and-others-are-creating-universal-two-
> factor-authentication-tokens/
>
> https://fidoalliance.org/specifications/overview/
>
>
>
>
>
>
>
> On 9 Jan 2017, at 01:43, Richard Wheeldon <ri...@voxsmart.com>
> wrote:
>
>
>
> I think your idea seems equally reasonable. I considered something
> similar. However, we’re using SMS as the 2nd factor, which is imperfect
> from a security point of view but much better from a distribution and
> usability point of view. I’m guessing you have some sort of token generator
> for the 2nd factor. The key point being that there’s a difference in
> workflow:
>
>                 User supplies username + password -> System sends token to
> user -> User inputs token
>
> vs.
>
>                 User generates token -> User supplies username, password
> and token
>
>
>
> Richard
>
>
>
> *From:* Björn Raupach [mailto:raupach@me.com <ra...@me.com>]
> *Sent:* Sunday, January 8, 2017 12:02 PM
> *To:* user@shiro.apache.org
> *Subject:* Re: MFA - Possible Solution?
>
>
>
> Hi Richard,
>
>
>
> I am currently working on an implementation for U2F  with Apache Shiro. It
> is still not officially
>
> rolled out but it works and I am also not sure if I did it correct.
>
>
>
> Like you I created two realms. Then however I changed the authentication
> strategy in the
>
> security manager.
>
>
>
> authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
>
> securityManager.authenticator.authenticationStrategy = $authcStrategy
>
> securityManager.realms = $jdbcRealm, $u2fRealm
>
>
>
> I used U2FAuthenticationToken that extends UsernamePasswordToken
>
> that works on both realms. The custom U2FRealm overrides supports and
> checks that the token
>
> is an instance of U2FAuthenticationToken.
>
>
>
>
>
> On 6 Jan 2017, at 19:27, Richard Wheeldon <ri...@voxsmart.com>
> wrote:
>
>
>
> Hi,
>
>
>
> As a few of you may know, I posted many, many months ago about trying to
> get Multi Factor Authentication working on a Shiro-based app. I think I
> have a plan that isn’t totally crazy and doesn’t involve stupid levels of
> custom code or changing any of the core Shiro libs. However, I’d like it
> run it by you guys to see what you think and poke holes in it before I turn
> it into prod-ready code. Maybe there’s something here that folks can adapt
> in the future if they need to do something similar.
>
>
>
> I started off with a pre-existing config with a DefaultWebSessionManager
> and a JDBC realm reading username / password and role info from a PG DB.
> Apart from a non-standard session manager and a non-standard cookie name,
> it’s fairly bog-standard stuff.
>
>
>
> What I’m doing now is to split the JDBC logic into two realms. The first
> is the same password stuff:
>
>
>
> passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
>
> passwordJdbcRealm.permissionsLookupEnabled = true
>
> passwordJdbcRealm.authenticationQuery = select password from users where …
>
> passwordJdbcRealm.userRolesQuery = select …
>
> passwordJdbcRealm.dataSource = $dataSource
>
> passwordJdbcRealm.credentialsMatcher = $passwordMatcher
>
> passwordJdbcRealm.permissionsQuery = select …
>
>
>
> The second is for accessing one-time authentication tokens:
>
>
>
> tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
>
> tokenJdbcRealm.permissionsLookupEnabled = false
>
> tokenJdbcRealm.authenticationQuery = select token from auth_tokens where …
>
> tokenJdbcRealm.userRolesQuery = select null where ? is not null and false
> (must be a neater way just to disable role lookups but this works)
>
> tokenJdbcRealm.dataSource = $dataSource
>
> tokenJdbcRealm.credentialsMatcher = $tokenMatcher
>
>
>
> Both of these are now bound to the same old security manager:
>
>
>
> securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm
>
>
>
> I’ve now also got two form filters - one for the password and a new one
> for the one-time token:
>
> passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
>
> passwordFormAuth.loginUrl = /login.jsp
>
>
>
> tokenFormAuth = com.voxsmart….TokenFormAuthenticationFilter
>
> tokenFormAuth.loginUrl = /login2.jsp
>
>
>
> login2.jsp redirects to login.jsp if the user isn’t authenticated.
>
>
>
> These form filters are wired up like so:
>
>
>
> /login.jsp = passwordFormAuth
>
> /login2.jsp = tokenFormAuth
>
> /logout = logout
>
> /** = tokenFormAuth
>
>
>
> The first is basic except for sending out a token if the login succeeds:
>
> @Override
>
> public boolean onLoginSuccess(AuthenticationToken token, Subject subject,
> ServletRequest request, ServletResponse response) throws Exception {
>
>                                 // Generate a one-time token, write it to
> the DB and send it to the user via SMS / e-mail / carrier pigeon
>
> }
>
>
>
> The second checks that an MFA attribute is set on the session in order to
> allow access and sets it if / only if the token match is found:
>
>
>
> private boolean isMfaOk(Object mfaStatus) {
>
>                                return "ALLOWED".equals(mfaStatus) ||
> "NOT_NEEDED".equals(mfaStatus);
>
>                 }
>
>
>
> @Override
>
> public boolean isAccessAllowed(ServletRequest request, ServletResponse
> response, Object mappedValue) {
>
> Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
>
> return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional
> check for MFA
>
> (!isLoginRequest(request, response) && isPermissive(mappedValue)); //
> Copy-n-paste from AuthenticatingFilter
>
>                 }
>
>
>
> @Override
>
>                 public boolean onLoginSuccess(AuthenticationToken token,
> Subject subject, ServletRequest request, ServletResponse response) throws
> Exception {
>
> boolean ret = super.onLoginSuccess(token, subject, request, response);
>
> subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
>
> // write some audit log entries and other fluff
>
> return ret;
>
> }
>
>
>
> Does this seem sane? Is there a better way to do it? Is there something
> I’m missing or forgotten that will cause this to blow up in my face at some
> later point in time? Is there something that could be cribbed and added to
> Shiro to make it easier in the future?
>
>
>
> Regards,
>
>
>
> Richard
>
>
>

RE: MFA - Possible Solution?

Posted by Richard Wheeldon <ri...@voxsmart.com>.
So I’m guessing in your example the auths are wired more like this:
/password-login = passwordFormAuth
/token-login = passwordFormAuth, tokenFormAuth
/** = passwordFormAuth, tokenFormAuth

Is that right? If so, it makes sense and is probably neater that my solution. It might also give a point for wiring in IP address checking. e.g.
/** = ipRangeAuth, passwordFormAuth, tokenFormAuth

Slightly OT but here’s my take on SMS auth in general:

  *   It has a weakness because if you have the ability to reset the password via e-mail and an end-user has inappropriately secured e-mail on their phone (pretty common) then the possession of the phone becomes a single factor for authentication. If you don’t allow password resets via e-mail you wind up with another usability issue.
  *   I agree with your points on the reliability and 3rd party concerns.
  *   NIST have deprecated it largely because of the VOIP concerns. In our scenario we can be almost certain that the number in question will represent a physical mobile phone. This is not true for many use-cases so there’s another potential weakness. However, imperfect SMS-based 2FA is still better than nothing.
  *   It’s better for usability purely because a user already knows how to receive a text message 99% of the time and they tend to carry phones around with them already. Hence if you’ve got a lot of users who use the site / app rarely it’s a simpler option.
  *   If you have a lot of users but access is rare, SMS is much cheaper than hard-token. If you have a few users making regular access hard-token is much cheaper than SMS.

From a Shiro point of view:

  *   It’s largely irrelevant as the workflow for a soft-token like Google authenticator would (AIUI) work in pretty much the same way.
  *   Using JdbcRealm for obtaining an SMS token is a bit dubious because the realm only returns one token and it’s quite likely if there is an SMS delay that the user will retry and we’re then left with two tokens generated and the user may try either. It wasn’t something I thought of when I wrote the original mail so I think I’ll have to have to consider this carefully.

Thanks for the U2F link and I’ll be sure to take a look,

Richard

From: Björn Raupach [mailto:raupach@me.com]
Sent: Monday, January 9, 2017 7:20 AM
To: user@shiro.apache.org
Subject: Re: MFA - Possible Solution?

Interesting. We were looking into SMS, too but I don’t find it user friendly at all. You rely on a third party. SMS often don’t arrive within seconds and there is an additional cost in every sms you send.

I don’t get your difference in workflows. AllSuccessfulStrategy says all realms have to authenticate. Thats why we needed to create a AuthenticationToken that works on both realms. It has the username and password and the challenge from the token generator. First page collects username and password, second page creates token. Then submit them all. You can’t generate the token in advance because you need to find the matching user first.

Have a look at U2F! It is a rather cheap hardware token generator. You can get one for less than 20 EUR. Use it on many sites and there are no additional license costs. Only downside is it works currently only Chrome and Firefox (with an extension).

https://www.yubico.com/about/background/fido/
http://www.howtogeek.com/232314/u2f-explained-how-google-microsoft-and-others-are-creating-universal-two-factor-authentication-tokens/
https://fidoalliance.org/specifications/overview/



On 9 Jan 2017, at 01:43, Richard Wheeldon <ri...@voxsmart.com>> wrote:

I think your idea seems equally reasonable. I considered something similar. However, we’re using SMS as the 2nd factor, which is imperfect from a security point of view but much better from a distribution and usability point of view. I’m guessing you have some sort of token generator for the 2nd factor. The key point being that there’s a difference in workflow:
                User supplies username + password -> System sends token to user -> User inputs token
vs.
                User generates token -> User supplies username, password and token

Richard

From: Björn Raupach [mailto:raupach@me.com]
Sent: Sunday, January 8, 2017 12:02 PM
To: user@shiro.apache.org<ma...@shiro.apache.org>
Subject: Re: MFA - Possible Solution?

Hi Richard,

I am currently working on an implementation for U2F  with Apache Shiro. It is still not officially
rolled out but it works and I am also not sure if I did it correct.

Like you I created two realms. Then however I changed the authentication strategy in the
security manager.

authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
securityManager.realms = $jdbcRealm, $u2fRealm

I used U2FAuthenticationToken that extends UsernamePasswordToken
that works on both realms. The custom U2FRealm overrides supports and checks that the token
is an instance of U2FAuthenticationToken.


On 6 Jan 2017, at 19:27, Richard Wheeldon <ri...@voxsmart.com>> wrote:

Hi,

As a few of you may know, I posted many, many months ago about trying to get Multi Factor Authentication working on a Shiro-based app. I think I have a plan that isn’t totally crazy and doesn’t involve stupid levels of custom code or changing any of the core Shiro libs. However, I’d like it run it by you guys to see what you think and poke holes in it before I turn it into prod-ready code. Maybe there’s something here that folks can adapt in the future if they need to do something similar.

I started off with a pre-existing config with a DefaultWebSessionManager and a JDBC realm reading username / password and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it’s fairly bog-standard stuff.

What I’m doing now is to split the JDBC logic into two realms. The first is the same password stuff:

passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
passwordJdbcRealm.permissionsLookupEnabled = true
passwordJdbcRealm.authenticationQuery = select password from users where …
passwordJdbcRealm.userRolesQuery = select …
passwordJdbcRealm.dataSource = $dataSource
passwordJdbcRealm.credentialsMatcher = $passwordMatcher
passwordJdbcRealm.permissionsQuery = select …

The second is for accessing one-time authentication tokens:

tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
tokenJdbcRealm.permissionsLookupEnabled = false
tokenJdbcRealm.authenticationQuery = select token from auth_tokens where …
tokenJdbcRealm.userRolesQuery = select null where ? is not null and false (must be a neater way just to disable role lookups but this works)
tokenJdbcRealm.dataSource = $dataSource
tokenJdbcRealm.credentialsMatcher = $tokenMatcher

Both of these are now bound to the same old security manager:

securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm

I’ve now also got two form filters - one for the password and a new one for the one-time token:
passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
passwordFormAuth.loginUrl = /login.jsp

tokenFormAuth = com.voxsmart….TokenFormAuthenticationFilter
tokenFormAuth.loginUrl = /login2.jsp

login2.jsp redirects to login.jsp if the user isn’t authenticated.

These form filters are wired up like so:

/login.jsp = passwordFormAuth
/login2.jsp = tokenFormAuth
/logout = logout
/** = tokenFormAuth

The first is basic except for sending out a token if the login succeeds:
@Override
public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
                                // Generate a one-time token, write it to the DB and send it to the user via SMS / e-mail / carrier pigeon
}

The second checks that an MFA attribute is set on the session in order to allow access and sets it if / only if the token match is found:

private boolean isMfaOk(Object mfaStatus) {
                               return "ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaStatus);
                }

@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional check for MFA
(!isLoginRequest(request, response) && isPermissive(mappedValue)); // Copy-n-paste from AuthenticatingFilter
                }

@Override
                public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
boolean ret = super.onLoginSuccess(token, subject, request, response);
subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
// write some audit log entries and other fluff
return ret;
}

Does this seem sane? Is there a better way to do it? Is there something I’m missing or forgotten that will cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to make it easier in the future?

Regards,

Richard


Re: MFA - Possible Solution?

Posted by Björn Raupach <ra...@me.com>.
Interesting. We were looking into SMS, too but I don’t find it user friendly at all. You rely on a third party. SMS often don’t arrive within seconds and there is an additional cost in every sms you send.

I don’t get your difference in workflows. AllSuccessfulStrategy says all realms have to authenticate. Thats why we needed to create a AuthenticationToken that works on both realms. It has the username and password and the challenge from the token generator. First page collects username and password, second page creates token. Then submit them all. You can’t generate the token in advance because you need to find the matching user first.

Have a look at U2F! It is a rather cheap hardware token generator. You can get one for less than 20 EUR. Use it on many sites and there are no additional license costs. Only downside is it works currently only Chrome and Firefox (with an extension).

https://www.yubico.com/about/background/fido/ <https://www.yubico.com/about/background/fido/>
http://www.howtogeek.com/232314/u2f-explained-how-google-microsoft-and-others-are-creating-universal-two-factor-authentication-tokens/ <http://www.howtogeek.com/232314/u2f-explained-how-google-microsoft-and-others-are-creating-universal-two-factor-authentication-tokens/>
https://fidoalliance.org/specifications/overview/ <https://fidoalliance.org/specifications/overview/>



> On 9 Jan 2017, at 01:43, Richard Wheeldon <ri...@voxsmart.com> wrote:
> 
> I think your idea seems equally reasonable. I considered something similar. However, we’re using SMS as the 2nd factor, which is imperfect from a security point of view but much better from a distribution and usability point of view. I’m guessing you have some sort of token generator for the 2nd factor. The key point being that there’s a difference in workflow:
>                 User supplies username + password -> System sends token to user -> User inputs token
> vs.
>                 User generates token -> User supplies username, password and token
>  
> Richard
>  
> From: Björn Raupach [mailto:raupach@me.com] 
> Sent: Sunday, January 8, 2017 12:02 PM
> To: user@shiro.apache.org
> Subject: Re: MFA - Possible Solution?
>  
> Hi Richard,
>  
> I am currently working on an implementation for U2F  with Apache Shiro. It is still not officially
> rolled out but it works and I am also not sure if I did it correct.
>  
> Like you I created two realms. Then however I changed the authentication strategy in the 
> security manager.
>  
> authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
> securityManager.authenticator.authenticationStrategy = $authcStrategy
> securityManager.realms = $jdbcRealm, $u2fRealm
>  
> I used U2FAuthenticationToken that extends UsernamePasswordToken 
> that works on both realms. The custom U2FRealm overrides supports and checks that the token
> is an instance of U2FAuthenticationToken.
>  
>  
> On 6 Jan 2017, at 19:27, Richard Wheeldon <richard.wheeldon@voxsmart.com <ma...@voxsmart.com>> wrote:
>  
> Hi,
>  
> As a few of you may know, I posted many, many months ago about trying to get Multi Factor Authentication working on a Shiro-based app. I think I have a plan that isn’t totally crazy and doesn’t involve stupid levels of custom code or changing any of the core Shiro libs. However, I’d like it run it by you guys to see what you think and poke holes in it before I turn it into prod-ready code. Maybe there’s something here that folks can adapt in the future if they need to do something similar.
>  
> I started off with a pre-existing config with a DefaultWebSessionManager and a JDBC realm reading username / password and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it’s fairly bog-standard stuff.
>  
> What I’m doing now is to split the JDBC logic into two realms. The first is the same password stuff:
>  
> passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
> passwordJdbcRealm.permissionsLookupEnabled = true
> passwordJdbcRealm.authenticationQuery = select password from users where …
> passwordJdbcRealm.userRolesQuery = select …
> passwordJdbcRealm.dataSource = $dataSource
> passwordJdbcRealm.credentialsMatcher = $passwordMatcher
> passwordJdbcRealm.permissionsQuery = select …
>  
> The second is for accessing one-time authentication tokens:
>  
> tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
> tokenJdbcRealm.permissionsLookupEnabled = false
> tokenJdbcRealm.authenticationQuery = select token from auth_tokens where …
> tokenJdbcRealm.userRolesQuery = select null where ? is not null and false (must be a neater way just to disable role lookups but this works)
> tokenJdbcRealm.dataSource = $dataSource
> tokenJdbcRealm.credentialsMatcher = $tokenMatcher
>  
> Both of these are now bound to the same old security manager:
>  
> securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm
>  
> I’ve now also got two form filters - one for the password and a new one for the one-time token:
> passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
> passwordFormAuth.loginUrl = /login.jsp
>  
> tokenFormAuth = com.voxsmart….TokenFormAuthenticationFilter
> tokenFormAuth.loginUrl = /login2.jsp
>  
> login2.jsp redirects to login.jsp if the user isn’t authenticated.
>  
> These form filters are wired up like so:
>  
> /login.jsp = passwordFormAuth
> /login2.jsp = tokenFormAuth
> /logout = logout
> /** = tokenFormAuth
>  
> The first is basic except for sending out a token if the login succeeds:
> @Override
> public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
>                                 // Generate a one-time token, write it to the DB and send it to the user via SMS / e-mail / carrier pigeon
> }
>     
> The second checks that an MFA attribute is set on the session in order to allow access and sets it if / only if the token match is found:
>  
> private boolean isMfaOk(Object mfaStatus) {
>                                return "ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaStatus);
>                 }
>  
> @Override
> public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
> Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
> return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional check for MFA
> (!isLoginRequest(request, response) && isPermissive(mappedValue)); // Copy-n-paste from AuthenticatingFilter
>                 }
>  
> @Override
>                 public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
> boolean ret = super.onLoginSuccess(token, subject, request, response);
> subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
> // write some audit log entries and other fluff
> return ret;
> }    
>  
> Does this seem sane? Is there a better way to do it? Is there something I’m missing or forgotten that will cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to make it easier in the future?
>  
> Regards,
>  
> Richard


RE: MFA - Possible Solution?

Posted by Richard Wheeldon <ri...@voxsmart.com>.
I think your idea seems equally reasonable. I considered something similar. However, we’re using SMS as the 2nd factor, which is imperfect from a security point of view but much better from a distribution and usability point of view. I’m guessing you have some sort of token generator for the 2nd factor. The key point being that there’s a difference in workflow:
                User supplies username + password -> System sends token to user -> User inputs token
vs.
                User generates token -> User supplies username, password and token

Richard

From: Björn Raupach [mailto:raupach@me.com]
Sent: Sunday, January 8, 2017 12:02 PM
To: user@shiro.apache.org
Subject: Re: MFA - Possible Solution?

Hi Richard,

I am currently working on an implementation for U2F  with Apache Shiro. It is still not officially
rolled out but it works and I am also not sure if I did it correct.

Like you I created two realms. Then however I changed the authentication strategy in the
security manager.

authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
securityManager.realms = $jdbcRealm, $u2fRealm

I used U2FAuthenticationToken that extends UsernamePasswordToken
that works on both realms. The custom U2FRealm overrides supports and checks that the token
is an instance of U2FAuthenticationToken.


On 6 Jan 2017, at 19:27, Richard Wheeldon <ri...@voxsmart.com>> wrote:

Hi,

As a few of you may know, I posted many, many months ago about trying to get Multi Factor Authentication working on a Shiro-based app. I think I have a plan that isn’t totally crazy and doesn’t involve stupid levels of custom code or changing any of the core Shiro libs. However, I’d like it run it by you guys to see what you think and poke holes in it before I turn it into prod-ready code. Maybe there’s something here that folks can adapt in the future if they need to do something similar.

I started off with a pre-existing config with a DefaultWebSessionManager and a JDBC realm reading username / password and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it’s fairly bog-standard stuff.

What I’m doing now is to split the JDBC logic into two realms. The first is the same password stuff:

passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
passwordJdbcRealm.permissionsLookupEnabled = true
passwordJdbcRealm.authenticationQuery = select password from users where …
passwordJdbcRealm.userRolesQuery = select …
passwordJdbcRealm.dataSource = $dataSource
passwordJdbcRealm.credentialsMatcher = $passwordMatcher
passwordJdbcRealm.permissionsQuery = select …

The second is for accessing one-time authentication tokens:

tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
tokenJdbcRealm.permissionsLookupEnabled = false
tokenJdbcRealm.authenticationQuery = select token from auth_tokens where …
tokenJdbcRealm.userRolesQuery = select null where ? is not null and false (must be a neater way just to disable role lookups but this works)
tokenJdbcRealm.dataSource = $dataSource
tokenJdbcRealm.credentialsMatcher = $tokenMatcher

Both of these are now bound to the same old security manager:

securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm

I’ve now also got two form filters - one for the password and a new one for the one-time token:
passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
passwordFormAuth.loginUrl = /login.jsp

tokenFormAuth = com.voxsmart….TokenFormAuthenticationFilter
tokenFormAuth.loginUrl = /login2.jsp

login2.jsp redirects to login.jsp if the user isn’t authenticated.

These form filters are wired up like so:

/login.jsp = passwordFormAuth
/login2.jsp = tokenFormAuth
/logout = logout
/** = tokenFormAuth

The first is basic except for sending out a token if the login succeeds:
@Override
public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
                                // Generate a one-time token, write it to the DB and send it to the user via SMS / e-mail / carrier pigeon
}

The second checks that an MFA attribute is set on the session in order to allow access and sets it if / only if the token match is found:

private boolean isMfaOk(Object mfaStatus) {
                               return "ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaStatus);
                }

@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional check for MFA
(!isLoginRequest(request, response) && isPermissive(mappedValue)); // Copy-n-paste from AuthenticatingFilter
                }

@Override
                public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
boolean ret = super.onLoginSuccess(token, subject, request, response);
subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
// write some audit log entries and other fluff
return ret;
}

Does this seem sane? Is there a better way to do it? Is there something I’m missing or forgotten that will cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to make it easier in the future?

Regards,

Richard


Re: MFA - Possible Solution?

Posted by Björn Raupach <ra...@me.com>.
Hi Richard,

I am currently working on an implementation for U2F  with Apache Shiro. It is still not officially
rolled out but it works and I am also not sure if I did it correct.

Like you I created two realms. Then however I changed the authentication strategy in the 
security manager.

authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $authcStrategy
securityManager.realms = $jdbcRealm, $u2fRealm

I used U2FAuthenticationToken that extends UsernamePasswordToken 
that works on both realms. The custom U2FRealm overrides supports and checks that the token
is an instance of U2FAuthenticationToken.


> On 6 Jan 2017, at 19:27, Richard Wheeldon <ri...@voxsmart.com> wrote:
> 
> Hi,
>  
> As a few of you may know, I posted many, many months ago about trying to get Multi Factor Authentication working on a Shiro-based app. I think I have a plan that isn’t totally crazy and doesn’t involve stupid levels of custom code or changing any of the core Shiro libs. However, I’d like it run it by you guys to see what you think and poke holes in it before I turn it into prod-ready code. Maybe there’s something here that folks can adapt in the future if they need to do something similar.
>  
> I started off with a pre-existing config with a DefaultWebSessionManager and a JDBC realm reading username / password and role info from a PG DB. Apart from a non-standard session manager and a non-standard cookie name, it’s fairly bog-standard stuff.
>  
> What I’m doing now is to split the JDBC logic into two realms. The first is the same password stuff:
>  
> passwordJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
> passwordJdbcRealm.permissionsLookupEnabled = true
> passwordJdbcRealm.authenticationQuery = select password from users where …
> passwordJdbcRealm.userRolesQuery = select …
> passwordJdbcRealm.dataSource = $dataSource
> passwordJdbcRealm.credentialsMatcher = $passwordMatcher
> passwordJdbcRealm.permissionsQuery = select …
>  
> The second is for accessing one-time authentication tokens:
>  
> tokenJdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
> tokenJdbcRealm.permissionsLookupEnabled = false
> tokenJdbcRealm.authenticationQuery = select token from auth_tokens where …
> tokenJdbcRealm.userRolesQuery = select null where ? is not null and false (must be a neater way just to disable role lookups but this works)
> tokenJdbcRealm.dataSource = $dataSource
> tokenJdbcRealm.credentialsMatcher = $tokenMatcher
>  
> Both of these are now bound to the same old security manager:
>  
> securityManager.realms = $passwordJdbcRealm, $tokenJdbcRealm
>  
> I’ve now also got two form filters - one for the password and a new one for the one-time token:
> passwordFormAuth =com.voxsmart.PasswordFormAuthenticationFilter
> passwordFormAuth.loginUrl = /login.jsp
>  
> tokenFormAuth = com.voxsmart….TokenFormAuthenticationFilter
> tokenFormAuth.loginUrl = /login2.jsp
>  
> login2.jsp redirects to login.jsp if the user isn’t authenticated.
>  
> These form filters are wired up like so:
>  
> /login.jsp = passwordFormAuth
> /login2.jsp = tokenFormAuth
> /logout = logout
> /** = tokenFormAuth
>  
> The first is basic except for sending out a token if the login succeeds:
> @Override
> public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
>                                 // Generate a one-time token, write it to the DB and send it to the user via SMS / e-mail / carrier pigeon
> }
>     
> The second checks that an MFA attribute is set on the session in order to allow access and sets it if / only if the token match is found:
>  
> private boolean isMfaOk(Object mfaStatus) {
>                                return "ALLOWED".equals(mfaStatus) || "NOT_NEEDED".equals(mfaStatus);
>                 }
>  
> @Override
> public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
> Object mfaStatus = subject.getSession().getAttribute("MFA-STATUS");
> return (subject.isAuthenticated() && isMfaOk(mfaStatus)) || // Additional check for MFA
> (!isLoginRequest(request, response) && isPermissive(mappedValue)); // Copy-n-paste from AuthenticatingFilter
>                 }
>  
> @Override
>                 public boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
> boolean ret = super.onLoginSuccess(token, subject, request, response);
> subject.getSession().setAttribute("MFA-STATUS", "ALLOWED");
> // write some audit log entries and other fluff
> return ret;
> }    
>  
> Does this seem sane? Is there a better way to do it? Is there something I’m missing or forgotten that will cause this to blow up in my face at some later point in time? Is there something that could be cribbed and added to Shiro to make it easier in the future?
>  
> Regards,
>  
> Richard