Rememberme vs authentication

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Rememberme vs authentication

kaosko
Shiro makes this artificial, strong separation between rememberme and
authentication. In the javadocs for RememberMeAuthenticationToken
(http://jsecurity.org/api/org/jsecurity/authc/RememberMeAuthenticationToken.html)
it is said that "Authentication is the process of proving you are who
you say you are". It is stated that rememberme is not considered an
authentication and implied that only using username/password can be
used for an "actual authentication". However, in practice there's no
way to know that whoever supplied the password is who they say they
are and passwords are generally not considered the strongest form of
authentication. Shiro's default rememberme implementation results in
no or very weak authentication, but there are alternatives that
results in stronger form of key-based authentication. Private/public
keys are often considered equally strong authentication to
username/password and for remember me, using rolling tokens (as
described for example at
http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/)
makes remembering the identity a much more secure process.

I've implemented rolling token-based remember cookies with Acegi in
the past, and now I'd like to do the same with Shiro. Considering the
view Shiro takes to rememberme, I wonder if it would make more sense
to implement a custom RememberMeManager or a custom authentication
filter for it. Current implementation doesn't allow you to authorize
the principal for anything when rememberMe is used, but I might like
to grant weaker roles to user that has been identified/authenticated
with a rolling token. I didn't find any built-in support for this type
of authentication so I'd also be interested in reviewing and possibly
reusing any existing code if somebody has already implemented
something like what I have in mind.

Kalle
Reply | Threaded
Open this post in threaded view
|

Re: Rememberme vs authentication

Les Hazlewood-2
Hi Kalle,

I think your comments are based on maybe previous Acegi use and there is possible confusion as to the differences between the two projects.

First and foremost, Shiro's rememberMe cookie does not store credentials (passwords, keys, etc).  It can never be used to find a user's password.  Only identity (principals), such as a username, is stored.  Premises #2 and #3 in your linked article on rolling tokens does not apply to Shiro.

Shiro makes this artificial, strong separation between rememberme and
authentication. In the javadocs for RememberMeAuthenticationToken
(http://jsecurity.org/api/org/jsecurity/authc/RememberMeAuthenticationToken.html)
it is said that "Authentication is the process of proving you are who
you say you are". It is stated that rememberme is not considered an
authentication and implied that only using username/password can be
used for an "actual authentication".

It is most certainly not artificial.  Authentication is the act of verifying who you are who you say you are by supplying credentials that verify your identity - the type of credentials (password, public/private key pair, etc) are irrelevant and up to what the system architect/administrator deems as appropriate.

When you are only remembered, you haven't authenticated anything.  By the very definition of authentication, there can be no confusion as to whether or not a simply 'remembered' user is authenticated or not.  Any framework that does not make this distinction is just plain wrong, and the amazon.com example shows a very real use case of why this is important.

Shiro makes no requirement that passwords are used to authenticate, and they have no relationship to rememberMe functionality at all - users can authenticate by whatever means they desire, such as public/private key pairs.  The RememberMeAuthenticationToken and its parent interface explicitly have no notions of usernames and/or passwords for this reason.
 
However, in practice there's no
way to know that whoever supplied the password is who they say they
are and passwords are generally not considered the strongest form of
authentication. Shiro's default rememberme implementation results in
no or very weak authentication,

The correct statement is "Shiro's default rememberMe implementation results in no authentication" - on purpose.  And again, Shiro's default rememberMe implementation makes no assumptions about whether or not passwords are good enough - the application configuration does. 

When you are remembered, by default, you are not automatically authenticated - and never should considered as such just by the very definition of the word.  Indeed, because Shiro does not retain credentials in rememberMe, it _can't_ automatically authenticate the remembered user (a good thing). 

The application developer decides what state is 'good enough' for his application by performing the appropriate checks, and because Shiro distinguisihes between the different states, the developer can do this easily.  It is not something cludged together as an afterthought in the framework.

That is,

currentSubject.isAuthenticated() == true:  provided correct credentials during the current session
currentSubject.getPrincipal() != null: provided correct credentials during the current session OR during a previous session and they are currently 'remembered' from a previous session
currentSubject.getPrincipal() == null: anonymous user - hasn't authenticated yet and is not rememebered.
 
but there are alternatives that
results in stronger form of key-based authentication. Private/public
keys are often considered equally strong authentication to
username/password and for remember me, using rolling tokens (as
described for example at
http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/)
makes remembering the identity a much more secure process.

I just read that article, and I fail to see how it is any better than Shiro's default approach.  Those cookies are valid for only a current session, just as Shiro's 'authenticated' state is only valid for a single session.  In fact, Premises #1, #2, and #3 are exactly why our implementation functions the way it does! :)  And because we don't store passwords in the rememberMe cookie, Premises #2 and #3 aren't even valid for Shiro.

Also, when a user logs out - the rememberMe cookie is invalidated.  The article's approach and Shiro's approach are _very_ similar.
 
I wonder if it would make more sense
to implement a custom RememberMeManager or a custom authentication
filter for it. Current implementation doesn't allow you to authorize
the principal for anything when rememberMe is used,

I'm confused by this statement.  If you are 'remembered' with Shiro, you can most certainly be authorized (remembered user X is allowed/not allowed to do action y).  Authorization is based solely on identity (which is retained by rememberMe) - not based on your authentication state.

Based on what I've read in that article, Shiro already does what you require.  I'm wondering if this is just confusion based on Acegi-related preconceptions?

Best,

Les
Reply | Threaded
Open this post in threaded view
|

Re: Rememberme vs authentication

kaosko
On Tue, Jun 16, 2009 at 6:50 AM, Les Hazlewood<[hidden email]> wrote:
> I think your comments are based on maybe previous Acegi use and there is
> possible confusion as to the differences between the two projects.
> First and foremost, Shiro's rememberMe cookie does not store credentials
> (passwords, keys, etc).  It can never be used to find a user's password.
> Only identity (principals), such as a username, is stored.  Premises #2 and
> #3 in your linked article on rolling tokens does not apply to Shiro.

Yes, not confused really, just asking for confirmation.

> It is most certainly not artificial.  Authentication is the act of verifying
> who you are who you say you are by supplying credentials that verify your
> identity - the type of credentials (password, public/private key pair, etc)
> are irrelevant and up to what the system architect/administrator deems as
> appropriate.

Sure, but it depends on your viewpoint. Shiro takes the approach that
remember should not supply any credentials, but I see it as a line
where different authentication/identifying mechanisms could supply
some credentials that vary in their strength.

>> http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/)
>> makes remembering the identity a much more secure process.
> I just read that article, and I fail to see how it is any better than
> Shiro's default approach.  Those cookies are valid for only a current
> session, just as Shiro's 'authenticated' state is only valid for a single
> session.  In fact, Premises #1, #2, and #3 are exactly why our
> implementation functions the way it does! :)  And because we don't store
> passwords in the rememberMe cookie, Premises #2 and #3 aren't even valid for
> Shiro.

