Re: Welcome to the new JSR-375 mailing list


Will Hopkins
 

Hi Arjan,

On 05/15/2017 07:18 AM, Arjan Tijms wrote:
Hi,

On Mon, May 15, 2017 at 7:36 AM, Will Hopkins <will.hopkins@...> wrote:

Can you explain more about that? What does Custom Form do that's not already available (apart from leveraging IdentityStore), and what benefits does the alignment bring for apps?

With the existing form authentication mechanism, an application can start the authentication dialog by calling HttpServletRequest#authenticate, but it can't continue it in this way after having collected the credentials.

Custom form provides the full form algorithm / process such as specified by the Servlet spec, with the small but essential difference that  the "command" to continue the authentication dialog after the mechanism calls out to the application can be done programmatically (via Java code) instead of posting back to the virtual URL /j_security_check with post parameters j_username and j_password.

This is important because in modern Java EE applications you often want to bind those credentials to a CDI bean, then use existing bean validation to validate some basics (password not null, password long enough, etc) and render an error message via JSF if anything was wrong.

There's a test/example of that in the RI:
[...]
Then the CDI bean has bean validation annotations applied:

@Named
@RequestScoped
public class LoginBacking {

    @NotNull
    @Size(min = 3, max = 15, message="Username must be between 3 and 15 characters")
    private String username;
    
    @NotNull
    @Size(min = 5, max = 50, message="Password must be between 5 and 50 characters")
    private String password;

    // ...
}
[...]
This modern approach to Java EE programming is not possible with the existing form mechanism, where posting back to /j_security_check is often seen as old and clunky and certainly not capable of easily leveraging existing code in CDI, EL, BeanValidation and JSF.

I agree that the existing mechanisms aren't modern, and that there may be use cases where developers want or need to modify the process. That said, I think the legitimate use cases are probably few and far between. Existing implementations of FORM and BASIC have been refined over the years, such that they are robust and handle most reasonable requirements.

The example you provide above demonstrates the capability, but doesn't demonstrate the need -- in my view, intervening to check username/password length before actually validating the credentials is misguided on separation of duties grounds -- only the identity store can authoritatively say whether the credentials are valid or not -- and is potentially insecure. Best practice when validating credentials is to return "login failed" for all failure cases, regardless of the cause. Returning a different error for bad password vs. invalid user -- or for username/password too short -- reveals information that an attacker could use to break into a system. Timing differences between the two code paths could also reveal information helpful to an attacker.

I have not heard feedback from our customers that they need or want to extend the functionality provided by FORM or BASIC authentication in WebLogic.

Also -- is it not possible to reference a CDI bean, or perform validation on j_username/j_password in some other way, from a login form using the existing mechanism?

The regular form and basic authentication mechanisms are present not only for consistency reasons, but also make it both trivial and guaranteed to use the JSR 375 identity store with them, which wouldn't hold for the existing Servlet authentication mechanisms.

Support for IdentityStores could be achieved more simply and directly (from a container implementer's POV), by simply mandating that FORM and BASIC auth methods (perhaps CERT as well) use application-provided IdentityStores when present.

This is certainly an option, and the reverse has actually been proposed as well (but not acted upon) that the HAM will be able to use a container specific IdentityStore in some way (a bridge IdentityStore would be one way to achieve this).

My proposal would be specify that the container should use an application-supplied IdentityStore for FORM/BASIC if present, but also that the container is permitted to override an application-provided IdentityStore with one of its own. i spent some time reading the CDI spec, and it appears to me that this would be possible by observing events during bean discovery and replacing the application's bean with a container bean when the appropriate event is fired -- I'm still not sure which event is the most appropriate one, though -- I'd be interested in your thoughts, as you clearly know CDI better than I.

That however doesn't help with the consistency and CDI things.

Agreed, and I'm generally a fan of consistency. In this case, the question in my mind is whether consistency alone is a good enough reason to require containers to provide this functionality when it would impose an implementation cost on container vendors and there is no clear functional requirement driving it. Vendors could always choose to provide the functionality, or even re-implement their existing FORM/BASIC support as HAMs, if it were present in the RI.

You could see it the other way around though; a full container does not necessarily have to implement those two mechanisms twice. From the point of the spec the JSR 375 provided FORM is NOT required to be implemented fully, it just says this:

/**
 * Annotation used to define a container {@link AuthenticationMechanism} that implements
 * FORM authentication mechanism as defined by the Servlet spec and make that implementation
 * available as an enabled CDI bean.
 *
 */
@Retention(RUNTIME)
@Target(TYPE)
public @interface FormAuthenticationMechanismDefinition {
 
    @Nonbinding
    LoginToContinue loginToContinue();
    
}

The RI for convenience implements it from scratch, but full containers are only required to look at this annotation for the configuration of the existing (servlet) mechanism and expose a CDI bean that wraps or delegates to it.

Refactoring FORM/BASIC to expose it via CDI may or may not be a trivial exercise for any given container vendor. If significant refactoring is involved, vendors may choose to provide two implementations instead. The requirement to also support the custom form API might also be a factor.

Your concern is valid, but we took this into account earlier. I think we had discussions with Alex and perhaps David about this way in the beginning. That's why it says "as defined by the Servlet spec". A lot of thought went onto that seemingly simple fragment of text.

Good to know. Does he existing RI implementation meet that requirement?

Any given app is likely to have one or the other, not both, and custom schemes are, by definition, custom.

Here I meant from a practical EE product testing and implementation perspective. If a lot of tests that the implementors do for BASIC and FORM directly go to the Servlet mechanism, they don't test the CDI "wrapper/delegation" code paths. A separate custom mechanism would have to be added to the test suite and only that tests those paths.

As I've seen with JASPIC, this rarely happens, resulting in 4 long years of reporting and fixing various bugs.

Applications that use FORM via the CDI wrapper would also implicitly test that CDI wrapper, so when it comes time for someone to use a custom authentication mechanism, the chance that these paths have already been tested via the supplied mechanisms is a lot higher.

If a vendor chooses to provide two implementations of FORM/BASIC, that problem will still remain. Mandating functionality to drive testing seems like the tail wagging the dog; a focus on a robust TCK seems like a better approach.
 
The servlet container already provides default implementations of FORM and BASIC, just not ones that leverage HAM. Does JSF provide multiple implementations of each default UIComponent?

You mean if JSF provides a component already present in another spec?

In that regards, it's a full yes.

JSP and JSTL define a c:out tag, which is used to render output.

JSF defines an h:outputText tag, which does the same thing but more aligned with how the other tags (and associated components) work in JSF. Here too, especially when used on JSP, an implementation would not necessarily have to code up the same output code twice but could have the h:outputText tag as a front for the code in c:out.

I don't know these technologies well enough to have an informed opinion on whether h:outputText is duplicative of c:out, but it does seem that JSF is layered on JSP/JSTL in a way that, in my conception, JSR-375 is not layered on Servlet. Although JSR-375 and Servlet are different specs, they are tightly integrated, rather than one being simply layered on top of the other.

I have some architectural concerns about the general approach of implementing HAMs for BASIC and FORM auth, and with RememberMe in particular.

It's possible that I don't fully understand the intent of RememberMe, but it looks to me like what it's really doing is session management, not authentication.

This is not entirely correct. What it does is effectively exchange credentials for a token, and the token is used to authenticate the caller at every request when no (valid) HTTP session is present.

This is currently defined in the spec as follows:

 * For the remember me function the credentials provided by the caller are exchanged for a token
 * which is send to the user as the value of a cookie, in a similar way to how the HTTP session ID is send.
 * It should be realized that this token effectively becomes the credential to establish the caller's
 * identity within the application and care should be taken to handle and store the token securely. E.g.
 * by using this feature with a secure transport (SSL/https), storing a strong hash instead of the actual
 * token, and implementing an expiration policy. 

 
The RememberMeIdentityStore isn't really an identity store at all, it's a cache of authenticated users.

Not necessarily. A simple implementation of the interface could be just a cache indeed, but a "real" IdentityStore could also just be effectively a cache.

How a RememberMeIdentityStore is implemented is up to the user. An implementation could say go to a local filesystem based DB if the token is not older than a certain amount of time, and force revalidation at a remote origin authentication server if it's older. Tokens could be automatically expired and/or revoked explicitly.

I still feel like RememberMe is not well-defined. If it's really a session management facility, then it's under-specified, it shouldn't be delegating to an IdentityStore, and there's no clear rationale for implementing a new session management capability in addition to what the container already provides. Existing container session management is almost certainly more robust and more secure than RememberMe. I would argue that long-lived sessions are a bad idea (as distinct from long-lived authentication tokens), but existing containers allow for configurable session lifetimes.

If it's really an authentication token service, then, again, it's underspecified. There are certainly use cases for long-lived tokens, but the specifics of the token matter. OAuth tokens are one example, and are well-specified in terms of token format and content, and the trust models that apply to various types of OAuth tokens and various use cases. Importantly, OAuth tokens use encryption and digital signatures to provide confidentiality, integrity, and trust in the issuer. None of that is specified here because no specific token type (or token protocol) is specified.

The best way to think about RememberMe is probably in terms of a generic mechanism for token issuance, and that seems to be the intent of RememberMeIdentityStore. But it's not fully "plumbed" -- since HAMs are supposed to interact only with IdentityStoreHandler, not IdentityStore, shouldn't IdentityStoreHandler expose generateLoginToken()? Also, the removeLoginToken() method is more consistent with a session model than a token issuance model -- issued tokens, especially long-lived ones, are typically not cached because there's no need to -- they can be cryptographically verified, and they contain all the information needed for user identification and authentication. Lastly, the RememberMe annotation provides very little configurability -- it only supports cookies (not, e.g., HTTP headers, or form fields), and even for cookies allows configuration only for cookie name and lifetime.

The FORM and BASIC annotations make sense to me -- I still have reservations about including them, but I understand the value proposition, and how they're intended to work. RememberMe doesn't seem sufficiently well thought out yet.

If we really wanted to specify session management, we could do so, but the servlet spec already says how servlet containers should do session management. Shouldn't we integrate with that existing capability, rather than building a redundant, and less capable, feature on top of the existing feature?

The Servlet spec does not have any specification for this kind of session management. The HTTP session is a very broad type of session that brings with it a lot of other things, but mostly memory consumption. That's why the HTTP session has to expire after a relatively short amount of time.

The remember me token is longer lived (by definition, basically) and has no associated concept of consuming server resources. As mentioned above, an application can authenticate users using the token by going to a database or any other store.

The entire point of the token is to be able to live longer than the http session. If it would directly use the http session it would not even be needed (this is basically the key insight).

As noted above, that sounds more like a long-lived authentication token than a session. Having given it some more thought, I think RememberMe is not really session management or token-based authentication, it's token issuance, but I think it needs to be conceptualized as such, and more fully specified, before it's ready for inclusion in the spec.

A typical pipeline as defined by priorities in a CDI interceptor chain in JSR 375 is to do the following in order:

1. Check the HTTP session if the caller is authenticated, if so use this identity
2. If no details in the HTTP session, check if a remember me token is present, if so use this to authenticate the user
3. If no remember me token is present, start the lowest level authentication mechanism (e.g. FORM).

So with both HTTP sessions and RememberMe activated, the very first request would hit 3). The second and following requests within that HTTP session would hit 1).

Then when the HTTP session expires and the caller does a request again, it would hit 2), and then for all following requests within the new HTTP session it would hit 1) again.

I think a key conceptual difference between what happens in step 2 and what happens in step 3. Step two is the container identifying an authentication token and using it to authenticate the user. The function/behavior of step 3 depends on whether FORM is just an alternative authentication mechanism (because multiple acceptable mechanisms are available), or whether step 3 is really a redirect to a token issuance service. (Not necessarily a literal redirect, but a logical redirect.)

Do we really want two session cookies rather than one?

Yes, absolutely, since the expiration policy of the two cookies are totally different. One is longer lived and MUST NOT be removed when the browser closes, while the other is shorted lived and MUST be removed when the browser closes.

I think the answer is that one is a session cookie and the other is not (it's an authentication token). If the model is really token issuance, the fact that only cookies are supported seems like a bit limitation.

Have we specified it fully enough? How does RememberMe ensure that the cookies it issues are protected against session stealing attacks? Do we require that the cookie is marked secure? If so, how does it work if an authenticated user makes a request over a non-SSL connection?

These are good questions and the answer is that it's basically the same as for the JSESSIONID cookie. Servlet containers basically give the user 2 options for this as evidenced by the cookie-config:

<cookie-config>
    <http-only>true</http-only>
    <secure>true</secure>
</cookie-config>

In the JSR 375 case the spec warns for this case by suggesting that "secure" should be required "by using this feature with a secure transport (SSL/https", but it would probably be a good idea to default these two settings to be set to true. The only reason for secure not being set to true is localhost development, where you often don't have a secure connection to your dev server.

The cookie handler in the RI already sets http only to true btw:

public class CookieHandler {

public static void saveCookie(HttpServletRequest request, HttpServletResponse response, String name, String value, Integer maxAge) {
Cookie cookie = new Cookie(name, value);
if (maxAge != null) {
cookie.setMaxAge(maxAge);
}
cookie.setHttpOnly(true);
cookie.setPath(isEmpty(request.getContextPath())? "/" : request.getContextPath());

response.addCookie(cookie);
}

But I think it would be good to make the spec a bit more strict here, thanks for mentioning this concern!

Agreed, but an application with both http and https resources has more complicated requirements, given that you don't want users to have to authenticate twice, and given that the servlet spec mandates that the same session be shared across all of an application's resources, both http and https. The presense of multiple session cookies, representing sessions with different lifetimes complicates the situation even more, and potentially presents opportunities for session stealing when login events occur or tokens are refreshed/re-issued.

Similarly the FORM and BASIC implementations, if left in the API, should be required to integrate with container session management, so that behavior is uniform for apps that use HAM and apps that don't. Authentication is authentication; session management is different, and mostly (though not entirely) orthogonal.

In this case it's simply a layer underneath the existing session, like layers in a memory hierarchy. So as explained above it's http session -> token store -> original store (and mechanism).

 
In part where I'm coming from is that I see a difference between a framework that's mean to run in a wide variety of platforms, some of which may be Java EE containers, and some of which may not, and a JSR, which is explicitly meant to be *part* of the Java EE platform, and fully integrated with it -- not merely layered on top. I think we should be specifying where JSR-375 needs to integrate with the servlet container, not how it can duplicate or re-implement existing functionality.

I understand your concerns, but as explained above it's not duplicating or re-implementating functionality. By its very definition a "remember me" token must live outside the http session, but it only comes into play if the http session is not there anymore. Perhaps that's where the confusion comes from. If the http session is active, the mechanisms you mention work with the http session as they normally would.

Agree. I have a better understanding of the motivation for FORM and BASIC annotations/HAMs now. I think I understand RememberMe better as well, but I feel it still needs significant work before it's ready to be specified.

Those are my initial thoughts, anyway. I do see the benefits of the fact that HAMs are CDI beans, but, at a minimum, I think there's complexity here, and some questions that need to be resolved, and little or no time to do so. That's probably my fault, but we are where we are.

Thoughts?

We are where we are indeed. I hope to have answered most questions, and that we can amend the spec today where needed. Especially requiring secure and httponly to default to true would probably be a good thing to add to the spec.

Yes -- thanks for the explanations. I still don't think RememberMe is ready to specify, but I'm more comfortable with the FORM and BASIC annotations. Do you think you could send me a brief write-up of those today?

Thanks,

Will


Thanks for your thorough review!

Kind regards,
Arjan Tijms


 


Will

-- 
Will Hopkins | WebLogic Security Architect | +1.781.442.0310
Oracle Application Development
35 Network Drive, Burlington, MA 01803


-- 
Will Hopkins | WebLogic Security Architect | +1.781.442.0310
Oracle Application Development
35 Network Drive, Burlington, MA 01803

Join javaee-security-spec@javaee.groups.io to automatically receive all group messages.