현재 백엔드 API를 만들어 프론트엔드에게 공유해야하고 commit, push후 깃허브에 올라가야 github action을 통해 aws에 올라가게 한 상황입니다. 이러면 fastapi에 대해 잘 알고있는 사람은 마음대로 docs에 접근하게 됩니다. 그렇다고 docs를 노출하지 않으면 협업을 위한 api문서를 따로 만들어야 하는 번거로움이 생깁니다.
원하는 사람만 접근하도록 권한을 설정해야하는 처음에는 쿠키, 로컬스토리지에 값을 넣어 사용하는 방법을 생각했었는데 이것도 프론트엔드에서 귀찮게 설정해야하고 보안상 취약하기 때문에 사용할 수 없었습니다. 그렇게 문서를 찾아보던 중 HTTP Basic auth를 알게되었습니다.
def get_admin(credentials: Annotated[HTTPBasicCredentials, Depends(security)]):
correct_username = secrets.compare_digest(credentials.username, "user")
correct_password = secrets.compare_digest(credentials.password, "password")
if not (correct_username and correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Basic"},
)
return ""
@app.get("/docs", include_in_schema=False)
async def get_documentation(admin: str = Depends(get_admin)):
return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")
@app.get("/openapi.json", include_in_schema=False)
async def openapi(admin: str = Depends(get_admin)):
return get_openapi(title=app.title, version=app.version, routes=app.routes)
위 코드를 넣으면 간단한 인증을 하고 docs를 보도록 합니다.
여기서 백엔드에서 정의한 user, password를 넣고 인증을 완료하면 정상적으로 들어갈 수 있습니다.
위 코드에서 하드코딩된 유저이름, 비밀번호만 수정해주어야하는데 따로 환경변수로 빼내거나 db에 따로 super_user로 저장한 것으로 비교하는 방법을 사용해서 구성하면 됩니다.
FastAPI 공식문서에서 timing attack에 대해 설명해주는데 한번 보고 가는게 좋아보입니다. 단순 FastAPI뿐만 아니라 다른 언어를 코딩할 때도 해당되는 내용이고 기본 파이썬 보안 관련문제도 해당되는 것 같습니다.
# 1
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
...
# 2
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
...
이런 식으로 코딩이 되어 있을 때 1의 경우 가장 앞부분부터 틀렸기 빠르게 오류가 반환되고 2번의 경우는 1번보다는 느리게 오류가 반환됩니다. 이런 미세한 시간차이를 이용해 수백번 반복적으로 시도하며 타이밍을 분석해 username과 password를 알아낼 수 있다고 합니다.
그래서 코드에는 ==을 사용해 비교하는 것이 아닌 secrets.compare_digest 메서드를 이용해 비교해야 합니다.
secrets.compare_digest를 사용하면 1, 2번 비교 시간이 동일하게 반환되며(비밀번호도 마찬가지) 이러한 범위의 보안공격에 대해 안전하다고 합니다.
API 문서를 보호하는 방법 (github issue)
FastAPI 공식 문서 (http-basic-auth)
즐겁게 읽었습니다. 유용한 정보 감사합니다.