Oh I think you need to re-read the article until you see the
difference and agree that its at least stronger form of identification
than Shiro's default approach :) It's stronger because the server
issues you a key that is good for one time access only. Compare this
to Shiro - you are only storing the identity on the client side and
the server didn't issue you any key to compare to. It is also stronger
because all of the rolling tokens can be invalidated on the server,
for example when you successfully use username/password authentication
or change a password on the server. Finally, it's stronger because the
server decides if the remembered identity is expired - not the client.

> Also, when a user logs out - the rememberMe cookie is invalidated.  The
> article's approach and Shiro's approach are _very_ similar.

I don't want to make this yes/no argument, but if you claim that I
don't think you fully understood the article.

>> I wonder if it would make more sense
>> to implement a custom RememberMeManager or a custom authentication
>> filter for it. Current implementation doesn't allow you to authorize
>> the principal for anything when rememberMe is used,
> I'm confused by this statement.  If you are 'remembered' with Shiro, you can
> most certainly be authorized (remembered user X is allowed/not allowed to do
> action y).  Authorization is based solely on identity (which is retained by
> rememberMe) - not based on your authentication state.

Ok, so it's clear that Shiro's rememberme is only to used for
remembering the identity. Let's forget about the rememberme for now
and I'll rephrase the problem: how do I best implement authentication
logic based on disposable server issued keys so that client doesn't
have to go through a particular authentication point?

> Based on what I've read in that article, Shiro already does what you
> require.  I'm wondering if this is just confusion based on Acegi-related
> preconceptions?

Certainly not. Acegi doesn't take any stand on how strong any
particular authentication/identification mechanism is - it's all left
up to the developer. Shiro instead implements custom logic for
rememberme, i.e. just for the "remember my identity only" case.

Kalle
Reply | Threaded
Open this post in threaded view
|

Re: Rememberme vs authentication

Les Hazlewood-2
> It is most certainly not artificial.  Authentication is the act of verifying
> who you are who you say you are by supplying credentials that verify your
> identity - the type of credentials (password, public/private key pair, etc)
> are irrelevant and up to what the system architect/administrator deems as
> appropriate.

Sure, but it depends on your viewpoint. Shiro takes the approach that
remember should not supply any credentials, but I see it as a line
where different authentication/identifying mechanisms could supply
some credentials that vary in their strength.

That's Shiro's _default_ assumption.  Any developer can change this if they want to ;)  Which is obviously what you're asking.

I just wanted to be very clear to most readers and users of the framework that auto-login - where the end-user is automatically logged in without their required interaction, is usually a really, really bad idea.  It is not something that should be done by default (and is why we don't do it by default). 

That being said, Shiro is definitely flexible and will allow you to override almost anything you want.  If one wants to shoot themselves in the foot, then they're free to do so (I'm not implying you're doing this, I'm just making a point that the framework is not inherently limited to only the case of "remember only my identity" - that's just the default approach for the same exact reasons your article points out and is sufficient for 95% of use cases).
 
>> http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/)
>> makes remembering the identity a much more secure process.
> I just read that article, and I fail to see how it is any better than
> Shiro's default approach.  Those cookies are valid for only a current
> session, just as Shiro's 'authenticated' state is only valid for a single
> session.  In fact, Premises #1, #2, and #3 are exactly why our
> implementation functions the way it does! :)  And because we don't store
> passwords in the rememberMe cookie, Premises #2 and #3 aren't even valid for
> Shiro.

Oh I think you need to re-read the article until you see the
difference and agree that its at least stronger form of identification
than Shiro's default approach :) It's stronger because the server
issues you a key that is good for one time access only. Compare this
to Shiro - you are only storing the identity on the client side and
the server didn't issue you any key to compare to. It is also stronger
because all of the rolling tokens can be invalidated on the server,
for example when you successfully use username/password authentication
or change a password on the server. Finally, it's stronger because the
server decides if the remembered identity is expired - not the client.

My point was this doesn't matter much if the programmer uses Shiro's API properly.  If the API is being used correctly, it would protect certain site areas depending on their state (anonymous, remembered, and/or authenticated).  This, ultimately, is the end-goal of the article to which you linked and what is important.  Yes, the random ID does give the server a little more control over the entire process, but it is mostly unnecessary when using the API correctly.

For example, the developer should do something like this:

if ( currentSubject.isAuthenticated() ) {
    spendUsersMoney();
}

and never this:

if ( currentSubject.getPrincipal() != null ) {
    spendUsersMoney();
}

There are JSP tags that execute the same logic based on these 3 states: anonymous, a user (either remembered or authenticated), and authenticated, respectively:

<shiro:guest/>
<shiro:user/>
<shiro:authenticated/>

There are tags that represent the inverse of these as well.

But if you disagree that this doesn't provide the same end-goal as what the article advocates, albeit in a different way, I'd certainly like to hear your thoughts on why - if there is something that can be added to the framework to make lives easier for people, it probably should be incorporated :)

Ok, so it's clear that Shiro's rememberme is only to used for
remembering the identity.

That's the default yes - but not limited to that.  The developer can do whatever he likes by implementing the RememberMeManager interface.  You're probably a super user in this regard and know exactly how you want things to work - its just I think people should ask themselves if the above approach is not usable before going down that road - most of the time it is not necessary and could even cause a security hole.  It is typically a bad thing to perform sensitive operations without explicitly requiring human interaction to verify identity, so we don't enable this by default on purpose.

Let's forget about the rememberme for now
and I'll rephrase the problem: how do I best implement authentication
logic based on disposable server issued keys so that client doesn't
have to go through a particular authentication point?

> Based on what I've read in that article, Shiro already does what you
> require.  I'm wondering if this is just confusion based on Acegi-related
> preconceptions?

Certainly not. Acegi doesn't take any stand on how strong any
particular authentication/identification mechanism is - it's all left
up to the developer.

Same with Shiro - we just provide a nice default that works assuming you use the API correctly.  The developer can certainly override this if they desire by providing their own RememberMeManager implementation.

Please let us know if you have any questions about your implementation or any other ideas that might make RememberMe better.  I for one am definitely open to suggestions!

Cheers,

Les

Reply | Threaded
Open this post in threaded view
|

