How to use CookieAuthenticator?

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

How to use CookieAuthenticator?

Johanneke Lamberink
So I'm trying to use the CookieAuthenticator, but there are some things unclear to me. The documentation focuses on explaining how to do HTTP Basic or HTTP Digest, I haven't been able to find an example of HTTP Cookie anywhere, which is a shame :(

 I am using Restlet 2.1.2.

Question 1
------------
According to the documentation:
"public void challenge(Response response,
                      boolean stale)
This method should be overridden to return a login form representation.
By default, it redirects the user's browser to the getLoginFormPath() URI, adding the URI of the target resource as a query parameter of name getRedirectQueryName().
In case the getLoginFormPath() is not set, it calls the parent's method."

How do you override the implementation to return a representation? The return type is already void.

I now have a path in the router to a ServerResource with the uri of the login form that 'challenge' redirects to, which returns a Representation of a Form.
Is that what is meant? Then do I have to implement GET, POST, PUT and DELETE, or only POST, or what?
If not, how should I override 'challenge'?

Question 2
------------
And how do you handle this client side? I want to make my login form in the same style as the rest of the site. What is the flow when I return the form from the server side? My client is a javascript web application.

Question 3
------------
When a user is logged in, what do I send on subsequent requests? How do I handle this in the CookieAuthenticator? The authenticate method expects a cookie with a username and password, should I send that on every request? What if I want to use some sort of session security token? I know the server has no state, but I thought this is where the cookies came into play. I just have trouble understanding how exactly.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3059804
Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Pieter Martin
Hi,

Took me a while but I did get it working,

Here is what I did.

In my restlet application I set up the CookieAuthenticator as follows

public class CMRestletApplication extends Application {...

     @Override
     public Restlet createInboundRoot() {
         Router router = new Router(getContext());
         ...
         router.attach("/rest/modules/login/login.ftl",
LoginFormServerResource.class, Template.MODE_STARTS_WITH);
         ...
         CmCookieAuthenticator authenticator = new
CmCookieAuthenticator(getContext(), "My Realm", /*must be 16 bytes*/"My
Server KeyXXX".getBytes());
         authenticator.setLoginPath("/rest/special/loginPost");
authenticator.setLoginFormPath("/cm/rest/modules/login/login.ftl");
         authenticator.setRedirectQueryName("redirectUri");
         authenticator.setNext(router);

         MapVerifier mapVerifier = new MapVerifier();
         mapVerifier.getLocalSecrets().put("chunkylover53",
"pwd".toCharArray());
         authenticator.setVerifier(mapVerifier);
         ...
         router.attach(mode resources);
         ...
         return authenticator;
     }
}

The login form via freemarker

<form action="/cm/rest/special/loginPost?redirectUri=${redirectUri}"
method="POST">
     <input type="text" id="login" name="login" size="15"/>
     <input type="password" id="password" name="password" size="15"/>
     <input type="submit" value="Login"/>
</form>

The authenticator is a filter so I filter all urls that need authentication.

public class CmCookieAuthenticator extends CookieAuthenticator {...

