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
반응형