HTTP Basic Auth¶
🌐 Traducción por IA y humanos
Esta traducción fue hecha por IA guiada por humanos. 🤝
Podría tener errores al interpretar el significado original, o sonar poco natural, etc. 🤖
Puedes mejorar esta traducciónayudándonos a guiar mejor al LLM de IA.
Para los casos más simples, puedes usar HTTP Basic Auth.
En HTTP Basic Auth, la aplicación espera un header que contiene un nombre de usuario y una contraseña.
Si no lo recibe, devuelve un error HTTP 401 "Unauthorized".
Y devuelve un headerWWW-Authenticate con un valor deBasic, y un parámetrorealm opcional.
Eso le dice al navegador que muestre el prompt integrado para un nombre de usuario y contraseña.
Luego, cuando escribes ese nombre de usuario y contraseña, el navegador los envía automáticamente en el header.
Simple HTTP Basic Auth¶
- Importa
HTTPBasicyHTTPBasicCredentials. - Crea un "esquema de
security" usandoHTTPBasic. - Usa ese
securitycon una dependencia en tupath operation. - Devuelve un objeto de tipo
HTTPBasicCredentials: - Contiene el
usernameypasswordenviados.
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}Cuando intentas abrir la URL por primera vez (o haces clic en el botón "Execute" en la documentación) el navegador te pedirá tu nombre de usuario y contraseña:

Revisa el nombre de usuario¶
Aquí hay un ejemplo más completo.
Usa una dependencia para comprobar si el nombre de usuario y la contraseña son correctos.
Para esto, usa el módulo estándar de Pythonsecrets para verificar el nombre de usuario y la contraseña.
secrets.compare_digest() necesita tomarbytes o unstr que solo contenga caracteres ASCII (los carácteres en inglés), esto significa que no funcionaría con caracteres comoá, como enSebastián.
Para manejar eso, primero convertimos elusername ypassword abytes codificándolos con UTF-8.
Luego podemos usarsecrets.compare_digest() para asegurar quecredentials.username es"stanleyjobson", y quecredentials.password es"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}Esto sería similar a:
ifnot(credentials.username=="stanleyjobson")ornot(credentials.password=="swordfish"):# Devuelve algún error...Pero al usarsecrets.compare_digest() será seguro contra un tipo de ataques llamados "timing attacks".
Timing attacks¶
¿Pero qué es un "timing attack"?
Imaginemos que algunos atacantes están tratando de adivinar el nombre de usuario y la contraseña.
Y envían un request con un nombre de usuariojohndoe y una contraseñalove123.
Entonces el código de Python en tu aplicación equivaldría a algo como:
if"johndoe"=="stanleyjobson"and"love123"=="swordfish":...Pero justo en el momento en que Python compara la primeraj enjohndoe con la primeras enstanleyjobson, devolveráFalse, porque ya sabe que esas dos strings no son iguales, pensando que "no hay necesidad de gastar más computación comparando el resto de las letras". Y tu aplicación dirá "Nombre de usuario o contraseña incorrectos".
Pero luego los atacantes prueban con el nombre de usuariostanleyjobsox y contraseñalove123.
Y el código de tu aplicación hace algo así como:
if"stanleyjobsox"=="stanleyjobson"and"love123"=="swordfish":...Python tendrá que comparar todostanleyjobso en ambosstanleyjobsox ystanleyjobson antes de darse cuenta de que ambas strings no son las mismas. Así que tomará algunos microsegundos extra para responder "Nombre de usuario o contraseña incorrectos".
El tiempo de respuesta ayuda a los atacantes¶
En ese punto, al notar que el servidor tardó algunos microsegundos más en enviar el response "Nombre de usuario o contraseña incorrectos", los atacantes sabrán que acertaron enalgo, algunas de las letras iniciales eran correctas.
Y luego pueden intentar de nuevo sabiendo que probablemente es algo más similar astanleyjobsox que ajohndoe.
Un ataque "profesional"¶
Por supuesto, los atacantes no intentarían todo esto a mano, escribirían un programa para hacerlo, posiblemente con miles o millones de pruebas por segundo. Y obtendrían solo una letra correcta adicional a la vez.
Pero haciendo eso, en algunos minutos u horas, los atacantes habrían adivinado el nombre de usuario y la contraseña correctos, con la "ayuda" de nuestra aplicación, solo usando el tiempo tomado para responder.
Arréglalo consecrets.compare_digest()¶
Pero en nuestro código estamos usando realmentesecrets.compare_digest().
En resumen, tomará el mismo tiempo compararstanleyjobsox constanleyjobson que compararjohndoe constanleyjobson. Y lo mismo para la contraseña.
De esa manera, usandosecrets.compare_digest() en el código de tu aplicación, será seguro contra todo este rango de ataques de seguridad.
Devuelve el error¶
Después de detectar que las credenciales son incorrectas, regresa unHTTPException con un código de estado 401 (el mismo que se devuelve cuando no se proporcionan credenciales) y agrega el headerWWW-Authenticate para que el navegador muestre el prompt de inicio de sesión nuevamente:
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}






