You are viewing a plain text version of this content. The canonical link for it is here.
Posted to httpclient-users@hc.apache.org by Oleg Kalnichevski <ol...@apache.org> on 2008/09/16 19:44:18 UTC

Re: Potential account lockouts when using authentication using concurrent http requests

On Mon, 2008-09-15 at 12:53 -0700, Henrich Kraemer wrote:
> 
> Lets consider an application which uses credentials similar to a web
> browser:
> 1. Credentials previously accepted by a server are automatically reused for
> the same authentication scope.
> 2. If a user gets prompted for credentials they are only remembered after
> the server accepts them. The user can indicates to persist them beyond the
> current session.
> 3. Credentials which are not accepted by the server are invalidated and no
> longer reused. They are removed from session state and/or  the persisted
> credential store.
> 
> The application uses
> - HttpClient 3.
> - Multiple threads to execute HttpClient requests. All threads use a single
> (global) HttpClient instance with a MultiThreadedHttpConnectionManager.
> - a CredentialProvider to provide credentials.
> 
> Consider a server which locks out users after receiving a bad password for
> a particular user more than 3 consecutive times.
> Now if the application uses 4 or more threads to access this server a user
> can get locked out:
> 
> Scenario 1)  when the password has changed but HttpClient.state or the
> CredentialProvider use old credentials. All threads send the old
> credentials before noticing that they are no longer valid. I believe this
> can happen even if the CredentialProvider provided them only once as other
> threads will use the state of HttpState (I am looking at HttpClient 3.0
> code, but I believe this hasn't changed, looking at release notes)
> (see org.apache.commons.httpclient.HttpMethodDirector.promptForCredentials
> (AuthScheme, HttpParams, AuthScope))
> 
> Scenario 2) The user provides bad credentials but a correct username. The
> provided credentials are stored in the HttpState associated with the
> HttpClient instance. The other threads send the bad credentials as well
> again picking them up via the HttpClient.state field. This would require a
> good amount of bad luck, but I believe it might happen.
> 
> I would think a solution would involve that concurrent requests requiring
> the credentials for the same authentication scope would have to wait for
> the server response before sending these credentials again (or refraining
> from that and instead prompting again). This could be implemented by the
> CredentialProvider. However the HttpState used in HttpClient 3 seems to
> prevent this be without issues. On the other hand it might be useful for
> many apps so perhaps it should be implemented by the framework and apps
> responsibility would be limited to prompt users and store credentials.
> 

Hi Henrich,

This is an inherent design issue. When requests are executed
concurrently from multiple threads there is always a chance that enough
requests get submitted in order to get the account locked before the
application stands a change to react to the first authentication
failure. The only way around this problem is serializing / synchronizing
execution of requests. If you can live with occasional account lockouts,
then you can execute a single request in order to validate the
credentials and then proceed with executing more requests concurrently
from multiple threads. 

> Is there a way for an application as sketched out to let HttpClient 3
> handle the authentication but avoid the lock out problem?
> How would this be done using HttpClient 4?
> 
>
> An issue for an application which would implement this is to determine
> whether provided credentials have been accepted by the server or not. In
> HttpClient 3 one can assume that if the CredentialProvider is called that
> any previously provided credentials were wrong. However the app can only
> notice that it was accepted by waiting for HttpClient.executeMethod() to
> return. However one would want to know as soon as possible to increase
> concurrency. It seems that one would have to switch off redirection
> handling.

Unfortunately CredentialProvider stuff in HttpClient 3.x is
fundamentally flawed. The idea of requesting new credentials in case of
an authentication failure from inside the main execution loop was
ill-conceived. 


> Has HttpClient 4 mechanism by which an application can determine as soon as
> possible whether credentials used were accepted or rejected?
> 

When using HttpClient 4.0 one is advised to do the following:

* populate the credentials store with the default credentials if
available
* execute the request
* if the request fails with status code 401 or 407, prompt the user for
new credentials
* update the credentials store according to the user input
* retry

One can also use the same logic with HttpClient 3.x.

Hope this helps

Oleg

> Thanks,
> 
> Henrich


---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Potential account lockouts when using authentication using concurrent http requests

Posted by Henrich Kraemer <he...@us.ibm.com>.
Oleg,

There was a typo. I meant to ask whether "Therefore the SET/clear methods
would not be strictly needed, right?"

But I believe I know the answer.

Thanks,

Henrich



                                                                           
             Oleg Kalnichevski                                             
             <olegk@apache.org                                             
             >                                                          To 
                                       HttpClient User Discussion          
             09/16/2008 02:45          <ht...@hc.apache.org>    
             PM                                                         cc 
                                                                           
                                                                   Subject 
             Please respond to         Re: Potential account lockouts when 
             "HttpClient User          using authentication  using         
                Discussion"            concurrent http requests            
             <httpclient-users                                             
              @hc.apache.org>                                              
                                                                           
                                                                           
                                                                           
                                                                           




On Tue, 2008-09-16 at 14:24 -0700, Henrich Kraemer wrote:
> Hi Oleg,
>
> I assume with credentials store you talks about the abstraction provided
by
> the CredentialsProvider interface which allows to set or get a credential
> as well as to clear them.

Correct.

> I believe you are saying in HttpClient 4 managing the credential store is
> entirely the responsibility of the application.
> Therefore the get/clear methods would not be strictly needed, right?
>

The getter is used by HttpClient internally to obtain credentials for a
particular scope. #clear() method is not strictly needed


> See also my follow up questions below.
>
> Thanks much,
>
> Henrich
>
>
> > When using HttpClient 4.0 one is advised to do the following:
> >
> > * populate the credentials store with the default credentials if
> > available
> > * execute the request
> > * if the request fails with status code 401 or 407, prompt the user for
> > new credentials
> > * update the credentials store according to the user input
> > * retry
>
> I looked at the ClientInteractiveAuthentication example. Here are some
> excerpts
>         boolean trying = true;
>         while (trying) {
> ..
>             HttpResponse response = httpclient.execute(httpget,
> localContext);
> ..
>             HttpEntity entity = response.getEntity();
>             if (entity != null) {
>                 entity.consumeContent();
>             }
>
>             int sc = response.getStatusLine().getStatusCode();
>
>             AuthState authState = null;
>             if (sc == HttpStatus.SC_UNAUTHORIZED) {
>                 // Target host authentication required
>                 authState = (AuthState) localContext.getAttribute
> (ClientContext.TARGET_AUTH_STATE);
>             }
>             if (sc == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
>                 // Proxy authentication required
>                 authState = (AuthState) localContext.getAttribute
> (ClientContext.PROXY_AUTH_STATE);
>             }
>
>             if (authState != null) {
> ..
>                 AuthScope authScope = authState.getAuthScope();
> ..
>                 System.out.print("Enter username: ");
>                 String user = console.readLine();
>                 System.out.print("Enter password: ");
>                 String password = console.readLine();
>
>                 if (user != null && user.length() > 0) {
>                     Credentials creds = new UsernamePasswordCredentials
> (user, password);
>                     httpclient.getCredentialsProvider().setCredentials
> (authScope, creds);
>                     trying = true;
>                 } else {
>                     trying = false;
>                 }
>             } else {
>                 trying = false;
>             }
>         }
>
> I would think that some state needs to be carried over when forming the
> response (for digest scheme)
> Is this done via the HttpContext (localContext) which is passed into each
> execute() call?
>

Yes, it is. You may want to take a look at
ClientPreemptiveDigestAuthentication for an example of how local context
can be used to maintain state information between request invocations.

Hope this helps

Oleg


> >
> > One can also use the same logic with HttpClient 3.x.
> >
> > Hope this helps
> >
> > Oleg
> >
> > > Thanks,
> > >
> > > Henrich
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
> > For additional commands, e-mail: httpclient-users-help@hc.apache.org
> >


---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Potential account lockouts when using authentication using concurrent http requests

Posted by Oleg Kalnichevski <ol...@apache.org>.
On Tue, 2008-09-16 at 14:24 -0700, Henrich Kraemer wrote:
> Hi Oleg,
> 
> I assume with credentials store you talks about the abstraction provided by
> the CredentialsProvider interface which allows to set or get a credential
> as well as to clear them.

Correct.

> I believe you are saying in HttpClient 4 managing the credential store is
> entirely the responsibility of the application.
> Therefore the get/clear methods would not be strictly needed, right?
> 

The getter is used by HttpClient internally to obtain credentials for a
particular scope. #clear() method is not strictly needed


> See also my follow up questions below.
> 
> Thanks much,
> 
> Henrich
> 
> 
> > When using HttpClient 4.0 one is advised to do the following:
> >
> > * populate the credentials store with the default credentials if
> > available
> > * execute the request
> > * if the request fails with status code 401 or 407, prompt the user for
> > new credentials
> > * update the credentials store according to the user input
> > * retry
> 
> I looked at the ClientInteractiveAuthentication example. Here are some
> excerpts
>         boolean trying = true;
>         while (trying) {
> ..
>             HttpResponse response = httpclient.execute(httpget,
> localContext);
> ..
>             HttpEntity entity = response.getEntity();
>             if (entity != null) {
>                 entity.consumeContent();
>             }
> 
>             int sc = response.getStatusLine().getStatusCode();
> 
>             AuthState authState = null;
>             if (sc == HttpStatus.SC_UNAUTHORIZED) {
>                 // Target host authentication required
>                 authState = (AuthState) localContext.getAttribute
> (ClientContext.TARGET_AUTH_STATE);
>             }
>             if (sc == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
>                 // Proxy authentication required
>                 authState = (AuthState) localContext.getAttribute
> (ClientContext.PROXY_AUTH_STATE);
>             }
> 
>             if (authState != null) {
> ..
>                 AuthScope authScope = authState.getAuthScope();
> ..
>                 System.out.print("Enter username: ");
>                 String user = console.readLine();
>                 System.out.print("Enter password: ");
>                 String password = console.readLine();
> 
>                 if (user != null && user.length() > 0) {
>                     Credentials creds = new UsernamePasswordCredentials
> (user, password);
>                     httpclient.getCredentialsProvider().setCredentials
> (authScope, creds);
>                     trying = true;
>                 } else {
>                     trying = false;
>                 }
>             } else {
>                 trying = false;
>             }
>         }
> 
> I would think that some state needs to be carried over when forming the
> response (for digest scheme)
> Is this done via the HttpContext (localContext) which is passed into each
> execute() call?
> 

Yes, it is. You may want to take a look at
ClientPreemptiveDigestAuthentication for an example of how local context
can be used to maintain state information between request invocations.

Hope this helps

Oleg


> >
> > One can also use the same logic with HttpClient 3.x.
> >
> > Hope this helps
> >
> > Oleg
> >
> > > Thanks,
> > >
> > > Henrich
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
> > For additional commands, e-mail: httpclient-users-help@hc.apache.org
> >


---------------------------------------------------------------------
To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
For additional commands, e-mail: httpclient-users-help@hc.apache.org


Re: Potential account lockouts when using authentication using concurrent http requests

Posted by Henrich Kraemer <he...@us.ibm.com>.
Hi Oleg,

I assume with credentials store you talks about the abstraction provided by
the CredentialsProvider interface which allows to set or get a credential
as well as to clear them.
I believe you are saying in HttpClient 4 managing the credential store is
entirely the responsibility of the application.
Therefore the get/clear methods would not be strictly needed, right?

See also my follow up questions below.

Thanks much,

Henrich


> When using HttpClient 4.0 one is advised to do the following:
>
> * populate the credentials store with the default credentials if
> available
> * execute the request
> * if the request fails with status code 401 or 407, prompt the user for
> new credentials
> * update the credentials store according to the user input
> * retry

I looked at the ClientInteractiveAuthentication example. Here are some
excerpts
        boolean trying = true;
        while (trying) {
..
            HttpResponse response = httpclient.execute(httpget,
localContext);
..
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                entity.consumeContent();
            }

            int sc = response.getStatusLine().getStatusCode();

            AuthState authState = null;
            if (sc == HttpStatus.SC_UNAUTHORIZED) {
                // Target host authentication required
                authState = (AuthState) localContext.getAttribute
(ClientContext.TARGET_AUTH_STATE);
            }
            if (sc == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
                // Proxy authentication required
                authState = (AuthState) localContext.getAttribute
(ClientContext.PROXY_AUTH_STATE);
            }

            if (authState != null) {
..
                AuthScope authScope = authState.getAuthScope();
..
                System.out.print("Enter username: ");
                String user = console.readLine();
                System.out.print("Enter password: ");
                String password = console.readLine();

                if (user != null && user.length() > 0) {
                    Credentials creds = new UsernamePasswordCredentials
(user, password);
                    httpclient.getCredentialsProvider().setCredentials
(authScope, creds);
                    trying = true;
                } else {
                    trying = false;
                }
            } else {
                trying = false;
            }
        }

I would think that some state needs to be carried over when forming the
response (for digest scheme)
Is this done via the HttpContext (localContext) which is passed into each
execute() call?

>
> One can also use the same logic with HttpClient 3.x.
>
> Hope this helps
>
> Oleg
>
> > Thanks,
> >
> > Henrich
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: httpclient-users-unsubscribe@hc.apache.org
> For additional commands, e-mail: httpclient-users-help@hc.apache.org
>