Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add privacy consideration about terminating getAssertion early #687

Merged
merged 12 commits into from Jan 17, 2018
Merged

Conversation

emlun
Copy link
Member

@emlun emlun commented Nov 13, 2017

Related issues: #184, #204 . Loosely related: #140 .

This adds a privacy consideration note to §5.1.4. Use an existing credential to make an assertion about the privacy implications of terminating before the timer expires.

The PR also includes some language changes to the waiting condition in both §5.1.3. Create a new credential and §5.1.4. Use an existing credential to make an assertion which are needed to make the privacy consideration text meaningful, and which were probably already intended.


Preview | Diff

The previous language would have the procedure terminate as soon as
there are no pending authenticator requests - including immediately at
the beginning unless at least one authenticator is available at that
time.
@emlun emlun added privacy-tracker Group bringing to attention of Privacy, or tracked by the Privacy Group but not needing response. subtype:algorithms/WebIDL subtype:privacy type:technical labels Nov 13, 2017
@emlun emlun added this to the CR milestone Nov 13, 2017
index.bs Outdated
- A named [=public key credential|credential=] is not available.
- A named [=public key credential|credential=] is available, but the user denies [=user consent|consent=] to use it.

If the above cases are distinguishable, that leaks information by which a malicious [=[RP]=] could identify the user by probing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"that leaks information" -> "information is leaked"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds better, thanks!

Copy link
Contributor

@equalsJeffH equalsJeffH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks fine, thanks. Though:

  1. please merge-from-master
  2. perhaps we should land PR fix #322: flesh out Security Considerations (for now) #705 first since it creates a priv cons section, and add the proposed #getAssertion-privacy-considerations as a priv cons subsection?
  3. normative requirements ought to be in the algorithm itself, rather than buried in priv cons, pls see comment below.

index.bs Outdated
If the above cases are distinguishable, information is leaked by which a malicious [=[RP]=] could identify the user by probing
for which [=public key credential|credentials=] are available. In particular, the client MUST NOT return a "{{NotAllowedError}}"
before the |lifetimeTimer| has expired, as that difference in timing would constitute such an information leak.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMV, the latter final normative sentence needs to be stated in the algorithm itself, rather than buried down here in Priv Cons.

@equalsJeffH
Copy link
Contributor

Reviewed, modest changes requested: #687 (review)

@equalsJeffH
Copy link
Contributor

so this fixes issues: #184, #204 ?

@emlun
Copy link
Member Author

emlun commented Dec 7, 2017

Agreed on all three points. I'll finish this up after #705 is merged.

Yes, this fixes #184 and #204, assuming I'm right in interpreting #204 as raising the same concern as #184.

@emlun
Copy link
Member Author

emlun commented Dec 25, 2017

@equalsJeffH Please re-review!

Copy link
Contributor

@equalsJeffH equalsJeffH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo nits. thx @emlun !

index.bs Outdated

In order to protect the user from being identified without [=user consent|consent=], implementations of the
{{PublicKeyCredential/[[DiscoverFromExternalSource]](origin, options, sameOriginWithAncestors)}} method need to take care to
not leak information that could enable the [=[RP]=] to distinguish between the cases:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/the cases/these cases/

index.bs Outdated
not leak information that could enable the [=[RP]=] to distinguish between the cases:

- A named [=public key credential|credential=] is not available.
- A named [=public key credential|credential=] is available, but the user denies [=user consent|consent=] to use it.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/user denies/user does not/

index.bs Outdated

If the above cases are distinguishable, information is leaked by which a malicious [=[RP]=] could identify the user by probing for
which [=public key credential|credentials=] are available. For example, one such information leak is if the client returns a
failure response as soon as the user denies [=user consent|consent=] to proceed with the operation. In this case the [=[RP]=]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/user denies/user does not/

Copy link
Contributor

@equalsJeffH equalsJeffH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks :)

@emlun
Copy link
Member Author

emlun commented Jan 11, 2018

@equalsJeffH Reading this again, I'm inclined to revert this one change #687 (comment) to "the user denies consent", as the containing sentence is meant to express "if the client returns a failure in response to the user's action of refusing a prompt for consent". What do you think?

@equalsJeffH
Copy link
Contributor

sure, fine with me.

@emlun emlun mentioned this pull request Jan 12, 2018
@nadalin
Copy link
Contributor

nadalin commented Jan 17, 2018

@selfissued please review so we can merge

Copy link
Contributor

@selfissued selfissued left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new privacy section seems fine. But it seems like an incorrect and unrelated change to delete the clause "issuedRequests not empty" in two places. Please don't merge this until this this additional change is either documented and agreed to or removed.

@@ -885,8 +885,8 @@ When this method is invoked, the user agent MUST execute the following algorithm

1. [=set/Append=] |authenticator| to |issuedRequests|.

1. [=While=] |issuedRequests| [=list/is not empty=], perform the following actions depending upon
|lifetimeTimer| and responses from the authenticators:
1. [=While=] |lifetimeTimer| has not expired, perform the following actions depending upon |lifetimeTimer| and responses from the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was the clause "issuedRequests not empty" deleted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -1233,7 +1235,7 @@ When this method is invoked, the user agent MUST execute the following algorithm

1. [=set/Append=] |authenticator| to |issuedRequests|.

1. While |issuedRequests| [=list/is not empty=], perform the following actions depending upon |lifetimeTimer|
1. [=While=] |lifetimeTimer| has not expired, perform the following actions depending upon |lifetimeTimer|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was the clause "issuedRequests not empty" deleted?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emlun
Copy link
Member Author

emlun commented Jan 17, 2018

@selfissued Thank you for reviewing. The clause "issuedRequests is not empty" was changed to "lifetimeTimer has not expired" because with the previous language the operation would terminate immediately if no authenticator is present at the moment when the timer is started, but makeCredential step 19 and getAssertion step 17 are intended to be run asynchronously as authenticators appear within the timeout. This change is mostly orthogonal to the addition of the privacy considerations section, but it's necessary for the privacy consideration text to make any sense. I also believe we need to make this change for the algorithms themselves to make sense, even without the new privacy consideration.

@selfissued
Copy link
Contributor

Thanks for the explanation, @emlun . Please proceed with the merge.

@emlun emlun merged commit 51ec228 into master Jan 17, 2018
@emlun emlun deleted the issue-184 branch January 17, 2018 16:53
@equalsJeffH
Copy link
Contributor

@emlun wrote in #687 (comment):

Yes, this fixes #184 and #204, assuming I'm right in interpreting #204 as raising the same concern as #184.

Actually, upon further review, I do not believe this PR addressed issue #204, because the latter is in regards to #createCredential aka [[Create]], and this PR addressed privacy concerns at authentication time i.e. #getAssertion aka [[DiscoverFromExternalSource]] aka "authentication ceremonies". See also #184 (comment)

So we still have #204 to address.

@emlun
Copy link
Member Author

emlun commented Jan 19, 2018

I believe with hot-plugging makeCredential doesn't have the same information leak anymore, and either way this PR made the same normative changes to both the makeCredential and the getAssertion algorithms. But you're right that the Privacy Considerations subsection only mentions getAssertion, which should be fixed.

My reasoning is the following. The attack would previously be:

  1. The attacker calls makeCredential with some credentials in the excludeCredentials parameter.
  2. The browser notices that one of the excluded parameters is available, and terminates the operation immediately.
  3. The attacker concludes that the user possesses one of the named credentials.

