-
Notifications
You must be signed in to change notification settings - Fork 687
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
Bump 2FA secret bit length from 80 to 160 bits as recommended by RFC4226 #5958
Conversation
securedrop/models.py
Outdated
@@ -566,7 +576,7 @@ def valid_password(self, passphrase: 'Optional[str]') -> bool: | |||
return is_valid | |||
|
|||
def regenerate_totp_shared_secret(self) -> None: | |||
self.otp_secret = pyotp.random_base32() | |||
self.otp_secret = generate_otp_secret() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an issue open to eliminate the pyotp dependency altogether ( #5613 ) would you be open to expanding this PR to cover that, or to additions to the PR by our team to get rid of pytop altogether?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not able to move this forward right know but please feel free to add other changes and i may be able to support with the review. thank you
securedrop/models.py
Outdated
@@ -403,7 +413,7 @@ class Journalist(db.Model): | |||
is_admin = Column(Boolean) # type: Column[Optional[bool]] | |||
session_nonce = Column(Integer, nullable=False, default=0) | |||
|
|||
otp_secret = Column(String(16), default=pyotp.random_base32) | |||
otp_secret = Column(String(32), default=generate_otp_secret) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Varchars in sqlite ignore the length constraint, but a db migration is a good idea regardless. Happy to add it, as it's more of a maintenance task.
securedrop/models.py
Outdated
Generate an OTP secret of 160 bits encoded base32 | ||
""" | ||
symbols = string.ascii_uppercase + string.digits | ||
return ''.join(secrets.choice(symbols) for i in range(32)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For readability it might make sense to define the secret length like OTP_LENGTH = 32
and use that throughout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i was in favour of the simple number and a comment to not confuse further developers thinking that this could be tweaked.
securedrop/models.py
Outdated
""" | ||
Generate an OTP secret of 160 bits encoded base32 | ||
""" | ||
symbols = string.ascii_uppercase + string.digits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
D'oh, my bad - this is 36 symbols. Currently the secret keys being generated use [ascii_upper]+[2.3,4,5,6,7], which makes sense in terms of readability (don't wanna mix up 0 and O or 1 and l).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This plus alembic migration woes covers off the app-test failures in CI.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, this is what pyotp does ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
As for the symbol choice i consider that who speculated on this idea when defining RFC 4648 Base32 didnt really evaluate end users; in my opinion end users wont know anyway that we are not using 0 ad 1 and will confuse O with 0 and 1 with I anyway.
I would suggest to use ABCDEFGHJKLMNPQRSTUVWXYZ23456789 that does not include any of the symbols O 0 I 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In relation to this change i've opened a pull on pyotp so to get to understand if any drawback could exist in this variation: pyauth/pyotp#119
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably the first thing that comes to mind is interoperability with apps - just as an example google authenticator considers 8 and 9 illegal chars in secret keys. If that's the set that pyotp uses it's probably worth sticking to it, so the only ux change is that secrets are longer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch. I was actually expecting this but did not find confirmations in the RFC about this limitation so i did not try.
Thank you @zenmonkeykstop .
I've corrected the patch accordingly.
This pull request introduces 1 alert when merging 4e9735e into eb6f4f8 - view on LGTM.com new alerts:
|
Codecov Report
@@ Coverage Diff @@
## develop #5958 +/- ##
===========================================
- Coverage 85.30% 85.25% -0.05%
===========================================
Files 53 54 +1
Lines 3878 3894 +16
Branches 481 482 +1
===========================================
+ Hits 3308 3320 +12
- Misses 457 461 +4
Partials 113 113
Continue to review full report at Codecov.
|
More work required to remove pyotp altogether
Hey @evilaliv3, after looking at what would be involved in using There will be some additional work on docs and the diff review for the |
Will push a change for typelint errors. |
This pull request introduces 1 alert when merging a8f57b5 into 6df6672 - view on LGTM.com new alerts:
|
This pull request introduces 1 alert when merging b9d4795 into 6df6672 - view on LGTM.com new alerts:
|
This pull request introduces 2 alerts when merging 4b461bd into 6df6672 - view on LGTM.com new alerts:
|
Changes pushed so far:
The heads situation before
Current error status from alembic migrationRight now there will be a few migration related failures as the column size is changed in the original work on the PR.
|
This pull request introduces 2 alerts when merging fe9bf16 into 6df6672 - view on LGTM.com new alerts:
|
This pull request introduces 2 alerts when merging d676621 into 6df6672 - view on LGTM.com new alerts:
|
…rnalists.otp_secret length change
We need to assert the type of the variable for any Optional type.
A merge version is recommended for situations where alembic history has multiple heads, but in this case that was breaking the upgrade/downgrade tests. Since the db changes in question are not yet in prod, it's ok to reorder the history instead - so the otp_secret change has been moved from its own alembic branch to the head of the existing one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Only ran through the dev portion of the test plan, but all that checked out just fine. The VM-based scenarios we can touch on again as part of release QA.
Thanks, @evilaliv3, for submitting this, and for your patience during deep review. Thanks also to @zenmonkeykstop and @kushaldas for giving it lots of attention. 😃
Status
Ready for review
Description of Changes
Resolves #5933
This clean patch already includes revisions as by #5934 and is ready for merge con current devel branch.
\cc @eloquence @zenmonkeykstop
Testing
dev environment:
make dev
journalist
account and an incorrect TOTP code failsjournalist
account can successfully log in using its existing 16-char shared secret to generate a valid TOTP codejournalist
account's TOTP credentials can successfully be updated using the QR code and an authenticator appjournalist
account's TOTP credentials can successfully be updated using the text shared secret and an authenticator appupgrade scenario:
2.0.2
)Deployment
Checklist
If you made changes to the server application code:
make lint
) and tests (make test
) pass in the development containerIf you made non-trivial code changes:
Choose one of the following:
If you added or updated a code dependency:
Choose one of the following: