users@javaee-security-spec.java.net

[javaee-security-spec users] [jsr375-experts] Re: Comments on Current Spec Content (take 3)

From: Will Hopkins <will.hopkins_at_oracle.com>
Date: Mon, 27 Feb 2017 17:00:43 -0500

Comments inline.

On 02/23/2017 10:04 AM, arjan tijms wrote:
> Hi Will,
>
> Some more replies to your comments inline below:
>
>
> On Fri, Feb 17, 2017 at 7:05 PM, Will Hopkins <will.hopkins_at_oracle.com
> <mailto:will.hopkins_at_oracle.com>> wrote:
>
>> * Is CallerPrincipal a sufficient credential for
>> IdentityStore.getGroupsByCallerPrincipal()? Can it be
>> safely used when there are multiple ID stores configured
>> (since the wrong groups could potentially be returned)?
>>
>>
>> This is a good question that we should explore further I think.
>> One the one hand, some proprietary solutions have complex
>> principals that contain much information to disambiguate callers.
>> This however does hurt the simple case when scaling down. In JSR
>> 375 one of the key drivers is that security should be simpler to
>> implement and comprehend by application developers.
>>
>> In especially smaller system, "Peter.Johnson_at_somedomain.com
>> <mailto:Peter.Johnson_at_somedomain.com>" is enough to full
>> distinguish the caller.
>>
>> Do we want to introduce extra attributes to allow for simple
>> named, distinguished names, etc? If they are optional, maybe. I
>> don't know yet.
>
> I'm not sure we can always expect names that are as unique as
> email addresses -- often, they are just usernames, like "joe" or
> "whopkins".
>
> In addition to the ambiguity problem -- which I think is
> significant -- I think there's also a problem in that the API
> would enable unprivileged code to query group memberships for
> arbitrary users -- all I need to do is construct a principal with
> the name I want to query, then invoke the interface. Is there
> anything that would prevent an application from doing that?
>
>
> Well first of all I'm a strong advocate of not having untrusted code
> running at all on the server.
>
> A great deal of the Java SE machinery around untrusted code was
> created for the situation where a single used downloaded a great deal
> of untrusted code (applets) and executed that on a local machine.
>
> In Java EE the reverse is typical; you have your own trusted code
> running and you want to protected that against many untrusted users.
>
> If you have untrusted code running in your server, you're already too
> late and it's almost a lost battle (IMHO).

I agree with you to a point, but I think that assumption is less and
less true as apps move into the cloud -- an environment where the owner
of the infrastructure doesn't necessarily know or trust the
owner/developer of the hosted app.

Also, as an advocate of defense-in-depth, I think we have to consider
security in everything we do, and attempt to secure things as best we
can, so that an exploit in one part of the system doesn't automatically
give access to all parts of the system.

> Using, again, JACC, but also a variety of proprietary security
> systems, it's often already possible to do this very query. And with
> Interceptors and Servlet Filters it's already possible to wrap many
> things, which additionally gives you access to many more things than
> just which groups a principal is in.

Not sure that's true -- it's possible to interrogate the current user,
but it's not possible, AFAIK, to query external user stores (LDAP/RDBMS)
about arbitrary user accounts. The systems I'm familiar with use
proprietary code that makes direct LDAP (or JDBC) calls to the external
server, and requires access to protected credentials in order to
bind/authenticate as a sufficiently privileged user to the external
system. There is currently no Java API I'm aware of that would let an
arbitrary caller construct a Principal with an arbitrary name, and use
that Principal to retrieve the user's groups from an external store.
(And, moreover, allow such code to construct a list of users that do and
don't exist in the store, based on whether groups, or an error, came
back from the query.) This would be a fairly serious information leak, IMO.

> That said, we could of course specify that this query is a privileged
> operation and if the Java SE security manager is active it should be
> use to make that call.

If we need that call -- and I do see its usefulness -- then I think
that's the approach we'd need to take.

> But again, I strongly belief in not running unprivileged code at all,
> and if absolutely needed, isolate it in a separate OS process or
> container and interact with it via e.g. rest calls.

I generally agree. Perhaps there's another way, that also addresses the
uniqueness problem -- could we specify that the API returned some sort
of unique and random identifier from each successful authentication,
that could be passed instead of (or in addition to), the Principal
argument? If the the identifier were essentially a transaction ID
(rather than a user ID), then: a) possession of the ID would demonstrate
prior possession of the user's authentication credential (password), and
therefore authorization to retrieve the user's groups; and, b) it could
serve to uniquely identify the user (through a map, or through encoding
a unique user ID into the transaction ID), so that the correct groups to
return could be unambiguously identified.

Such an approach would complicate implementation of IdentityStores that
aggregated disparate systems (due to the need to recognize each others'
transaction IDs), but would solve both the question of
privilege/authorization to access groups, and the need to be sure the
API returns the correct groups.

Thoughts?

> I had in mind something considerably simpler than what JSR 351 was
> considering -- a new AttributePrincipal type, or perhaps a
> Credential rather than a Principal. The trouble with using
> Principal is that, for attributes, we need both name and value,
> but Principal only defines Name. We'd need to extend the Principal
> interface for this use case -- or define a single, formatted
> String with an "=" in the middle, which the user would have to
> parse after calling getName(). The problem with using a
> non-Principal type and keeping it in one of the Credential sets is
> that credentials aren't serialized when a Subject is serialized.
>
>
>
> Well, in the not yet integrated authorization rules prototype I did
> kinda use something like this as well; see
> https://github.com/arjantijms/cdi-jacc-provider/blob/master/src/main/java/org/omnifaces/jaccprovider/jacc/Caller.java
>
> Maybe we could haul that over first and give it a general attribute
> map or so. I'm not sure yet, just thinking out loud,
>
> This is something we could consider, that might make IdentityStore
> more useful, but I don't have strong feelings about it, and we're
> probably better off keeping things simple.
>
>
> Indeed, if there once was a complete JSR for it the problem space is
> likely to be quite complex in general.

Seems better at this point to constrain scope, rather than add new features.

> Agree; JASPIC took another approach -- an application that
> registers an AuthConfig gets what it wants; the container can't
> legally override that.
>
>
>
> Although you can provide a SAM at the container level and the
> application does not replace the JASPIC Factory typically.
>
> It may be too late in the process now, but seeing how we could make
> such override legal could have a lot of value still.

Agree. It's one of the aspects of JASPIC that seems limiting to me; the
other being that only one AuthConfigProvider can be registered for a
context. A SaaS application that wanted to support multiple different
"tenants" (customers), might like to be able to specify a context like
/app/customer and get a different AuthConfigProvider for each customer
(one that, e.g., pointed at different external user stores for
authentication). It would be nice to be able to "slice" the context for
AuthConfigProvider more thinly, and/or specify multiple AuthConfigFactories.

>> I'd say for now, and with some reservation, that in the simple
>> case an identity store should contain only callers/users that can
>> be uniquely defined using the information contained within the
>> CallerPrincipal. In other words, "foo_at_bar.com
>> <mailto:foo_at_bar.com>" *must* be unique.
>>
>> If this is insufficient for a particular application, it may be a
>> matter of adding url like parameters to the name string, e.g.
>> "foo_at_bar.com?department=xyz&branch=abc
>> <http://foo@bar.com?department=xyz&branch=abc>". Interpretation
>> of such name is up to the identity store implementation and the
>> spec would not have to know about it.
>
> But if the spec didn't know, that implies non-public contracts
> between IdentityStores and HttpAuthenticationMechanisms -- a
> nightmare for interoperability.
>
>
> I'm not sure if there would be non-public contracts really needed
> here. The HttpAuthenticationMechanisms just passes a String to
> the IdentityStores. What it does with that is its concern.

But if the string passed by the HttpAuthenticationMechanism is just
"foo_at_bar.com", and it's passing the same string to each invocation of
the IdentityStoreHandler (and therefore to the underlying
IdentityStores), then there's really no further disambiguation even if
the IdentityStores are adding to the string. If the HAM is adding the
additional URL parameters, then it must somehow know that it needs to.

I'm assuming here that the user is not entering the additional
parameters when asked for username, and, further, that while email as
username is common in many environments (and generally works well as a
unique identifier), there are still many environments where usernames
less unique (e.g., "whopkins").
>
> 2) you only return groups to a caller who has presented proof of
> the right to access them (i.e., the login credential).
>
>
> It's not actually the caller who uses or sees the groups, but more the
> system operating internally.

Sure, the system should be allowed to look up groups, but the system
usually uses a low-level API like an LDAP library, and must authenticate
itself to the external system using bind credentials that are provided
through secure configuration. A public API that returns groups given
only a username (wrapped in a Principal) would have the effect of
publicly exposing the user store's contents to all users, including
unauthenticated users.

> If "joe" is unique within a system, and "joe" is authenticated, why
> would the internal container code not be allowed to query what groups
> or roles "joe" has? This is already what happens now in internal code
> anyway, and that internal code is often easier to access by
> application code than people likely think.

Agreed, re: easier to access than people think -- but not nearly as easy
as calling a public API with a username. A fair amount of reverse
engineering would likely be required in most cases. I'd like to operate
on the principle that we make things as secure as reasonably possible.

> If "joe" is not unique, then obviously the system architect or
> developer should not set up multiple stores relying on that.

Yes, but that may not always be possible, or an environment may be
initially set up that way, but changes to data/requirements over time
violate the constraint. Sometimes uniqueness of accounts is based on an
account ID that's different from the name a user uses to log in,
allowing for multiple users with the same name. Better if we could
safely support such environments with this new API.

> OK, makes sense. I wonder if it would be better to say that
> aggregation must be provided by an IdentityStore implementation,
> rather than the framework.
>
>
> It's technically the handler which does the aggregation, and this
> handler can be replaced. I was present at a JUG event where Rudy
> presented and took advantage of this option in one of his examples. It
> looked quite easy and still very powerful.

OK.

> We could probably define some additional flags similar to JAAS
> that would provide flexibility in how providers were aggregated.
>
>
> JAAS/PAM style flags were considered before by the EG, but honestly
> these are quite complex and hard to understand, and even if you do
> understand them well they don't allow you half of the options of
> Rudy's simple replaceable handler ;)

Makes sense. We could certainly define a simpler set, but I agree that
replacing the handler is simpler.

>> It's the same behaviour now, although there was a discussion not the
>> use the exact exit codes and constants for them, but use more
>> specific enums instead. These enums would then be mapped to the
>> existing JASPIC return codes, so they would still imply the same
>> behaviour.
>
> So we'll need to define these as part of the spec ...
>
> Yes indeed, this is both an open implementation and spec issue.

OK.

> Right. It would either need to maintain it's own context -- which
> the legacy calls delegated to -- or figure out what container it
> was in and delegate to that.
>
>
> Figure out the container and delegate to that is somewhat of a viable
> approach indeed. This is what I used for my own (now deprecated)
> OmniSecurity framework.

Yup.
>
> Could we not leave the exact mechanism up to the containers, as is
> currently the case for, e.g., HttpServletRequest.isUserInRole()?
> I'm reluctant to rely on JACC because, as you point out, it's not
> always present/enabled.
>
>
> I understand where you're coming from as WLS is indeed one of the
> servers that does not have it enabled (which btw I believe is a spec
> violation, just one that the TCK doesn't test for).
>
> Not having a JACC module/provider present should not be a big problem,
> as JSR 375 can deliver one as part of the spec.
>
> But I guess a compromise would be to *strongly recommend* that JACC is
> used for this (so Soteria will use it for that then), but that
> ultimately containers may decide to use their own mechanism for it.
>
> Would that work for you?

I'm willing to spec it that way, but I think as long as the semantic
contract is kept -- i.e., that the API can accurately tell you whether a
user is in a particular role or not -- that we don't need to care about
the implementation. I'd be happier with "you could use JACC" than "we
strongly recommend JACC" -- seems better, to me, if we can avoid any
explicit dependency on JACC -- but I could live with either approach.

>> In the ideal case BASIC, FORM, etc are implemented using SAMs I
>> think. That's a lower level interface more suited to plain
>> Servlet container than the HAM is, which is as mentioned
>> basically a higher level SAM.
>
> While I think JASPIC is a natural implementation for those
> mechanisms, I don't think mandating an implementation makes sense
> -- presumably, the externally-visible behavior would not change,
> so containers should be free to do what they like. I was just
> making sure I understood the implications of the statement.
>
>
> I think you understand it correctly ;)
>
> I think it makes practical sense to have those mechanisms being SAMS,
> so that there's 1 code path and not 2 for containers. Management
> interfaces would only have to query the JASPIC Factory for all
> mechanisms present, and in general the entire "two worlds" that JASPIC
> SAMs and Servlet standard authentication mechanisms now are would
> disappear.
>
> Whether we should focus on that now is another question ;)

As mentioned elsewhere, I think that, even in the case that JASPIC is
used to implement the default Servlet authentication mechanisms, there
are some ambiguities about what behavior should result when Servlet
config and JASPIC config specify different mechanisms. The rest is
internal implementation detail that's not externally visible, but I
agree that it's cleaner for containers to use JASPIC to implement the
default mechanisms.

> Was there a reason for specifying the ability to downcast? For
> container implementations I'm familiar with, that's automatically
> true, because the container simply returns the same principal that
> was created by the ATN mechanism. Have you had cases where doing
> this was necessary, but not possible?
>
>
> Yes certainly., In JBoss it happened quite often that the custom
> principal got lost. In GlassFish this too was (and I think still is)
> the case. Payara fixed it though in the GlassFish case.
>
> In general "not getting custom principal" is a topic I've seen many
> times on different forums and lists throughout the years. Containers
> are quite eager with wrapping the principals in their own principal
> wrappers, and this not rarely leaks when doing a
> getUser/CallerPrincipal call.

Good to know. I haven't seen that a lot in WebLogic, but I suspect
that's because WebLogic in general makes it fairly difficult (and
unnecessary) to use custom principals, so in practice they tend to
almost always be WLSUser and WLSGroup. WebLogic does hand back the same
principals that were originally put into the Subject, though.

> Even with that, I believe the language is too restrictive. AFAIK,
> there is no requirement for roles to be represented by groups,
> even after mapping. In the case of WebLogic (and assuming JACC is
> not in use), role mapping is done dynamically at runtime, and
> roles are distinct from groups. Roles are mapped, using a
> proprietary descriptor, to either user or group principals, and,
> at runtime, the existence of a mapped principal implies the role
> -- but the role is not, itself, a group, and is never represented
> in the Subject by a principal of any time.
>
>
> I agree with you there. Indeed, a role does not *have* to be
> represented by a group.
>
> In fact I wrote an article about this very topic last year:
> http://arjan-tijms.omnifaces.org/2016/07/simplified-custom-authorization-rules.html
>
> This uses the prototype authorization rules proposal, which itself
> uses JACC under the cover. So yes, JACC too can do the dynamic mapping
> or granting.
>
> The reason why I put that draft text there was to make the connection
> between those "things" being called "group" in the identity store and
> "role" in the application, and the connection between those.
>
> We should surely rewrite this text to make it less restrictive indeed.

I'll see what I can do.

Thanks for the detailed and thoughtful comments!

Will

>
> Kind regards,
> Arjan Tijms
>
>
>

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