Whereas now, if an excluded credential is available, the browser will simply ignore that authenticator and instead wait for a new authenticator without an excluded credential to appear. If the browser were to violate the spec and return a NotAllowedError as soon as the user denies the prompt (which step 21 now expressly forbids), the attack would be:

  1. The attacker calls makeCredential with some credentials in the excludeCredentials parameter.
  2. The browser notices that one of the excluded parameters is available, ignores that authenticator and waits for another to appear.
  3. The user plugs in a new authenticator.
  4. The browser asks the user if they want to create a credential on that authenticator.
  5. The user denies the prompt.
  6. The browser returns a NotAllowedError to the attacker.
  7. The attacker notices that a NotAllowedError was returned before the timer expired.
    • The attacker can conclude that the user has an authenticator that does not contain one of the named credentials.
    • The attacker cannot conclude that the user has an authenticator that contains one of the named credentials.
    • The attacker cannot conclude that the user does not have an authenticator that contains one of the named credentials.

I suppose this conclusion could perhaps be combined with some external assumptions (like an upper limit on how many authenticators a user is likely to own) to narrow the field to some extent, possibly enough to identify a single user. But again, this should also not be possible if the browser never returns the NotAllowedError before the timer expires.

Please let me know if you spot any errors in the above reasoning. :)

@equalsJeffH
Copy link
Contributor

equalsJeffH commented Jan 19, 2018

Thanks @emlun for your detailed analysis above. I had not reviewed the diff closely enough and had focused on the added Authentication Ceremonies privacy considerations subsection rather than the revised lifetime timer stipulations in both #createCredential aka [[Create]] and #getAssertion aka [[DiscoverFromExternalSource]]. For #createCredential aka [[Create]] these are steps 20 and 21. mea culpa!

I agree with the overall conclusion of the detailed analysis above: a malicious RP (aka "the attacker") will not learn anything through seeding of the excludeCredentials list because any returned NotAllowedErrors are returned after the lifetimeTimer has expired, regardless of the user's actions (see step 21).

It seems, IIUC, there are a couple detail-level issues with the steps in the detailed analysis above, but they do not AFAICT affect this conclusion.

Here they are for completeness' sake:

  1. The browser notices that one of the excluded parameters is available, ignores that authenticator and waits for another to appear.

AFAICT, the browser/client-platform does not do this (see #createCredential steps 19.5 and 19.6), rather the authnr does this (see authenticatorMakeCredential step 3), and returns NotAllowedError, which is handled by #createCredential step 20 switch branch "If any authenticator returns an error status".

  1. The browser asks the user if they want to create a credential on that authenticator.

nit: AFAICT the spec does not stipulate whether the browser, client platform, or authenticator prompts the user.

  1. The browser returns a NotAllowedError to the attacker.
  2. The attacker notices that a NotAllowedError was returned before the timer expired.

Ah, but the imperative "this step MUST NOT be executed before lifetimeTimer has expired" language this PR inserted into #createCredential step 21 means that the error is not returned before the timer expires, yes? (which makes the above analysis stronger).

thanks again.

@emlun
Copy link
Member Author

emlun commented Jan 20, 2018

Thanks for your detailed response!

  1. The browser notices that one of the excluded parameters is available, ignores that authenticator and waits for another to appear.

AFAICT, the browser/client-platform does not do this [...] rather the authnr does this [...] and returns NotAllowedError

Ah yes, that is correct. Either way, the end result is that the client ignores the authenticator in question and keeps waiting for another candidate.

nit: AFAICT the spec does not stipulate whether the browser, client platform, or authenticator prompts the user.

Indeed, although authenticatorMakeCredential step 6 softly specifies that the authenticator does it if capable, and otherwise the client. I chose to keep it simple because I don't know of existing authenticator hardware that allows active denial of consent (as opposed to passive denial by timeout).

Ah, but the imperative [...] this PR inserted into #createCredential step 21 means that the error is not returned before the timer expires, yes? [...]

Assuming implementers read and implement all the steps, yes. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
privacy-tracker Group bringing to attention of Privacy, or tracked by the Privacy Group but not needing response. subtype:algorithms/WebIDL type:technical
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants