diff --git a/docs/en/docs/advanced/security/http-basic-auth.md b/docs/en/docs/advanced/security/http-basic-auth.md index da8d9da5c28ca..90c516808fc41 100644 --- a/docs/en/docs/advanced/security/http-basic-auth.md +++ b/docs/en/docs/advanced/security/http-basic-auth.md @@ -34,13 +34,19 @@ Here's a more complete example. Use a dependency to check if the username and password are correct. -For this, use the Python standard module `secrets` to check the username and password: +For this, use the Python standard module `secrets` to check the username and password. -```Python hl_lines="1 11-13" +`secrets.compare_digest()` needs to take `bytes` or a `str` that only contains ASCII characters (the ones in English), this means it wouldn't work with characters like `á`, as in `Sebastián`. + +To handle that, we first convert the `username` and `password` to `bytes` encoding them with UTF-8. + +Then we can use `secrets.compare_digest()` to ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`. + +```Python hl_lines="1 11-21" {!../../../docs_src/security/tutorial007.py!} ``` -This will ensure that `credentials.username` is `"stanleyjobson"`, and that `credentials.password` is `"swordfish"`. This would be similar to: +This would be similar to: ```Python if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"): @@ -50,9 +56,6 @@ if not (credentials.username == "stanleyjobson") or not (credentials.password == But by using the `secrets.compare_digest()` it will be secure against a type of attacks called "timing attacks". -!!! tip - We are comparing `bytes` (`encode("idna")`) instead of `str` with `compare_digest` because it cannot handle strings with non-ascii characters. - ### Timing Attacks But what's a "timing attack"? @@ -105,6 +108,6 @@ That way, using `secrets.compare_digest()` in your application code, it will be After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again: -```Python hl_lines="15-19" +```Python hl_lines="23-27" {!../../../docs_src/security/tutorial007.py!} ``` diff --git a/docs_src/security/tutorial007.py b/docs_src/security/tutorial007.py index 533aa8da089e8..790ee10bc6b1d 100644 --- a/docs_src/security/tutorial007.py +++ b/docs_src/security/tutorial007.py @@ -9,13 +9,17 @@ def get_current_username(credentials: HTTPBasicCredentials = Depends(security)): - correct_username = secrets.compare_digest( - credentials.username.encode("idna"), "stanleyjobson".encode("idna") + current_username_bytes = credentials.username.encode("utf8") + correct_username_bytes = b"stanleyjobson" + is_correct_username = secrets.compare_digest( + current_username_bytes, correct_username_bytes ) - correct_password = secrets.compare_digest( - credentials.password.encode("idna"), "swordfish".encode("idna") + current_password_bytes = credentials.password.encode("utf8") + correct_password_bytes = b"swordfish" + is_correct_password = secrets.compare_digest( + current_password_bytes, correct_password_bytes ) - if not (correct_username and correct_password): + if not (is_correct_username and is_correct_password): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect email or password",