Closed jaredhanson closed 11 years ago
I take back my comment about the necessity of verifying this chain. Thinking it through a bit more, the chain seems quite critical. Is there any other safeguard preventing a malicious fallback IdP from generating backed assertions for any email address, listing itself as the issuer?
I'm sitting on a 90% complete update of the spec that addresses this, but that last 10% has been putting up a good fight.
In brief:
Basically, if you're willing to trust login.persona.org, then just imagine that every failed discovery really ends in a delegation to login.persona.org, and continue from there. So it's not strictly a whitelist. If my domain is a native IdP, then you should not trust things signed by the fallback.
In case 2, how do you know what fallback domain to start from, particularly whether or not it is trustworthy.
Presumably if case 1 fails, then the assertion was issued from a fallback domain. If someone is doing something malicious, the fallback is the issuer, which could then be self-asserted by the entity initiating the request.
Each verifier / relying party should explicitly elect to trust specific fallbacks. Right now, there is only one, login.persona.org, and the reference implementation trusts it by default. Which is another way of saying: If you're willing to trust Mozilla, then passport-browserid should define const fallbackIdP = 'login.persona.org'
and go from there.
Pragmatically, the number of fallbacks can't / shouldn't fall below 1, nor should it grow much larger than 1. Without any fallbacks, the system can't bootstrap itself. With too many fallbacks, it becomes difficult for users to get an identity certificate that will be honored by sufficiently many sites.
(The list of fallback IdP's that your library trusts should be configured beforehand -- when you get an assertion, it should either be signed by someone in the direct chain of authority from that domain, or in the direct chain of authority from a fallback you've decided to trust. If it's neither of those, something fishy is going on.)
Right. When a fallback is in use, the delegation chain will only ever be 1 (the domain of the fallback itself).
If that's correct, then for case 2, there's no need to restart discovery. Instead, simply check the whitelist of allowed fallbacks.
If that's not correct, what scenario would lead to a larger chain when a fallback was used?
I really need to get this damned spec updated. I'm really sorry about that.
When you're doing discovery for foo@example.com
, pass ?domain=example.com
whenever you request a /.well-known/browserid
file during discovery. In most cases, IdPs will ignore this. In the login.persona.org's case, this lets us separate out signing keys used for different domains.
Case in point:
https://login.persona.org/.well-known/browserid ← General case, covers most domains https://login.persona.org/.well-known/browserid?domain=yahoo.com ← Delegates to the Yahoo identity bridge
Also, I imagine the number of "trusted" fallbacks would be roughly equal to the number of browser vendors. If Google or Apple were to ever natively implement navigator.id
, I'd take the bet that they'd be their own fallback.
That's probably a safe bet. A world with 2-3 fallbacks would probably be manageable, but more than that would get rough.
That's part of the reason we went with persona.org instead of a mozilla.org subdomain -- we're hopeful that we'll be able to collaborate with other browser vendors on a neutral domain.
Hmm, I don't immediately understand how the domain
argument is useful in this case.
For example: https://login.persona.org/.well-known/browserid?domain=jaredhanson.net
That's doesn't delegate, but yet login.persona.org is perfectly capable of issuing assertions for that domain, by emailing me tokens.
(To clarify, there's no chain from login.persona.org to jaredhanson.net. How does the domain param help "restart discovery" vs just saying "hey, I trust login.persona.org for damn near everything".)
The domain param is just there as a hook so that targets of delegation can (optionally) respond differently for each domain under their jurisdiction.
The fallback uses this to facilitate identity bridging: we're building an IdP that uses Gmail's OpenID endpoint. When it's ready, we want to use that to certify users instead of the email link / password challenge that we're currently using.
So, some time in the next 4-6 weeks, the response to https://login.persona.org/.well-known/browserid?domain=gmail.com will change from its current document to one that delegates to this standalone Google OpenID / Persona bridge.
OK, got it. So let's say I sign in using Google OpenID and identity bridging through Persona. What form does the resulting email address take? I'm guessing user@gmail.login.persona.org from the convention on the yahoo domain?
In that case, you're not really a fallback in the sense that your not asserting addresses outside your own domain, and you'll fall into case 1 above. I'd still like clarification on case 2, and why it should just be "trust these pre-configured domains to issue for any domain".
Relatedly, what happens to users of Persona who go through the identity bridge to sign in, and then all of a sudden Google sets up BrowserID support on gmail.com. If a user previously established their account as user@gmail.login.persona.org, how do they get back to their account when the verification will now be user@gmail.com?
There's no address munging -- it's just a question of who the "issuer" is in an identity certificate. Since it's live in production right now, let's consider Yahoo users.
Normally, you'd expect to see certificates from the fallback that look like this:
{ "principal": { "email": "bar@example.com" }, "iss": "login.persona.org", ... }
A Yahoo user, on the other hand, will end up with a certificate that looks like this:
{ "principal": { "email": "foo@yahoo.com" }, "iss": "yahoo.login.persona.org", ... }
The question is: is it OK that yahoo.login.persona.org issued the certificate for this identity?
{"authority":"yahoo.login.persona.org"}
{ "public-key": ..., "authentication": ..., "provision": ... }
Awesome! It's OK that yahoo.login.persona.org signed the cert, since that's what the trusted fallback delegates to.
{ "public-key": ..., "authentication": ..., "provision": ... }
Oh no! It's Not OK that yahoo.login.persona.org signed the cert, since that domain does not explicitly appear in the trust chain.
Note that the two services, login.persona.org and yahoo.login.persona.org are completely independent and use totally different signing keys.
Thanks for the clarification that there's no address munging!
In the "With domain parameter case", what I still don't understand is how or why step 2 GET https://login.persona.org/.well-known/browserid?domain=yahoo.com
would get inserted into the authority verification step. yahoo.com
is the email domain, and a failure there should terminate. The only reason to introduce a non-yahoo domain would be due to some pre-configured notion to do it.
In other words, I can't get from yahoo.com to yahoo.login.persona.org, without explicitly introducing login.persona.org into the chain myself. And if we resort to that, we've effectively just declared login.persona.org
as trustworthy for anything (which also means the ?domain parameter convention not necessary).
The only reason to introduce a non-yahoo domain would be due to some pre-configured notion to do it.
Bingo! The fallback is, by definition, a pre-configured notion. ;) If all else fails, check in with the fallback.
we've effectively just declared login.persona.org as trustworthy for anything.
Yep. Except for domains that are either their own IdP, or which explicitly delegate to another IdP.
Right, but in my opinion then, it should just be:
login.persona.org
whitelisted to assert other domains? yes = success : no = failRemember, by the time we get to verifying authority delegation, we've already established that the assertion was correctly signed using persona.org's keys. We just want to know if it is acceptable that it does so, and there's no need to add another HTTP roundtrip to that process.
Remember, by the time we get to verifying authority delegation, we've already established that the assertion was correctly signed using persona.org's keys.
How are you getting there? Don't you have to do the whole delegation chain thing first, to find out what keys persona.org should be using?
Also, with the current Yahoo bridge, now both login.persona.org
and yahoo.login.persona.org
need to be whitelisted for cross-domain assertions. Or use a wildcard.
Check out the "With the ?domain parameter" section again -- you only have to explicitly trust login.persona.org. Everything else flows from login.persona.org delegating to another domain.
(Since we've been going back and forth in pretty much real time, I'll note that I'm going to go get some sleep and I'll continue responding here in the morning. Thank you for being interested in Persona, and motivated enough to want to hack on a native verifier for the Passport module. You've got my full attention and support - let me know what you need to make this thing a reality.)
You can think of imbuing your verifier with the following principles:
@example.com
, I will trust example.com
, or anything to which example.com
delegates.login.persona.org
, or anything to which login.persona.org
delegates.With those two principles, you're golden. You don't have to care about the Yahoo bridge explicitly, because you'll naturally find it as a consequence of the second principle whenever you need it.
No, the verification starts with the assertion, which is issued by the authoritative domain (login.persona.org
or yahoo.login.persona.org
). This is passed to verifyBundle
code. The first thing that does is fetch the public key for the issuer to verify the assertion.
In effect, if there were any delegations used when provisioning, those are unknown at the point the assertion is received by the relying party. It's only after doing this that the assertion's signatures can be validated and we can trust the contents that specify the issuer and principal. From that point, we have to work backwards and see if there is a delegation chain from the principal domain to the issuer (if they are different) code
Ok, get some sleep :)
I'm pretty confident in my analysis on this one though. I think this domain stuff is unnecessary overhead, and a whitelist is the correct approach.
Get some sleep yourself ;)
And ponder: Right now, login.persona.org should not be able to issue valid certificates for yahoo.com users, because it's delegated that to another domain. How would a whitelist handle that?
Right now, login.persona.org should not be able to issue valid certificates for yahoo.com users, because it's delegated that to another domain.
What? How did login.persona.org
delegate for a domain it doesn't control? Remember, this is a concern for fallback domains which by definition are outside of any delegation chain. And that's why the whitelist is needed.
Didn't mean to close this. It's getting late and I'm pressing random buttons...
Correct, the fallback is outside of the original domain's delegation chain.
Being outside the first delegation chain is the only thing that makes the fallback special. It's a normal IdP in all other regards. Normal IdPs can delegate authority to other domains, ergo, so can the fallback. This is done by publishing a special ./well-known/browserid file.
In pseudocode, authority discovery should work like this:
def find_authority(email):
user, _, domain = email.partition('@')
authority = follow_chain(domain, domain)
if authority is None:
# Try the fallback
authority = follow_chain('login.persona.org', domain)
return authority
Notice how I'm using the same algorithm for both chains, and only traversing the second (fallback) chain if the first one fails?
That traversal would look something like this:
def follow_chain(lookup_domain, email_domain):
resp = http.get('https://' + lookup_domain + '/.well-known/browserid?domain=' + email_domain)
document = parse(resp)
if document.type == 'invalid' or document.type == 'disabled':
return None
elif document.type == 'delegates':
return follow_chain(document.delegate, email_domain)
elif document.type == 'primary':
return lookup_domain
else:
return None
Here are a few examples of what you should get in the real world:
>>> find_authority('foo@eyedee.me')
'eyedee.me'
>>> find_authority('foo@mockmyid.com')
'mockmyid.com'
>>> find_authority('foo@delegat.es')
'mockmyid.com
>>> find_authority('foo@example.com')
'login.persona.org'
>>> find_authority('foo@yahoo.com')
'yahoo.login.persona.org'
Certificates should only be valid if they're signed by the domain returned by find_authority
.
It's a normal IdP in all other regards. Normal IdPs can delegate authority to other domains, ergo, so can the fallback.
Except its not. Adding a query param named domain, does not make any random web host authoritative for that domain. Only control of the actual domain does. Therefore, it has no ability to delegate authority. The only thing that gives the fallback any sort of trust, is that the relying party imbued it with that ability.
I get what you're trying to do. However, I still think its not needed.
Let's walk through the Yahoo bridge case and compare it for something that doesn't have identity bridging.
certIssuer = yahoo.login.persona.org
find_authority('foo@yahoo.com')
GET https://yahoo.com/.well-known/browserid?domain=yahoo.com -> 404
*** BEGIN FALLBACK ***
GET https://login.persona.org/.well-known/browserid?domain=yahoo.com
-> {"authority":"yahoo.login.persona.org"}
GET https://yahoo.login.persona.org/.well-known/browserid?domain=yahoo.com
→ { "public-key": ..., "authentication": ..., "provision": ... }
*** OK *** (certIssuer = authority)
Consider this case:
certIssuer = login.persona.org
find_authority('foo@jaredhanson.net')
GET https://jaredhanson.net/.well-known/browserid?domain=jaredhanson.net -> 404
*** BEGIN FALLBACK ***
GET https://login.persona.org/.well-known/browserid?domain=jaredhanson.net
→ { "public-key": ..., "authentication": ..., "provision": ... }
*** ??? ***
What happens here. Nowhere has jaredhanson.net
been delegated to the certIssuer
. Should it fail? Yes, if I don't trust login.persona.org
. But I do, so I accept it because its on my whitelist.
Let's reattempt that case, but do something slightly different on the Persona side. Namely, change the issuing domain.
certIssuer = jaredhanson.login.persona.org // <-- note the change
find_authority('foo@jaredhanson.net')
GET https://jaredhanson.net/.well-known/browserid?domain=jaredhanson.net -> 404
*** BEGIN FALLBACK ***
GET https://login.persona.org/.well-known/browserid?domain=jaredhanson.net
-> {"authority":"jaredhanson.login.persona.org"}
GET https://jaredhanson.login.persona.org/.well-known/browserid?domain=jaredhanson.net
→ { "public-key": ..., "authentication": ..., "provision": ... }
*** OK *** (certIssuer = authority)
OK, now were acceptable according to the above algorithm. And this would be easy enough to do with some host wildcarding for *.login.persona.org.
But, we arrive back at the same point. If we trust the fallback (which is not the email domain) enough to vouch for a domain accepted as a query param, then we have granted it the ability to be authoritative for any and all domains. That trust only comes from pre-configuration by the relying party, and nothing else.
That reduces down to a whitelist, which makes the fallback requests unnecessary. The fallback algorithm can simply consult a whitelist.
I can't state this enough: login.persona.org
is not and will not ever be authoritative for yahoo.com
, if you start from a domain that is not yahoo.com
(which is what you're proposing in the fallback scenario).
This is important, and I've got some more scenarios to walk through. I may not get them posted until tonight though.
Another case, this time where there are multiple fallback providers. Lets say login.persona.org
and login.googleid.com
for sake of conversation:
I run a website, where people are using Chrome (with native navigator.id
falling back to Google ID). And, of course, Persona for Firefox and shimed browsers.
Sue uses both Firefox and Chrome. She has a Yahoo address, which doesn't support BrowserID natively. So assertions for her identity could be either:
I go to verify a cert issued by googleid.com, and have persona.org configured as a fallback:
certIssuer = yahoo.login.googleid.com
find_authority('foo@yahoo.com')
GET https://yahoo.com/.well-known/browserid?domain=yahoo.com -> 404
*** BEGIN FALLBACK ***
GET https://login.persona.org/.well-known/browserid?domain=yahoo.com
-> {"authority":"yahoo.login.persona.org"}
GET https://yahoo.login.persona.org/.well-known/browserid?domain=yahoo.com
→ { "public-key": ..., "authentication": ..., "provision": ... }
*** FAIL *** (certIssuer != authority)
Whoops. That's not good. I actually do trust login.googleid.com, so I repeat the fallback algorithm, rooted at a different domain
certIssuer = yahoo.login.googleid.com
find_authority('foo@yahoo.com')
GET https://yahoo.com/.well-known/browserid?domain=yahoo.com -> 404
*** BEGIN FALLBACK ***
GET https://login.googlid.com/.well-known/browserid?domain=yahoo.com
-> {"authority":"yahoo.login.googleid.com"}
GET https://yahoo.login.googleid.com/.well-known/browserid?domain=yahoo.com
→ { "public-key": ..., "authentication": ..., "provision": ... }
*** OK *** (certIssuer == authority)
Should my fallback algorithm loop through multiple providers, attempting to get success from a single one, and failing if all fail? Maybe, but that's not optimal. Again this reduces down:
All I have to do is check if whitelist.contains(certIssuer)
. If yes, trust the assertion, otherwise fail.
@jaredhanson Just to check in real quick: this started with a question about how things work today, but we've ended up with a proposal to simplify things via wildcards and white-listing, right?
And if so, is this an accurate summary?
(This part about today should be accurate -- it's the current state of the protocol. Any other behavior is likely to be non-conforming.)
As part of the protocol's contract, fallbacks may directly vouch for users at any domain or they may delegate that power to any other domain.
The set of trusted fallbacks is preconfigured within each verifier.
The set of trusted fallbacks is recommended to be ['login.persona.org']
.
A fallback may not vouch for users at a domain if there is a valid chain of authority rooted at the email's domain itself.
As part of the protocol's contract, fallbacks may directly vouch for users at any domain.
Fallbacks may not delegate authority. However, a whitelist of several fallbacks may be specified, which may contain wildcards.
The fallback whitelist is preconfigured within each verifier.
The whitelist is recommended to be ['login.persona.org', '*.login.persona.org']
.
A fallback may not vouch for users at a domain if there is a valid chain of authority rooted at the email's domain itself.
Yep, that's an accurate summary and I'm in favor of the proposed simplification. Thanks for taking the time to discuss!
@jaredhanson What are your thoughts on limiting the authority of the subdomains?
In the current model, an attacker who compromises yahoo.login.persona.org
could only forge certificates for users at Yahoo. In a wildcard model, couldn't it be used to forge certs for Gmail users, too?
Yes, that's true. In the simplified proposal, there's no reason to issue certificates from yahoo.login.persona.org
. They could just as well be issued by login.persona.org
directly, which would reduce the whitelist by one domain or avoid the need for a wildcard.
I do see some benefit to limiting the scope of domains for which a fallback IdP can issue assertions, of course. As an initial suggestion, I would recommend putting that in the support document, like so:
yahoo.login.persona.org/.well-known/browserid
:
{
"public-key": {},
"authentication": "/authentication",
"provisioning": "/provision",
"domain": "yahoo.com"
}
Primary IdPs would omit the domain, as it would be implicit.
yahoo.com/.well-known/browserid
:
{
"public-key": {},
"authentication": "/authentication",
"provisioning": "/provision"
}
For a primary IdP, the domain is equal to the domain the support document was served from.
Now, if someone compromises yahoo.login.persona.org
they can still issue certs for any domain they want to, but only a single domain, not all domains. They could cycle that domain, of course, using gmail.com for a few hours, and then hotmail.com.
If this approach were taken, there would be two classes of fallback IdPs. "Root" fallback IdPs, like login.persona.org, and "domain-specific" fallback IdPs. Given the cycling issue mentioned above, this suggestion may not be worth the complication.
The existence of any trusted, fallback IdP (including login.persona.org) creates a highly-privileged class of identity providers, and that needs to be carefully noted in the security considerations for BrowserID as a protocol (if fallbacks are in use). As a "root identity issuer" login.persona.org
becomes a highly valuable target for attacks. A compromise at that point, compromises all domains.
The ultimate solution, where domains are authoritative only for themselves, is to eliminate fallbacks. That is unlikely to happen, but with a whitelist approach, relying parties are free to do that by clearing the whitelist.
I'm not sure of any technical solution to the problem of trusting fallbacks, though I'd love to hear about it if there are. Establishing verifiable and revocable trust at the fallback level may require more of a social and political solution.
For instance, what if there were 4 or more separate organizations that acted as "trust enforcement" for fallback IdPs. Lets say login.persona.org
, login.appleid.com
, login.googleid.com
, login.verisign.com
. These domains could each publish a list of privileged, root-level fallbacks.
Relying parties who want a "trustworthy" list of fallbacks could subscribe to the list of fallback IdPs that each publish. If any domain is listed on 3 of 4 (or some acceptable number to establish an acceptable level of assurance), the relying party would add that domain to their whitelist. Ideally the set is chosen so that there's enough competitive interest among them all that there'd be no reason for some to collude, and therefore exclude one of the others from being trusted.
Now, if there is any compromise, that would get removed quickly from at least 2 of the lists, and propagate down. There's some propagation time where there is an open attack surface. Again, I'd note that that surface comes into existence due the the existence of fallbacks in the first place.
Relying parties have to assess this against their requirements. A government organization, for instance, should choose not to trust fallbacks. Other sites may whitelist without checking for any quorum. Some may choose to respect the decisions of the quorum.
I think I've rambled sufficiently on this for now. This is an important consideration for BrowserID, so I'd encourage others to share their thoughts on the matter.
One other note on limiting the scope of the whitelist. It could be configured by the relying party like:
login.persona.org -> * yahoo.login.persona.org -> yahoo.com
This way the configuration can be more fine-grained with respect to the authority given to fallback IdPs.
Yet another note on adding a domain field to the support document for a fallback IdP:
yahoo.login.persona.org/.well-known/browserid:
{
"public-key": {},
"authentication": "/authentication",
"provisioning": "/provision",
"domain": "yahoo.com"
}
Relying parties could use this to built a immutable provisioning map. Upon first seeing a domain-specific fallback IdP, they could tie that to the domain listed in the support document. If it were ever observed that the domain listed in the support document has changed, that would immediately signal a potential attack and trust of yahoo.login.persona.org
could be revoked.
Again, not sure this is worth the complication, but it would allow the relying party to detect attack vectors.
We want to keep discovery as simple as possible, while meeting the requirements of the BrowserID protocol.
I don't see how the proposed change simplifies the protocol, nor how it improves security.
Let's assume one fallback, we will work hard to work with other vendors to keep it to one fallback.
Capturing this in a whitelist config is a great option for PassportJS, having it be a list, hedges your bets down the road.
The dynamic domain piece of discovery has several use cases and is the simplest mechanism we could find to meet those requirements. Another example is a shared IdP which uses different public/private keys per domain. Imagine twofactorpersona.org which will let you delegate to them for an annual service fee. Each customer gets security scoped to their domain.
I agree with callahad, let's take this proposal to the mailing list, which is the usual means of working through proposals.
I'm super excited for local verification in Passport, great stuff Jared!
Hey Austin -
Thanks for checking in.
I just want to reiterate that I'm not proposing any changes to the discovery process. As far as primary IdPs go, BrowserID is top notch. The PKI advantages make it the best identity protocol going, especially in regards to privacy.
Your shared IdP example is a good use case. But, this falls under the primary IdP use case. The "domain" param here is a useful hint for a shared IdP, and they can't change the context from the original domain in question.
This discussion all relates to how fallback is handled. I don't believe an RFC could mandate this aspect as a MUST, given that local policies need to be assessed. So, I will definitely be implementing a whitelist in Passport.js, as that gives flexibility and control to the relying party.
I do think the "domain" parameter as used by a fallback in this case is misleading at best. Establishing a precedent for trusting delegation chains that don't originate from the domain in question is just bad practice, in my opinion.
The whitelist would short-circuit this, be just as deterministic and limit potential abuse.
Of course, I also want to be compliant with the protocol, so I wanted to raise this discussion. Happy to move it to the mailing list.
I'm going to close down this bug -- let's move the specific fallback / delegation proposal to the mailing list.
For this to be seriously considered, it will have to be able to fully replace the current delegation model, without causing any regressions in security or maintainability for all involved parties. Specifically, with the current system:
I personally do not see how a whitelist improves on the status quo, but if you can figure out how to retain the current properties of the system, I'm game.
Until then, directly implementing a whitelist approach in passport-browserid contradicts the current behavior of the system. It will break things and expose you to additional vulnerabilities. Please don't do that to your users.
Until then, directly implementing a whitelist approach in passport-browserid contradicts the current behavior of the system. It will break things and expose you to additional vulnerabilities. Please don't do that to your users.
How? I'll continue this discussion anywhere, but I'm of the exact opposite conclusion. A whitelist preserves all those properties and give more control to the RP.
Addressing those issues in the context of a whitelist:
A whitelist is able to act as a kill-switch on any fallback or identity bridges, by removing the whitelisted domain.
Wouldn't that require every RP / library updating their configuration? Using delegation, the fallback at login.persona.org can simply cut them off by no longer delegating.
I'm honestly not trying to move the goalposts here -- probably another good reason to take it to the mailing list.
How would turning on a new bridge work? Turning it off? Expanding its scope to cover additional domains, like ccTLDs?
If we have to rely on configurations to get updated everywhere, that won't work.
If the whitelists have a wildcard, then I can't see how we could adjust the scope of each bridge.
If we sign with the same keypair as login.persona.org, we've given the bridges the ability to sign for other domains, too.
Maybe we could maintain a centralized whitelist as part of login.persona.org's .well-known/browserid file, but I don't think that actually improves things.
I agree that all the questions you raise are valid considerations. However, I also think they are very focused on Mozilla's objectives with the persona.org infrastructure and the operation of the fallback. That is very important of course; however, I am raising my concerns from the perspective of the relying party, which needs to be given just as much (if not more) consideration for BrowserID to meet its objectives.
Specifically, there are enterprises using Passport (mostly with SAML), who do have specific arrangements where, for example: employees of corpa.com can log in to binc.com. But, only if that's done through approved channels. It would not be acceptable if assertions from a non-approved third-party (including login.persona.org) were used.
Note, I'm not trying to "enterprise-up" this discussion. I have no interest in that direction. BrowserID as a protocol is already very satisfactory for these use cases. Good enough, in fact, that I could see internal identity bridges set up between corporations to bridge over existing trusted channels.
In any case, my main concern here is to get an understanding of the fallback process, so that there is some level of transparency and control.
I know you're still working on the spec, but I'd like to walk through this again in the hopes that it can help clarify things. Let's take the case of verifying an assertion issued by yahoo.login.persona.org
:
yahoo.login.persona.org
for the public key to verify the assertion (check mark: satisfied different key pairs for issuers)Note: step 6 is not necessary. There's no information needed in the response, and we've already found that login.persona.org
delegates to the issuer. If you want delegation by the fallback, I don't think the spec needs to require this.
What would the response look like if yahoo.login.persona.org
had the kill switch thrown? Would it simply delegate to a different domain? If we traced this delegation and didn't get to the issuer, that suggests a compromise of some degree at the fallback, which could well extend beyond just yahoo.login.persona.org
. This fact should be raised to the relying party as a serious issue.
Now, repeat this process using jaredhanson.net
as a domain for which there is no identity bridge. I still haven't seen clarification on what the spec says with regards to trusting that. Authority was not delegated. Is a response containing a "normal" support document enough? If so fine, just clarify that.
Maybe we could maintain a centralized whitelist as part of login.persona.org's .well-known/browserid file, but I don't think that actually improves things.
I think that would actually be good. Some RPs may want to approve these, or at least be aware that new issuers are about to appear. They may also only want to accept assertions from some subset of identity bridges.
As a final point, I'd note that whatever persona.org chooses to do, it very easy to override, simply by changing the local fallback URL. For instance:
Now, the RP is entirely back in control. They can redelegate to login.persona.org
if they wish, or implement their own policies. Again, back to a whitelist.
I know my users will want this. From Passport's perspective, all I'm doing in this regard is moving the whitelist into configuration supported by the module, rather than requiring the setup of a "redelegating" BrowserID fallback.
What would the response look like if yahoo.login.persona.org had the kill switch thrown?
There are a couple ways to disable yahoo.login.persona.org, which are implementation details, but the key takeaway is that step 5 (GET https://login.persona.org/.well-known/browserid?domain=yahoo.com) would no longer delegate authority. It would respond with a public key and act as the issuer.
Sorry, if I missed the point of the proposal earlier.
I agree with many of the points you are making Jared, but I want to make sure the current flow is clear. What has been in production for a few months, and what is lagging in documentation is this:
There is a discovery protocol with several steps which include things like /.well-known/browserid?domain={domain.tld}
. This discovery is to be attempted on the domain portion of an email address. If this fails, the same discovery protocol is used against login.persona.org
.
There is no extra or different flow for the fallback IdP.
Thanks for hammering out these ideas guys!
the key takeaway is that step 5 (GET https://login.persona.org/.well-known/browserid?domain=yahoo.com) would no longer delegate authority. It would respond with a public key and act as the issuer.
OK then. This specifically makes it much harder to craft a whitelist.
I'll make one major point here: trusting a fallback, and trusting a fallback that can re-delegate authority are two very different levels of trust. Specifically, much more trust must be placed in the re-delegating fallback. Which, incidentally makes it less likely that the fallback is acceptable to use.
There will remain a need for relying parties to reliably choose who to trust. If the fallback can delegate, then any policy statements at the RP will need to take that into account. Specifically, now there needs to be policy statements about following delegations.
For example:
allow delegation from login.persona.org
to *.login.persona.org
; deny other
Without such policy in place, there exits the potential that login.persona.org
could delegate to an authority that the RP does not trust.
the key takeaway is that step 5 (GET https://login.persona.org/.well-known/browserid?domain=yahoo.com) would no longer delegate authority. It would respond with a public key and act as the issuer.
Also, this makes a "successful" response have two potentially different meanings.
GET https://login.persona.org/.well-known/browserid?domain=jaredhanson.net -> { "public-key": ..., "authentication": ..., "provision": ... }
Trustworthy, and what I expect from the known-good configuration of login.persona.org
.
GET https://login.persona.org/.well-known/browserid?domain=yahoo.com -> {"authority":"yahoo.login.persona.org"}
Trustworthy, and what I expect from the known-good configuration of login.persona.org
.
GET https://login.persona.org/.well-known/browserid?domain=yahoo.com -> { "public-key": ..., "authentication": ..., "provision": ... }
Hmmm, seemingly trustworthy but potentially suspect. This doesn't match the known-good configuration. Why is this response no longer delegated. Was yahoo.login.persona.org
compromised and the kill switch flipped? Is Mozilla just adjusting their internal deployments for innocent reasons?
I do, of course, need to check the cert issuer with the final authority for this particular assertion, but the questions remain. I may want to invalidate the login sessions of anyone who authenticated with certs from yahoo.login.persona.org
in the past hour. If I'm really paranoid, I may want to clear sessions from any delegation chain rooted at login.persona.org
, under the precaution that if one of them is broken, others potentially are as well.
As a sensitive RP, I want an issue raised so that my security team can investigate.
I'm implementing a local verification strategy for Passport.js (work-in-progress on the local-verify branch).
I've run into an issue regarding verifying the authority delegation chain when fallback IdPs are in use (including login.persona.org itself).
In Persona's verifier, anytime the issuer doesn't match the email domain, there's a check to verify that authority is delegated (see code). One exception to that is when the hostname of the verifier is also the issuer, which is nearly all the time currently, since Persona's fallback IdP is responsible for both issuing and verifying tokens.
That rule breaks if verification is done locally. For instance, my test app receives assertions issued by
login.persona.org
that covergmail.com
as a domain. Obviously, there is no delegation chain from gmail.com to login.persona.org. You can see my note on this here.Is there a recommended practice for this situation? Should fallback IdPs be whitelisted as acceptable to issue assertions for any domain? Is verifying this chain even necessary? Presumably there'd have to have been a problem in the provisioning process if this were to arise.