Browse Source

📝 Update docs for JWT to prevent timing attacks (#14908)

pull/14911/head
Sebastián Ramírez 5 months ago
committed by GitHub
parent
commit
d11f820ac3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 12
      docs/en/docs/tutorial/security/oauth2-jwt.md
  2. 3
      docs_src/security/tutorial004_an_py310.py
  3. 3
      docs_src/security/tutorial004_py310.py
  4. 3
      docs_src/security/tutorial005_an_py310.py
  5. 3
      docs_src/security/tutorial005_py310.py

12
docs/en/docs/tutorial/security/oauth2-jwt.md

@ -116,7 +116,11 @@ And another utility to verify if a received password matches the hash stored.
And another one to authenticate and return a user. And another one to authenticate and return a user.
{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,56:57,60:61,70:76] *} {* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *}
When `authenticate_user` is called with a username that doesn't exist in the database, we still run `verify_password` against a dummy hash.
This ensures the endpoint takes roughly the same amount of time to respond whether the username is valid or not, preventing **timing attacks** that could be used to enumerate existing usernames.
/// note /// note
@ -152,7 +156,7 @@ Define a Pydantic Model that will be used in the token endpoint for the response
Create a utility function to generate a new access token. Create a utility function to generate a new access token.
{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,79:87] *} {* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *}
## Update the dependencies { #update-the-dependencies } ## Update the dependencies { #update-the-dependencies }
@ -162,7 +166,7 @@ Decode the received token, verify it, and return the current user.
If the token is invalid, return an HTTP error right away. If the token is invalid, return an HTTP error right away.
{* ../../docs_src/security/tutorial004_an_py310.py hl[90:107] *} {* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *}
## Update the `/token` *path operation* { #update-the-token-path-operation } ## Update the `/token` *path operation* { #update-the-token-path-operation }
@ -170,7 +174,7 @@ Create a `timedelta` with the expiration time of the token.
Create a real JWT access token and return it. Create a real JWT access token and return it.
{* ../../docs_src/security/tutorial004_an_py310.py hl[118:133] *} {* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *}
### Technical details about the JWT "subject" `sub` { #technical-details-about-the-jwt-subject-sub } ### Technical details about the JWT "subject" `sub` { #technical-details-about-the-jwt-subject-sub }

3
docs_src/security/tutorial004_an_py310.py

@ -48,6 +48,8 @@ class UserInDB(User):
password_hash = PasswordHash.recommended() password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI() app = FastAPI()
@ -70,6 +72,7 @@ def get_user(db, username: str):
def authenticate_user(fake_db, username: str, password: str): def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username) user = get_user(fake_db, username)
if not user: if not user:
verify_password(password, DUMMY_HASH)
return False return False
if not verify_password(password, user.hashed_password): if not verify_password(password, user.hashed_password):
return False return False

3
docs_src/security/tutorial004_py310.py

@ -47,6 +47,8 @@ class UserInDB(User):
password_hash = PasswordHash.recommended() password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI() app = FastAPI()
@ -69,6 +71,7 @@ def get_user(db, username: str):
def authenticate_user(fake_db, username: str, password: str): def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username) user = get_user(fake_db, username)
if not user: if not user:
verify_password(password, DUMMY_HASH)
return False return False
if not verify_password(password, user.hashed_password): if not verify_password(password, user.hashed_password):
return False return False

3
docs_src/security/tutorial005_an_py310.py

@ -60,6 +60,8 @@ class UserInDB(User):
password_hash = PasswordHash.recommended() password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
scopes={"me": "Read information about the current user.", "items": "Read items."}, scopes={"me": "Read information about the current user.", "items": "Read items."},
@ -85,6 +87,7 @@ def get_user(db, username: str):
def authenticate_user(fake_db, username: str, password: str): def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username) user = get_user(fake_db, username)
if not user: if not user:
verify_password(password, DUMMY_HASH)
return False return False
if not verify_password(password, user.hashed_password): if not verify_password(password, user.hashed_password):
return False return False

3
docs_src/security/tutorial005_py310.py

@ -59,6 +59,8 @@ class UserInDB(User):
password_hash = PasswordHash.recommended() password_hash = PasswordHash.recommended()
DUMMY_HASH = password_hash.hash("dummypassword")
oauth2_scheme = OAuth2PasswordBearer( oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="token", tokenUrl="token",
scopes={"me": "Read information about the current user.", "items": "Read items."}, scopes={"me": "Read information about the current user.", "items": "Read items."},
@ -84,6 +86,7 @@ def get_user(db, username: str):
def authenticate_user(fake_db, username: str, password: str): def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username) user = get_user(fake_db, username)
if not user: if not user:
verify_password(password, DUMMY_HASH)
return False return False
if not verify_password(password, user.hashed_password): if not verify_password(password, user.hashed_password):
return False return False

Loading…
Cancel
Save