     @Override
     protected int beforeHandle(Request request, Response response) {
         if
(request.getResourceRef().getRemainingPart().startsWith("/css") ||
request.getResourceRef().getRemainingPart().startsWith("/rest/modules/login")
|| request.getResourceRef().getRemainingPart().startsWith("/jquery")) {
             return CONTINUE;
         } else {
             int result = super.beforeHandle(request, response);
             if (response.getStatus()== Status.REDIRECTION_SEE_OTHER) {
                 return STOP;
             } else {
                 return result;
             }
         }
     }

Still need to unhardcode the verifier part, but cookie authentication
slipped of the priority bus for now.

As far as I understand there is nothing further to do from the client
side as the browser sends the cookie details are sent through with every
request. The cookie is encrypted so security seems ok.
I used the default key security as defined by Restlet.

Logout happens via the default /logout path as specified in the
CookieAuthenticator.

My menu has the following logout url.  <a
href="/cm/logout?redirectUri=cm/rest/modules/tools/dashboard.ftl">logout</a>

All this might not be exactly as intended but it works in my basic tests.

Hope that helps some.
Pieter


On 04/07/2013 19:18, Johanneke Lamberink wrote:

> So I'm trying to use the CookieAuthenticator, but there are some things unclear to me. The documentation focuses on explaining how to do HTTP Basic or HTTP Digest, I haven't been able to find an example of HTTP Cookie anywhere, which is a shame :(
>
>   I am using Restlet 2.1.2.
>
> Question 1
> ------------
> According to the documentation:
> "public void challenge(Response response,
>                        boolean stale)
> This method should be overridden to return a login form representation.
> By default, it redirects the user's browser to the getLoginFormPath() URI, adding the URI of the target resource as a query parameter of name getRedirectQueryName().
> In case the getLoginFormPath() is not set, it calls the parent's method."
>
> How do you override the implementation to return a representation? The return type is already void.
>
> I now have a path in the router to a ServerResource with the uri of the login form that 'challenge' redirects to, which returns a Representation of a Form.
> Is that what is meant? Then do I have to implement GET, POST, PUT and DELETE, or only POST, or what?
> If not, how should I override 'challenge'?
>
> Question 2
> ------------
> And how do you handle this client side? I want to make my login form in the same style as the rest of the site. What is the flow when I return the form from the server side? My client is a javascript web application.
>
> Question 3
> ------------
> When a user is logged in, what do I send on subsequent requests? How do I handle this in the CookieAuthenticator? The authenticate method expects a cookie with a username and password, should I send that on every request? What if I want to use some sort of session security token? I know the server has no state, but I thought this is where the cookies came into play. I just have trouble understanding how exactly.
>
> ------------------------------------------------------
> http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3059804
>
> ____________________________________________________________________________________
> Your personal email. Anytime, anywhere.
> Ridiculously affordable at $19.95. No contracts.
> http://www.getpeek.com/lavabit.html
> ____________________________________________________________________________________

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3059867
Reply | Threaded
Open this post in threaded view
|

RE: Re: How to use CookieAuthenticator?

Johanneke Lamberink
Ok, part of my confusion was because of my inexperience with cookies.

The current login flow of my application is like this:

Do a POST to the loginPath().
If the credentials are valid a cookie is set, and the resource of the loginPath() returns some additional information.
If the credentials are invalid, still continue to the next restlet, but set the authenticated flag to false and no cookie is set. The resource of the loginPath returns an error code. I know this is not how it is supposed to be done (should return 401), but this is what I needed for my client application. Now my client application can decide what to do.

After supplying valid credentials and successfully logging in, the browser will store the cookie and automatically send it with every future request (until the cookie expires, default at the end of the browser session).

Every other URI that is accessed without credentials returns a 401.

        @Override
        public void challenge(Response response, boolean stale) {
                if (isLoggingIn(response.getRequest(), response)) {
                        response.setStatus(Status.SUCCESS_OK);
                } else {
                        response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED);
                }
        }

        @Override
        protected int unauthenticated(Request request, Response response) {
                // Update the client info accordingly
                if (request.getClientInfo() != null) {
                        request.getClientInfo().setAuthenticated(false);
                }

                if (response.getStatus().equals(Status.SUCCESS_OK)) {
                        // Continue the filtering chain
                        return CONTINUE;
                } else {
                        // Stop the filtering chain
                        return STOP;
                }
        }

I'm still looking for ways to prevent session hijacking and session fixation, but for now I have working code :) On to enroling/authorizing...

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060168
Reply | Threaded
Open this post in threaded view
|

RE: Re: How to use CookieAuthenticator?

Johanneke Lamberink
So, as far as I can see, the Restlet CookieAuthenticator takes care of session fixation by generating a new value for the cookie on every request (encrypting the username/password/timestamp).

But what about session hijacking? If every cookie contains the username and password, someone who got hold of a cookie could use it to log in, right? So when my cookie gets stolen, it can still be used after I logged out and my browser deleted the cookie, because the attacker still has the cookie.

I have implemented a check on the time the cookie was issued, but an attacker could do periodic requests t make sure his stolen cookie never gets old.

How do I protect against this? Every blog and tutorial I find just tells me to put the session id in the session and discard it after logout. But I don't have a session to store to or discard from, because the server is stateless...

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060314
Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Pieter Martin
I would also like to know about this.

However is stealing a cookie not the same as getting access to the users
computer?

Cheers
Pieter

On 12/07/2013 09:53, Johanneke Lamberink wrote:

> So, as far as I can see, the Restlet CookieAuthenticator takes care of session fixation by generating a new value for the cookie on every request (encrypting the username/password/timestamp).
>
> But what about session hijacking? If every cookie contains the username and password, someone who got hold of a cookie could use it to log in, right? So when my cookie gets stolen, it can still be used after I logged out and my browser deleted the cookie, because the attacker still has the cookie.
>
> I have implemented a check on the time the cookie was issued, but an attacker could do periodic requests t make sure his stolen cookie never gets old.
>
> How do I protect against this? Every blog and tutorial I find just tells me to put the session id in the session and discard it after logout. But I don't have a session to store to or discard from, because the server is stateless...
>
> ------------------------------------------------------
> http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060314
>
> ____________________________________________________________________________________
> Save with Super Cheap Insurance and your premium reduces monthly!
> http://click.lavabit.com/s46kjm8zs9rqxeich1cbdc5unbm5o8qyhchpswgr4w9o9uemacfy/
> ____________________________________________________________________________________

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060317
Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Pieter Martin
The above is assuming that https is being used.

Thanks
Pieter

On 12/07/2013 10:32, Pieter Martin wrote:

> I would also like to know about this.
>
> However is stealing a cookie not the same as getting access to the users
> computer?
>
> Cheers
> Pieter
>
> On 12/07/2013 09:53, Johanneke Lamberink wrote:
>> So, as far as I can see, the Restlet CookieAuthenticator takes care of session fixation by generating a new value for the cookie on every request (encrypting the username/password/timestamp).
>>
>> But what about session hijacking? If every cookie contains the username and password, someone who got hold of a cookie could use it to log in, right? So when my cookie gets stolen, it can still be used after I logged out and my browser deleted the cookie, because the attacker still has the cookie.
>>
>> I have implemented a check on the time the cookie was issued, but an attacker could do periodic requests t make sure his stolen cookie never gets old.
>>
>> How do I protect against this? Every blog and tutorial I find just tells me to put the session id in the session and discard it after logout. But I don't have a session to store to or discard from, because the server is stateless...
>>
>> ------------------------------------------------------
>> http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060314
>>
>> ____________________________________________________________________________________
>> Save with Super Cheap Insurance and your premium reduces monthly!
>> http://click.lavabit.com/s46kjm8zs9rqxeich1cbdc5unbm5o8qyhchpswgr4w9o9uemacfy/
>> ____________________________________________________________________________________
> ------------------------------------------------------
> http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060317
>
> ____________________________________________________________________________________
> Your personal email. Anytime, anywhere.
> Ridiculously affordable at $19.95. No contracts.
> http://www.getpeek.com/lavabit.html
> ____________________________________________________________________________________

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060322
Reply | Threaded
Open this post in threaded view
|

RE: Re: How to use CookieAuthenticator?

Johanneke Lamberink
In reply to this post by Pieter Martin
Well, yes, I believe that when using HTTPS, stealing a cookie means access to the computer. But people let other people use their computer, or your laptop might get stolen, or you might forget to lock it when you go out for lunch.

Because of the premise that you need physical access to the computer to steal the cookie, you probably have bigger security problems. But I would still like to protect against this.

I've been thinking about including some "start-of-session-timestamp" in the cookie, that *wouldn't* get updated on each request, and have a timelimit on the age of that timestamp. That way, a stolen cookie could only be used a limited amount of time.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060325
Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Pieter Martin
Hi,

I have not looked at the code for a while but my understanding was that
the cookie is invalidated when the user logs out. So it can not be
reused after a logout.
Cookies are (optionally) set to expire after some time of no use.

Cheers
Pieter

On 12/07/2013 11:21, Johanneke Lamberink wrote:

> Well, yes, I believe that when using HTTPS, stealing a cookie means access to the computer. But people let other people use their computer, or your laptop might get stolen, or you might forget to lock it when you go out for lunch.
>
> Because of the premise that you need physical access to the computer to steal the cookie, you probably have bigger security problems. But I would still like to protect against this.
>
> I've been thinking about including some "start-of-session-timestamp" in the cookie, that *wouldn't* get updated on each request, and have a timelimit on the age of that timestamp. That way, a stolen cookie could only be used a limited amount of time.
>
> ------------------------------------------------------
> http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060325
>
> ____________________________________________________________________________________
> Your personal email. Anytime, anywhere.
> Ridiculously affordable at $19.95. No contracts.
> http://www.getpeek.com/lavabit.html
> ____________________________________________________________________________________

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060327
Reply | Threaded
Open this post in threaded view
|

RE: Re: How to use CookieAuthenticator?

Johanneke Lamberink
When a user requests a logout, the 'maxAge' of the cookie is set to 0, which will tell the browser to delete it.

However, when a cookie was stolen, this stolen cookie still exists, and can still be used to log in. After all, the cookie contains all the information needed for logging in, no additional information is needed at all.


But maybe I'm looking for a problem that doesn't exist?

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060328
Reply | Threaded
Open this post in threaded view
|

Re: Re: How to use CookieAuthenticator?

Tim Peierls
You can always have the Verifier treat cookies with creation times too far in the past as stale. A legitimate client will be able to provide the credentials again; an impostor with a stolen cookie won't.

--tim

On Fri, Jul 12, 2013 at 6:01 AM, Johanneke Lamberink <[hidden email]> wrote:
When a user requests a logout, the 'maxAge' of the cookie is set to 0, which will tell the browser to delete it.

However, when a cookie was stolen, this stolen cookie still exists, and can still be used to log in. After all, the cookie contains all the information needed for logging in, no additional information is needed at all.


But maybe I'm looking for a problem that doesn't exist?

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060328

Reply | Threaded
Open this post in threaded view
|

RE: Re: Re: How to use CookieAuthenticator?

Johanneke Lamberink
Thanks for the reply. Yes, treating old cookies as stale is what I do now. I was wondering if there is another way to prevent re-use of an old cookie.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060332
Reply | Threaded
Open this post in threaded view
|

Re: Re: Re: How to use CookieAuthenticator?

Tim Peierls
You can override the formatCredentials and parseCredentials methods to store custom information in the (encrypted) cookie value.

On Fri, Jul 12, 2013 at 9:12 AM, Johanneke Lamberink <[hidden email]> wrote:
Thanks for the reply. Yes, treating old cookies as stale is what I do now. I was wondering if there is another way to prevent re-use of an old cookie.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060332

Reply | Threaded
Open this post in threaded view
|

RE: Re: Re: Re: How to use CookieAuthenticator?

Johanneke Lamberink
But what would I put in there? Let's call it a sessionID, regardless of the actual content. The only way this will give the protection I need requires some sort of state on the server, doesn't it?

So I guess my question is, is it possible to protect against this specific attack while maintaining a stateless server? Or did we already forfeit that when we started setting cookies?

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060334
Reply | Threaded
Open this post in threaded view
|

Re: Re: Re: Re: How to use CookieAuthenticator?

Tim Peierls
On Fri, Jul 12, 2013 at 9:32 AM, Johanneke Lamberink <[hidden email]> wrote:
But what would I put in there? Let's call it a sessionID, regardless of the actual content. The only way this will give the protection I need requires some sort of state on the server, doesn't it?

You could store some information about the request that is likely to be true of subsequent legitimate requests by the same client but not of a forged one, like the client's IP address. That doesn't require state on the server. It's by no means perfect, but it would force attackers to work harder to spoof client requests.

 
So I guess my question is, is it possible to protect against this specific attack while maintaining a stateless server? Or did we already forfeit that when we started setting cookies?

Aren't these three levels of protection sufficient?
  • Difficulty of obtaining a valid cookie in the first place.
  • Difficulty of using a valid cookie before it expires.
  • Difficulty of spoofing client-specific info.
If an attacker is able to get to a client's cookie store, is not bothered by the expiration time limit, and can make requests with ClientInfo that matches the real client, then it's highly likely that the client is already deeply compromised, so keeping track of sessions on the server side doesn't really help: The attacker could just issue its requests in a valid session.

So my answer is no, I don't think you give up security in any significant way by having a stateless server. If it still bothers you, though, why not isolate a small stateful service exclusively to address authentication concerns, leaving the rest of your server stateless?

--tim

Reply | Threaded
Open this post in threaded view
|

RE: Re: Re: Re: Re: How to use CookieAuthenticator?

Johanneke Lamberink
Ah yes, something like the client's IP address would make sense. I will have to think on what information we would like to include, but at least now I know that what I want is indeed possible :)

And you are right, those 3 levels of protection should be sufficient, but I am still missing one of the levels in my implementation (client specific info). So I guess I will have to decide if I really need that, or that the other 2 levels are sufficient protection.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3060357
Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Ramesh Kumar J
In reply to this post by Pieter Martin
Pieter Martin <pietermartin <at> lavabit.com> writes:

>
> Hi,
>
> Took me a while but I did get it working,
>
> Here is what I did.
>
> In my restlet application I set up the CookieAuthenticator as follows
>
> public class CMRestletApplication extends Application {...
>


Hi Pieter Martin,

Thanks for the example.

I tried to follow it. I am stuck with the "/rest/special/loginPost" action.

Can you please post the code for the "loginPost" request ?

I have a
secretverifier implementation which is not called for cookie authenticator.

Thanks,
Ramesh

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3072851
Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Jerome Louvel-3
Hi all,

Based on this ongoing discussion, it would be clearly useful to include a CookieAuthenticator example in Restlet user guide. 

Added new issue. If anyone wants to contribute a Markdown page for this that would be great:

Also, I've entered a RFE to include the client IP address and reduce risk of replay attacks:

Best regards,
Jerome



2014-02-11 2:35 GMT-08:00 Ramesh Kumar <[hidden email]>:
Pieter Martin <pietermartin <at> lavabit.com> writes:

>
> Hi,
>
> Took me a while but I did get it working,
>
> Here is what I did.
>
> In my restlet application I set up the CookieAuthenticator as follows
>
> public class CMRestletApplication extends Application {...
>


Hi Pieter Martin,

Thanks for the example.

I tried to follow it. I am stuck with the "/rest/special/loginPost" action.

Can you please post the code for the "loginPost" request ?

I have a
secretverifier implementation which is not called for cookie authenticator.

Thanks,
Ramesh

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=3072851

Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Ramesh Kumar J
This post was updated on .
Thanks Jerome Louvel for your understanding and quick action.

I have been trying last 3 days to get it work. I tried many possible ways to get it work. But still I didn't success. I guess it would be very simple implementation in the loginPost server resouce. Can you please throw some clue to get it work ? I can contribute user guide once I can setup cookie authentication.


      @Post
        public Representation post(Representation details) {

                Form form = new Form(details);
                String username = form.getFirstValue("login");
                String password = form.getFirstValue("password");
                String targetURI = getQueryValue("targetURI");
                //What code should I add here ? validate and set cookie ???
               
       }


- Ramesh
Reply | Threaded
Open this post in threaded view
|

Re: How to use CookieAuthenticator?

Ramesh Kumar J
Hi all,

Finally I got it work. As I guessed, it is very simple to setup. The restlet is a great framework which is always easy to setup. We don't need to do anything for loginPath. It will be automatically taken care by CookieAuthenticator. I stuck to setup, because of a small usability/understanding issue. The LoginFormPath also trying for authentication. It should be skip by default. Because of this my browser became unresponsive because of infinite loop.

I suggest, the default implementation of beforeHandle() method of CookieAuthenticator might be as below,


   protected int beforeHandle(Request request, Response response) {
        if (request.getResourceRef().getRemainingPart().equals(getLoginFormPath())) {
                        return CONTINUE;
                }else {

        if (isLoggingIn(request, response)) {
            login(request, response);
        } else if (isLoggingOut(request, response)) {
            return logout(request, response);
        }
        return super.beforeHandle(request, response);
      }
    }



Below are the working code of my application:


1. PTCookieAuthenticator.java


     public class PTCookieAuthenticator extends CookieAuthenticator {

        public PTCookieAuthenticator(Context context) {
                super(context, "realm-text", "my_16_bytes_text".getBytes());
        }

        @Override
        protected int beforeHandle(Request request, Response response) {
                if (request.getResourceRef().getRemainingPart().startsWith("/loginForm")) {
                        return CONTINUE;
                } else {
                        return super.beforeHandle(request, response);
                }
        }
}
 

2. RestletService.java


public class RestletService extends Application {
        ...
         public Restlet createInboundRoot() {
         ....
                router.attach("/loginForm", LoginForm.class);
                PTCookieAuthenticator cookieAuth=new PTCookieAuthenticator(getContext());
                cookieAuth.setLoginFormPath("/loginForm");
         ....
         }



The Pieter Martin suggested every thing, but changing the loginPath("/rest/special/loginPost") made me confusion. I thought I should do something in the post method.

Thanks,
Ramesh