Fastapi
fastapi swagger 페이지 기본 인증 추가
Prower
2022. 7. 16. 02:23
728x90
반응형
- fastapi는 어플리케이션을 실행하는 시점에 swagger 페이지를 기본으로 생성해준다.
- controller를 등록하면 해당 endpoint에 대한 spec을 fastapi에서 자동으로 swagger 페이지에 등록해주며, 다양한 설정 또한 가능하다.
- 다만, swagger 페이지 자체를 fastapi에서 관리하여 커스터마이징이 어렵다.
- swagger 페이지에 http 기본 인증을 추가하는 방법을 알아본다.
1. 기본 인증을 적용한 endpoint 생성
- fastapi에서 http basic auth를 위한 HTTPBasic, HTTPBasicCredentials 라이브러리가 있다.
- 두 라이브러리를 사용하여 기본 인증 구현
from fastapi.security import HTTPBasic, HTTPBasicCredentials
security = HTTPBasic()
@app.get("/test")
def doc(credentials: HTTPBasicCredentials = Depends(security)):
user = credentials.username == "testuser"
password = credentials.password == "testpassword"
if user and password:
return "pass"
raise HTTPException(
status_code=401,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Basic"},
)
- 기본 인증을 수행하는 HTTPBasic 객체를 생성하고 이를 controller에 주입하여 기본 인증을 구현하는 endpoint 구현
2. 인증 정보 확인 로직 수정
- 다만 저렇게 단순 비교 방식으로 인증을 진행하면 timing attack 이 가능함
- 간단하게 정리하면, python 특성상 "testusss" == "testuser" 와 "texxxxx" == "testuser" 를 비교하는 시간에 차이가 있어 비밀값에 대한 길이를 유추하여 공격하는 방법이다.
- 인증 정보 확인을 secrets.compare_digest() 메서드를 사용하여 timing attack을 방지하고, 안전하게 진행
- 길이 비교에 대한 시간을 조정하여 timing attack 방지. 참고
import secrets
@app.get("/test")
def doc(credentials: HTTPBasicCredentials = Depends(security)):
user = secrets.compare_digest(credentials.username, "testing")
password = secrets.compare_digest(credentials.password, "12341234aa")
...
3. 인증 성공시 기본 swagger 페이지 반환
- fastapi 자체에 기본 swagger 페이지를 반환하는 기능이 있다. Docs URLs
from fastapi.openapi.docs import get_swagger_ui_html
@app.get("/test")
def doc(credentials: HTTPBasicCredentials = Depends(security)):
...
if user and password:
return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")
...
- 기본 openapi 설정이 관리되는 openapi.json 파일이 기본적으로 존재하고 이를 사용하여 swagger 페이지 반환
4. 기본 swagger 페이지 제거
- fastapi를 실행시 docs endpoint로 기본 swagger 페이지가 생성된다.
- 해당 페이지를 제공하지 않도록 설정
app = FastAPI(docs_url=None)
5. endpoint 수정
- 기본 swagger 페이지로 제공되던 docs 를 인증이 포함된 swagger 페이지를 제공하도록 라우팅한다.
- 해당 endpoint는 swagger 설정에서 제외시킨다.
@app.get("/docs", include_in_schema=False)
def doc(credentials: HTTPBasicCredentials = Depends(security)):
...
결과
import secrets
from fastapi import FastAPI, HTTPException, Depends
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI(docs_url=None)
security = HTTPBasic()
@app.get("/docs", include_in_schema=False)
def doc(credentials: HTTPBasicCredentials = Depends(security)):
user = secrets.compare_digest(credentials.username, "testuser")
password = secrets.compare_digest(credentials.password, "testpassword")
if user and password:
return get_swagger_ui_html(openapi_url="openapi.json", title="docs")
raise HTTPException(
status_code=401,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Basic"},
)
728x90
반응형