Re: Rememberme vs authentication

kaosko
On Tue, Jun 16, 2009 at 1:38 PM, Les Hazlewood<[hidden email]> wrote:
> I just wanted to be very clear to most readers and users of the framework
> that auto-login - where the end-user is automatically logged in without
> their required interaction, is usually a really, really bad idea.  It is not
> something that should be done by default (and is why we don't do it by
> default).

Certainly, especially if you are given exactly the same permissions.

> My point was this doesn't matter much if the programmer uses Shiro's API
> properly.  If the API is being used correctly, it would protect certain site
> areas depending on their state (anonymous, remembered, and/or
.. snip ..
> <shiro:guest/>
> <shiro:user/>
> <shiro:authenticated/>
> There are tags that represent the inverse of these as well.

I hope JSP tags are a thing of past by now :)

> But if you disagree that this doesn't provide the same end-goal as what the
> article advocates, albeit in a different way, I'd certainly like to hear
> your thoughts on why - if there is something that can be added to the
> framework to make lives easier for people, it probably should be
> incorporated :)

Still not seeing the difference? Ok, let's take a real world example.
Bank of America's website remembers your username on that computer
(ip) only. If you move to a different location (say you suddenly
changed countries), it'll ask you a simple security question *before*
it gives you a chance to type in your password - so the "remember me"
works as an extra security feature rather than weaker form of
security. Obviously simply accepting the given client-side identity is
not enough. If you type in the wrong password, the implementation can
invalidate your remembered identity on the server. If somebody
intercepts your identity cookie it won't be valid anymore on the next
try. You can also better protect against systematic attacks since the
assigned key is unique (rather than the same encrypted identity) so
all keys can be invalidated at once.

>> Ok, so it's clear that Shiro's rememberme is only to used for
>> remembering the identity.
>
> That's the default yes - but not limited to that.  The developer can do
> whatever he likes by implementing the RememberMeManager interface.  You're
... snip ...
> Same with Shiro - we just provide a nice default that works assuming you use
> the API correctly.  The developer can certainly override this if they desire
> by providing their own RememberMeManager implementation.

Are you suggesting I'd do something like
SecurityUtils.getSubject().login() as part of
RememberMeManager.getRememberedPrincipals()? That doesn't sound like
the right approach to me. I mean I don't see how  RememberMeManager
interface can be used directly since it doesn't support
onLogin(PrincipalCollection subjectPrincipals) (because rememberme is
strictly limited to identity). Obviously I can hack Shiro anyway I
like (and believe me, I have) but don't you think that the design
dictates that in this case it'd be better just to implement a custom
SecurityManager that incorporates some aspects of RememberMeManager?
If not, how would you implement this using the RememberMeManager?

Kalle
Reply | Threaded
Open this post in threaded view
|

Re: Rememberme vs authentication

Les Hazlewood-2
Still not seeing the difference? Ok, let's take a real world example.
Bank of America's website remembers your username on that computer
(ip) only. If you move to a different location (say you suddenly
changed countries), it'll ask you a simple security question *before*
it gives you a chance to type in your password - so the "remember me"
works as an extra security feature rather than weaker form of
security. Obviously simply accepting the given client-side identity is
not enough. If you type in the wrong password, the implementation can
invalidate your remembered identity on the server. If somebody
intercepts your identity cookie it won't be valid anymore on the next
try. You can also better protect against systematic attacks since the
assigned key is unique (rather than the same encrypted identity) so
all keys can be invalidated at once.

Fair enough, although this does take quite a bit of effort to set up compared to the other approach, which I have to say I've used successfully on many commercial web sites with no ill-effect.  That doesn't mean the existing approach is the best it can be, it just means that, as is the case of so many things in the security world, that it hasn't been exploited yet on anything I've been involved with.  There is _always_ room for improvement in a security framework.

So, do you think 1) this paradigm could be easily incorporated into the framework in a 'hands off' approach where it would work out of the box with no required configuration and 2) if so, should it be the default implementation used at startup?

Are you suggesting I'd do something like
SecurityUtils.getSubject().login() as part of
RememberMeManager.getRememberedPrincipals()? That doesn't sound like
the right approach to me.

I completely agree and goes to what I was saying about an authenticated state vs a remembered state - I think an automatic login calls that are not manually initiated by a human is (usually) a bad thing.
 
