HTTP Basic Auth¶
🌐 Tradução por IA e humanos
Esta tradução foi feita por IA orientada por humanos. 🤝
Ela pode conter erros de interpretação do significado original ou soar pouco natural, etc. 🤖
Você pode melhorar esta traduçãoajudando-nos a orientar melhor o LLM de IA.
Para os casos mais simples, você pode utilizar o HTTP Basic Auth.
No HTTP Basic Auth, a aplicação espera um cabeçalho que contém um usuário e uma senha.
Caso ela não receba, ela retorna um erro HTTP 401 "Unauthorized".
E retorna um cabeçalhoWWW-Authenticate com o valorBasic, e um parâmetro opcionalrealm.
Isso sinaliza ao navegador para mostrar o prompt integrado para um usuário e senha.
Então, quando você digitar o usuário e senha, o navegador os envia automaticamente no cabeçalho.
HTTP Basic Auth Simples¶
- Importe
HTTPBasiceHTTPBasicCredentials. - Crie um "esquema
security" utilizandoHTTPBasic. - Utilize o
securitycom uma dependência em suaoperação de rota. - Isso retorna um objeto do tipo
HTTPBasicCredentials:- Isto contém o
usernamee opasswordenviado.
- Isto contém o
fromtypingimportAnnotatedfromfastapiimportDepends,FastAPIfromfastapi.securityimportHTTPBasic,HTTPBasicCredentialsapp=FastAPI()security=HTTPBasic()@app.get("/users/me")defread_current_user(credentials:Annotated[HTTPBasicCredentials,Depends(security)]):return{"username":credentials.username,"password":credentials.password}🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIfromfastapi.securityimportHTTPBasic,HTTPBasicCredentialsapp=FastAPI()security=HTTPBasic()@app.get("/users/me")defread_current_user(credentials:HTTPBasicCredentials=Depends(security)):return{"username":credentials.username,"password":credentials.password}Quando você tentar abrir a URL pela primeira vez (ou clicar no botão "Executar" na documentação) o navegador vai pedir pelo seu usuário e senha:

Verifique o usuário¶
Aqui está um exemplo mais completo.
Utilize uma dependência para verificar se o usuário e a senha estão corretos.
Para isso, utilize o módulo padrão do Pythonsecrets para verificar o usuário e senha.
Osecrets.compare_digest() necessita receberbytes oustr que possuem apenas caracteres ASCII (os em inglês). Isso significa que não funcionaria com caracteres como oá, como emSebastián.
Para lidar com isso, primeiramente nós convertemos ousername e opassword parabytes, codificando-os com UTF-8.
Então nós podemos utilizar osecrets.compare_digest() para garantir que ocredentials.username é"stanleyjobson", e que ocredentials.password é"swordfish".
importsecretsfromtypingimportAnnotatedfromfastapiimportDepends,FastAPI,HTTPException,statusfromfastapi.securityimportHTTPBasic,HTTPBasicCredentialsapp=FastAPI()security=HTTPBasic()defget_current_username(credentials:Annotated[HTTPBasicCredentials,Depends(security)],):current_username_bytes=credentials.username.encode("utf8")correct_username_bytes=b"stanleyjobson"is_correct_username=secrets.compare_digest(current_username_bytes,correct_username_bytes)current_password_bytes=credentials.password.encode("utf8")correct_password_bytes=b"swordfish"is_correct_password=secrets.compare_digest(current_password_bytes,correct_password_bytes)ifnot(is_correct_usernameandis_correct_password):raiseHTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="Incorrect username or password",headers={"WWW-Authenticate":"Basic"},)returncredentials.username@app.get("/users/me")defread_current_user(username:Annotated[str,Depends(get_current_username)]):return{"username":username}🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
importsecretsfromfastapiimportDepends,FastAPI,HTTPException,statusfromfastapi.securityimportHTTPBasic,HTTPBasicCredentialsapp=FastAPI()security=HTTPBasic()defget_current_username(credentials:HTTPBasicCredentials=Depends(security)):current_username_bytes=credentials.username.encode("utf8")correct_username_bytes=b"stanleyjobson"is_correct_username=secrets.compare_digest(current_username_bytes,correct_username_bytes)current_password_bytes=credentials.password.encode("utf8")correct_password_bytes=b"swordfish"is_correct_password=secrets.compare_digest(current_password_bytes,correct_password_bytes)ifnot(is_correct_usernameandis_correct_password):raiseHTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="Incorrect username or password",headers={"WWW-Authenticate":"Basic"},)returncredentials.username@app.get("/users/me")defread_current_user(username:str=Depends(get_current_username)):return{"username":username}Isso seria parecido com:
ifnot(credentials.username=="stanleyjobson")ornot(credentials.password=="swordfish"):# Return some error...Porém, ao utilizar osecrets.compare_digest(), isso estará seguro contra um tipo de ataque chamado "timing attacks" (ataques de temporização).
Ataques de Temporização¶
Mas o que é um "timing attack" (ataque de temporização)?
Vamos imaginar que alguns invasores estão tentando adivinhar o usuário e a senha.
E eles enviam uma requisição com um usuáriojohndoe e uma senhalove123.
Então o código Python em sua aplicação seria equivalente a algo como:
if"johndoe"=="stanleyjobson"and"love123"=="swordfish":...Mas no exato momento que o Python compara o primeiroj emjohndoe contra o primeiros emstanleyjobson, ele retornaráFalse, porque ele já sabe que aquelas duas strings não são a mesma, pensando que "não existe a necessidade de desperdiçar mais poder computacional comparando o resto das letras". E a sua aplicação dirá "Usuário ou senha incorretos".
Então os invasores vão tentar com o usuáriostanleyjobsox e a senhalove123.
E a sua aplicação faz algo como:
if"stanleyjobsox"=="stanleyjobson"and"love123"=="swordfish":...O Python terá que comparar todo ostanleyjobso tanto emstanleyjobsox como emstanleyjobson antes de perceber que as strings não são a mesma. Então isso levará alguns microssegundos a mais para retornar "Usuário ou senha incorretos".
O tempo para responder ajuda os invasores¶
Neste ponto, ao perceber que o servidor demorou alguns microssegundos a mais para enviar o retorno "Usuário ou senha incorretos", os invasores irão saber que eles acertaramalguma coisa, algumas das letras iniciais estavam certas.
E eles podem tentar de novo sabendo que provavelmente é algo mais parecido comstanleyjobsox do que comjohndoe.
Um ataque "profissional"¶
Claro, os invasores não tentariam tudo isso de forma manual, eles escreveriam um programa para fazer isso, possivelmente com milhares ou milhões de testes por segundo. E obteriam apenas uma letra a mais por vez.
Mas fazendo isso, em alguns minutos ou horas os invasores teriam adivinhado o usuário e senha corretos, com a "ajuda" da nossa aplicação, apenas usando o tempo levado para responder.
Corrija com osecrets.compare_digest()¶
Mas em nosso código já estamos utilizando osecrets.compare_digest().
Resumindo, levará o mesmo tempo para compararstanleyjobsox comstanleyjobson do que compararjohndoe comstanleyjobson. E o mesmo para a senha.
Deste modo, ao utilizarsecrets.compare_digest() no código de sua aplicação, ela estará a salvo contra toda essa gama de ataques de segurança.
Retorne o erro¶
Após detectar que as credenciais estão incorretas, retorne umHTTPException com o status 401 (o mesmo retornado quando nenhuma credencial foi informada) e adicione o cabeçalhoWWW-Authenticate para fazer com que o navegador mostre o prompt de login novamente:
importsecretsfromtypingimportAnnotatedfromfastapiimportDepends,FastAPI,HTTPException,statusfromfastapi.securityimportHTTPBasic,HTTPBasicCredentialsapp=FastAPI()security=HTTPBasic()defget_current_username(credentials:Annotated[HTTPBasicCredentials,Depends(security)],):current_username_bytes=credentials.username.encode("utf8")correct_username_bytes=b"stanleyjobson"is_correct_username=secrets.compare_digest(current_username_bytes,correct_username_bytes)current_password_bytes=credentials.password.encode("utf8")correct_password_bytes=b"swordfish"is_correct_password=secrets.compare_digest(current_password_bytes,correct_password_bytes)ifnot(is_correct_usernameandis_correct_password):raiseHTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="Incorrect username or password",headers={"WWW-Authenticate":"Basic"},)returncredentials.username@app.get("/users/me")defread_current_user(username:Annotated[str,Depends(get_current_username)]):return{"username":username}🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
importsecretsfromfastapiimportDepends,FastAPI,HTTPException,statusfromfastapi.securityimportHTTPBasic,HTTPBasicCredentialsapp=FastAPI()security=HTTPBasic()defget_current_username(credentials:HTTPBasicCredentials=Depends(security)):current_username_bytes=credentials.username.encode("utf8")correct_username_bytes=b"stanleyjobson"is_correct_username=secrets.compare_digest(current_username_bytes,correct_username_bytes)current_password_bytes=credentials.password.encode("utf8")correct_password_bytes=b"swordfish"is_correct_password=secrets.compare_digest(current_password_bytes,correct_password_bytes)ifnot(is_correct_usernameandis_correct_password):raiseHTTPException(status_code=status.HTTP_401_UNAUTHORIZED,detail="Incorrect username or password",headers={"WWW-Authenticate":"Basic"},)returncredentials.username@app.get("/users/me")defread_current_user(username:str=Depends(get_current_username)):return{"username":username}






