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

Timing and token reuse #1669

Closed
wants to merge 5 commits into from
Closed

Timing and token reuse #1669

wants to merge 5 commits into from

Conversation

psivesely
Copy link
Contributor

Status

Ready for review.

Description of Changes

Eliminates some timing leaks and prevents the same valid token from being used to authenticate twice in a row.

Checklist

If you made changes to the app code:

  • Unit and functional tests pass on the development VM

If you made changes to the system configuration:

  • Testinfra tests pass on the development VM

Noah Vesely added 2 commits April 25, 2017 15:59
Removes timing information from two functions that are executed during the JI
login process.

In `valid_password()` there was a timing-variable, byte-by-byte comparison of
the salted scrypt hash of submitted password with the salted scrypt hash of
their actual password. Since the salt is kept secret from the Journalist, this
makes it impossible to know what the final value of the submitted password hash
is. Thus an attacker may learn how many prefix bytes in common the hash of their
submitted password has with actual password hash, but not what those bytes are.
Due to the avalanche effect of cryptographic hash functions, this makes this
information totally useless. Therefore, *this timing information is not
exploitable*, but we are removing it anyway because we have gotten several
complaints about it, and would rather not go through the explanation of why it's
safe here again.

In `verify_token()` there was a timing-variable, byte-by-byte comparison of
`self.last_token` (a Journalist field corresponding to the last token a
Journalist attempted to login with) with the submitted token. If
`self.last_token` was a valid login token, and assuming best possible
circumstances (client time was skewed ahead of server and attacker immediately
starts guessing after it's entered, giving a >60s window of validity, timing
information resolution is perfect, etc.) it can be exploited to increase the
probability of an attacker guessing a correct token by ~2.2 times during that
period:

```
>>> p = (10 / float(10 ** 6))
>>> (sum([9 / (float(10 ** (6 - n)) * float(10 ** n)) for n in range(5)]) + (2 * p)) / (3 * p)
2.1666666666666665
```

Still, when you look at the actual value, it's pretty insignificant:

```
>>> sum([9 / (float(10 ** (6 - n)) * float(10 ** n)) for n in range(5)]) + (2 * p)
6.500000000000001e-05
```

Although the information gain is almost nothing here even under the best-case
scenario, we're again going to patch this one with a constant-time comparison
just to avoid bug reports in the future, and play it extra safe.
The intention of the orignal implementation of the `last_token` attribute was to
prevent a 2FA token from being re-used as it might be in some MitM or
shoulder-surfing attacks. The problem with it was that it always saved the last
token entered instead of the last valid token. Thus if attacker A watches
Journalist J use token T, they could attempt to login using T', a random token,
and then successfully login on their next attempt using T as `last_token` will
have been set to T'. This is resolved by updating `last_token` only for valid
tokens.
@redshiftzero
Copy link
Contributor

Hmm, builds seem to be reliably failing on Travis on this - you didn't see any test failures locally?

@conorsch
Copy link
Contributor

Also seeing 22 failures locally, same as in Travis.

@conorsch
Copy link
Contributor

hmac.compare_digest() was added in Python 2.7.7, but Ubuntu Trusty includes only Python 2.7.6. So these tests will never pass with the current implementation, nor did they prior to submission.

@conorsch
Copy link
Contributor

We cannot trivially upgrade the version of Python running on SecureDrop instances, so we'll need to consider another path. Worth nothing that #1530 considers upgrading the Trusty hosts to Xenial, which would get us newer versions.

@conorsch conorsch closed this Apr 26, 2017
@redshiftzero redshiftzero added this to the 0.4.1 milestone Apr 26, 2017
:meth:`hmac.compare_digest()` is not available until Python 2.7.7, which has not
landed in Ubuntu Trusty yet (still on 2.7.6). This function will hold us over
until then, and will automatically switch us over to `compare_digest()` when it
becomes available.
@psivesely
Copy link
Contributor Author

psivesely commented Apr 26, 2017

Re-posting from Slack:

Too much multitasking, sorry.

Tests were running and I preemptively checked them, then started on something else and submitted the PR w/o thinking.

Also, re-opening because I just replaced hmac.compare_digest() with another constant-time comparison function from Django.

@psivesely psivesely reopened this Apr 26, 2017
Noah Vesely added 2 commits April 26, 2017 15:25
Just like my implementation this is replacing, if :meth:`hmac.compare_digest()`
is available in the running Python version, it will be used. Thus, we need not
worry about "upgrading" this function in the future should our distribution
include a new version of Python.
@psivesely
Copy link
Contributor Author

Closing for now because I know this won't get considered for merge for a really long time and will be out of date by then.

@psivesely psivesely closed this May 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants