포스트

웹 인증 http-only

요즘 fastapi로 인증 관련 백엔드 서버를 만들고 있습니다.

인증은 서비스에 있어서 매우 중요한 요소입니다.

특히 무료 서비스가 아닌 유료 서비스를 하게 되면 결제라는 민감한 이슈가 있기 때문에

사용자 인증이 잘 되고 유지되어야 합니다.

기본적으로 프론트가 서버에 로그인을 하면 서버는 토큰을 프론트에게 주게 됩니다.

그 토큰은 쉽게 말하면 입장권이죠.

입장권은 누가 훔쳐가서 쓰면 곤란하기 때문에 여러 보안 장치들을 마련해서 못 훔쳐가게 하거나, 훔쳐가더라도 오래 못 쓰게 합니다.

오래 못 쓰게 하는 간단한 방법으로 유효기간을 두는 방법이 있죠.

한편, 못 훔쳐가게 하는 방법으로, 쉽게 훔쳐가는 수법 중 하나인, javascript 명령어로 토큰을 훔쳐가는 경우가 없도록 하는 방지책이 있습니다.

토큰을 예를 들어 입장권을 목에 걸고 다닌다던가, 보이기 쉬운 곳에 붙이고 다니면 도둑이 떼서 훔쳐가서 쓰겠죠.

만약 도둑이 “console.log(document.cookie)” 이런 식으로 javascript 명령어를 보내면 토큰은 쉽게 탈취됩니다. 이런걸 XXR(Cross-Site Scripting) 공격이라고 합니다.

그래서 웹브라우저로 접속하는 사용자에게는 토큰을 그렇게 대충 보관하지 못하게 해야 합니다.

그 방법으로 http-only 쿠키라는 것이 있습니다.

javascript 상에 쿠키가 드러나지 않고, 그래서 코드 상으로 읽어낼 수 없는 겁니다.

그럼 사용자는 접근할 수 없는 쿠키를 어떻게 사용하느냐?

그건 걱정하지 않아도 됩니다.

웹브라우저가 알아서 보관하고 알아서 사용합니다.

예를 들어 backend 서버에 auth/login api 요청을 보내면, 서버가 로그인이 성공했음을 알리며 인증 토큰을 보내줍니다.frontend는 그걸 javascript는 읽을 수 없도록 안전하게 보관합니다.

그리고 다시 사용자가 인증된 사용자만 부를 수 있는 api를 부를 때면 자동으로 웹브라우저는 보관하고 있던 인증 토큰을 실어서 같이 backend 서버에 보냅니다.

Fastapi에서는 다음과 같이 해주면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@router.post("/login")
async def login(request: LoginRequest, req: Request, response: Response):
...
        cookie_settings = {
            "key": "access_token",
            "value": access_token,
            "httponly": True,
            "max_age": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60,
            "path": "/",
        }

        # 프로덕션 모드에서는 secure=True, samesite="none"
        if settings.PRODUCTION_MODE:
            cookie_settings.update({"secure": True, "samesite": "none"})
        # 개발 환경에서는 secure=False, samesite=None
        else:
            cookie_settings.update(
                {
                    "secure": False,
                    # 개발 환경에서는 samesite 속성을 지정하지 않음
                    # 브라우저 기본값인 Lax가 적용됨
                }
            )

        # 쿠키 설정 적용
        response.set_cookie(**cookie_settings)

...

“httponly”: True

잘 적용됬는지 확인해보려면 프론트엔드의 로그인 성공화면에서 개발자모드(F12)를 눌러서 로컬스토리지에 있는 쿠키를 확인해보면 됩니다.

제 경우는 access_token 의 변수명으로, http-only 에 체크가 되어 있습니다.

더 쉽게는 editthiscookie라는 크롬확장프로그램을 받아서 볼 수도 있습니다.

한편, 같은 인증 서버를 웹과 앱 클라이언트에서 모두 쓰고 싶을 수도 있겠습니다.

앱에서는 예를 들어 플러터 앱에서는 httpOnly를 쓸 수 없고, 대신 local storage secure라는 패키지를 사용하면 인증토큰이 앱에서 노출되지 않고 숨겨진다고 합니다. httpOnly와 동일한 목적입니다.

#XXR, #Cross_site_scripting, #http-only, #httpOnly

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.