I mean I don't see how  RememberMeManager
interface can be used directly since it doesn't support
onLogin(PrincipalCollection subjectPrincipals) (because rememberme is
strictly limited to identity). Obviously I can hack Shiro anyway I
like (and believe me, I have) but don't you think that the design
dictates that in this case it'd be better just to implement a custom
SecurityManager that incorporates some aspects of RememberMeManager?
If not, how would you implement this using the RememberMeManager?

If the onFailedLogin and onSuccessfulLogin methods are not suitable, I'd be more than happy to add a 'beforeLogin' method to the interface if you think that would be sufficient.  This would provide proper AOP-ish 'around advice' for an authentication attempt.

Interestingly enough, both the RememberMeManager and the AuthenticationListener are nearly identical.  I think if we add a 'beforeLogin' method it should go into the AuthenticationListener interface and the RememberMeManager should just extend that interface.  Anyone have any objections on this?

If you think this is a good approach and would allow you to do what you need, please open a Jira issue and I'd be more than happy to push it through.  If this won't allow you to do what you desire, please let me know why not and we can see how other mechanisms might be made available to do what you need.  At the end of the day, you have the ability to create Subject instances however you want via a custom implementation of the SubjectFactory interface (which is still in flux and might change to reflect a Map argument, much like the SessionFactory interface looks like now).

Also, if you have to do any more 'hacking' :) and things aren't readily/easily available for you to override custom behavior, by all means please continue to post feedback here.  I feel that's one of the most important things to achieve in the project (where possible of course and when the solutions are not rare edge-cases).

Cheers,

Les
Reply | Threaded
Open this post in threaded view
|

Re: Rememberme vs authentication

kaosko
On Wed, Jun 17, 2009 at 10:44 AM, Les Hazlewood<[hidden email]> wrote:

> Fair enough, although this does take quite a bit of effort to set up
> compared to the other approach, which I have to say I've used successfully
> on many commercial web sites with no ill-effect.  That doesn't mean the
> existing approach is the best it can be, it just means that, as is the case
> of so many things in the security world, that it hasn't been exploited yet
> on anything I've been involved with.  There is _always_ room for improvement
> in a security framework.
> So, do you think 1) this paradigm could be easily incorporated into the
> framework in a 'hands off' approach where it would work out of the box with
> no required configuration and 2) if so, should it be the default
> implementation used at startup?

No, I don't think it can be easily implemented in plain Shiro. The
reason is the same as to why Shiro's Realm API is read-only: since
Shiro cannot rely on any particular persistence model to exist, it's
difficult to implement any more complex server-side authentication,
identification or verification logic that would work out-of-the-box.
Since I'm not bound to these limitations (the security module I'm
working on requires JPA persistence) I can implement it fully as a
drop-in functionality - users of the library won't even need to know
how it works.

> If the onFailedLogin and onSuccessfulLogin methods are not suitable, I'd be
> more than happy to add a 'beforeLogin' method to the interface if you think
> that would be sufficient.  This would provide proper AOP-ish 'around advice'
> for an authentication attempt.
> Interestingly enough, both the RememberMeManager and the
> AuthenticationListener are nearly identical.  I think if we add a
> 'beforeLogin' method it should go into the AuthenticationListener interface
> and the RememberMeManager should just extend that interface.  Anyone have
> any objections on this?

Even adding onLogin won't help me since I want to do something after a
remembered identity was retrieved. But yes, I see a lot of custom
logic dealing specifically with rememberme when it mostly looks like
an authenticationListener (except for the added
getRememberedPrincipals). So I think making RememberMeManager extend
the AuthenticationListener is a good idea.

> If you think this is a good approach and would allow you to do what you
> need, please open a Jira issue and I'd be more than happy to push it
> through.  If this won't allow you to do what you desire, please let me know
> why not and we can see how other mechanisms might be made available to do
> what you need.  At the end of the day, you have the ability to create
> Subject instances however you want via a custom implementation of the
> SubjectFactory interface (which is still in flux and might change to reflect

I was going to write that I think my best short term approach is to
override the DefaultSecurityManager which I didn't particularly like
because it would have created a lock-in for users, but yes I see that
SubjectFactory is new and might be exactly the extension point I need.
I can use the default RememberMeManager exactly as intended and keep
the rolling tokens separate, then use a custom SubjectFactory that
will do the token reading and comparison and invalidate the remembered
identity as needed. Thanks for that - I've been using 0.9 javadocs so
I missed SubjectFactory but I'm looking at trunk source now.

Kalle