사전 재인증
개념

재인증

gabury1고친 사람 github-actions[bot]

재인증은 이미 로그인한 사용자에게 서비스가 본인 확인을 한 번 더 요구하는 일입니다. 비밀번호를 바꾸려는 사용자에게 지금 비밀번호부터 넣으라고 하는 화면이 흔한 모습입니다. 로그인 상태만 손에 넣은 사람이 계정을 마음대로 쓰지 못하게 막습니다.

쉽고 빠른 이해

재인증은 로그인해 둔 사용자에게 본인인지 다시 묻는 일입니다. 로그인한 채로 비밀번호나 이메일을 바꾸려 하면 비밀번호를 한 번 더 넣으라는 화면이 뜹니다.

로그인한 뒤에는 서버가 요청마다 비밀번호를 묻지 않습니다. 브라우저가 들고 다니는 로그인 증거 하나만 보고 주인이라고 믿습니다. 그 증거를 남이 훔치거나 로그인해 둔 컴퓨터를 남이 쓰면, 그 사람도 주인처럼 움직입니다.

재인증은 이렇게 돕니다.

  1. 로그인할 때 본인을 확인한 시각을 적어 둔다
  2. 되돌리기 어려운 요청이 오면 그 시각에서 얼마나 지났는지 본다
  3. 너무 오래됐으면 요청을 멈추고 비밀번호를 다시 받은 뒤 이어서 처리한다

대가는 번거로움입니다. 너무 자주 물으면 사용자가 지칩니다. 너무 드물게 물으면 막는 힘이 약해집니다. 그래서 되돌리기 어려운 동작에만 겁니다.

상세

회사 출입증을 목에 건 직원은 로비와 사무실을 그냥 드나듭니다. 금고실 문 앞에서는 출입증을 댄 다음 비밀번호를 한 번 더 누릅니다. 떨어진 출입증을 주운 사람은 사무실까지는 들어가도 금고실 문은 못 엽니다.

재인증(reauthentication)은 이미 인증을 마친 사용자에게 본인 확인을 다시 요구하는 일입니다. 인증은 요청을 보낸 사람이 스스로 밝힌 그 사람이 맞는지 확인하는 일입니다. 아이디와 비밀번호로 로그인하는 것이 가장 흔한 인증입니다.

로그인 상태가 이어지는 방식

서버가 로그인한 사용자를 어떻게 알아보는지부터 봅니다. 재인증이 왜 필요한지가 거기서 드러납니다.

로그인이 끝나면 서버는 요청마다 비밀번호를 다시 묻지 않습니다. 대신 로그인했다는 증거로 값을 하나 내줍니다. 이 값을 토큰이라고 부릅니다. 브라우저나 앱은 그 뒤 모든 요청에 토큰을 실어 보냅니다.

로그인 상태를 이어 가는 방식 하나는 세션입니다. 세션은 서버가 로그인한 사용자의 정보를 자기 쪽에 보관하는 방식입니다. 토큰으로는 그 정보를 찾는 번호만 내줍니다. 서버는 번호를 받으면 보관해 둔 정보를 찾아 누구인지 압니다.

브라우저는 이 번호를 쿠키에 담아 둡니다. 쿠키는 서버가 브라우저에 맡겨 두는 작은 값입니다. 브라우저는 같은 서버로 가는 요청마다 쿠키를 자동으로 실어 보냅니다. 그래서 사용자가 번호를 따로 챙기지 않아도 로그인이 이어집니다.

다른 방식은 액세스 토큰입니다. 서버가 누구에게 무엇을 허락했는지를 문자열에 담아 서명합니다. 그 문자열을 토큰으로 내줍니다. 앱은 요청마다 이 문자열을 함께 보냅니다.

어느 방식이든 약점은 같습니다. 서버는 토큰을 들고 온 요청을 주인이 보낸 것으로 믿습니다. 토큰을 들고 있는 사람이 누구인지는 따지지 않습니다.

재인증은 이 믿음에 한계를 긋습니다. 확인한 지 오래됐거나 되돌리기 어려운 요청이면 토큰만으로는 모자라다고 봅니다. 그때 본인 확인을 다시 받습니다.

재인증이 막는 공격

토큰이 남의 손에 넘어가는 경우는 크게 셋입니다. 재인증은 셋 모두에 같은 방식으로 맞섭니다.

첫째는 로그인해 둔 채 두고 간 컴퓨터입니다. 공용 컴퓨터에서 로그아웃하지 않고 떠나면 다음 사람이 그 브라우저를 씁니다. 브라우저에 토큰이 남아 있으니 서버는 다음 사람을 주인으로 봅니다.

둘째는 토큰을 훔치는 공격입니다. 남의 토큰을 빼내 자기 브라우저에 넣고 주인 행세를 하는 것을 세션 하이재킹이라고 합니다. 공격자는 토큰만 가졌지 비밀번호는 모릅니다.

셋째는 사용자의 브라우저를 속여 요청을 보내게 하는 공격입니다. 요청이 진짜 사용자의 브라우저에서 나가므로 토큰도 함께 실립니다. 서버가 보기에는 주인이 보낸 요청과 다르지 않습니다.

그런 공격 하나가 클릭재킹입니다. 공격자는 진짜 사이트를 투명하게 만들어 자기 화면 위에 겹쳐 둡니다. 사용자는 눈에 보이는 버튼을 누른다고 생각합니다. 그런데 손가락이 닿는 것은 투명하게 덮인 진짜 사이트의 버튼입니다.

다른 하나는 크로스 사이트 요청 위조입니다. 사용자가 공격자의 사이트를 여는 순간 그 사이트가 몰래 진짜 사이트로 요청을 쏩니다. 이 요청에 토큰이 실리면 서버는 주인이 보낸 것으로 받아들입니다.

세 경우 모두 공격자에게 없는 것이 하나 있습니다. 주인만 아는 비밀번호나 주인만 가진 기기입니다. 재인증은 그것을 요구해서 토큰만 가진 사람을 멈춥니다.

재인증을 요구하는 때

서비스가 재인증을 요구하는 때는 크게 셋입니다. 아래 표는 때마다 예를 하나씩 보입니다.

때 예
로그인한 지 오래됐을 때 세션 기한이 지나 로그인 화면으로 돌아간다
되돌리기 어려운 동작 앞 비밀번호·이메일 변경, 결제, 계정 삭제
접속이 평소와 다를 때 처음 보는 기기나 평소와 먼 지역에서 토큰이 들어온다

첫 줄은 시간으로 판단합니다. 서버는 세션에 기한을 둡니다. 기한이 지나면 토큰을 버립니다. 사용자는 처음부터 다시 로그인해야 합니다.

기한을 재는 방법은 둘입니다. 첫째는 마지막 요청에서부터 재는 유휴 만료입니다. 30분 동안 요청이 하나도 없으면 로그인이 끊기는 식입니다. 사용자가 컴퓨터를 두고 떠난 뒤 남이 이어 쓰는 것을 막아 줍니다.

둘째는 로그인한 순간부터 재는 절대 만료입니다. 계속 쓰고 있어도 로그인하고 12시간이 지나면 끊기는 식입니다. 토큰을 훔친 사람이 요청을 계속 보내 로그인을 끝없이 늘리는 것을 막아 줍니다.

둘째 줄은 동작이 무엇을 바꾸는지로 판단합니다. 가장 먼저 꼽히는 것은 계정의 주인을 바꿀 수 있는 동작입니다. 공격자가 비밀번호나 이메일을 바꾸면 진짜 주인이 다시 들어올 길이 막힙니다. 이렇게 계정을 빼앗기는 것을 계정 탈취라고 합니다.

재인증을 동작의 무게를 기준으로 거는 방식을 스텝업 인증이라고 합니다. 평소에는 토큰만 봅니다. 되돌리기 어려운 동작 앞에서만 확인을 한 단계 올립니다. 계단을 한 칸 오르듯 확인을 세게 한다는 뜻의 이름입니다.

셋째 줄은 접속 정황으로 판단합니다. 같은 토큰이 갑자기 처음 보는 기기에서 들어오면 토큰을 도둑맞았을 수 있습니다. 재인증을 이런 접속 정황을 기준으로 거는 방식을 위험 기반 인증이라고 합니다. 여러 신호를 모아 다시 확인할지, 얼마나 세게 확인할지를 정합니다.

다시 확인하는 수단

재인증에서 받는 증거에는 기준이 하나 있습니다. 토큰을 훔친 사람도 낼 수 있는 증거라면 다시 묻는 의미가 없습니다.

가장 흔한 수단은 비밀번호입니다. 토큰을 훔친 사람은 대개 비밀번호를 모릅니다. 사용자가 직접 쳐야 하므로 브라우저를 속이는 공격도 흉내 내기 어렵습니다.

더 세게 확인하려면 다중 인증을 씁니다. 다중 인증은 비밀번호에 더해 휴대폰 앱에 뜬 숫자처럼 다른 종류의 증거를 하나 더 받는 방법입니다. 비밀번호까지 새어 나갔어도 주인의 휴대폰이 없으면 통과하지 못합니다.

패스키는 비밀번호 대신 기기 안에 저장한 열쇠로 로그인하는 방식입니다. 패스키를 쓰는 서비스는 지문이나 얼굴 인식 한 번으로 다시 확인합니다. 사용자가 아무것도 치지 않아도 되므로 재인증의 번거로움이 줄어듭니다.

서버가 재인증을 판단하는 법

서버 코드가 재인증을 판단하는 열쇠는 「언제 마지막으로 본인을 확인했나」입니다. 로그인에 성공하면 서버는 그 시각을 세션에 함께 적어 둡니다. 판단은 아래 그림처럼 두 번 갈립니다.

flowchart TD
    A["요청이 들어온다"] --> B{"되돌리기 어려운 동작인가"}
    B -->|아니오| E["요청을 처리한다"]
    B -->|예| C{"마지막 확인이 허용 시간 안인가"}
    C -->|예| E
    C -->|아니오| D["비밀번호를 다시 받는다"]
    D -->|맞으면| E

첫 갈림에서 되돌리기 쉬운 요청은 확인 없이 지나갑니다. 둘째 갈림에서는 적어 둔 시각을 꺼내 지금과 견줍니다. 되돌리기 어려운 동작이라도 방금 확인했으면 다시 묻지 않습니다.

허용 시간을 5분으로 잡았는데 마지막 확인이 15분 전이었다고 해 봅시다. 서버 코드는 이렇게 판단합니다.

Python
max_age = 5 * 60                  # 300
age = now - session["auth_time"]  # 900
if age > max_age:                 # True
    return ask_password_again()

비교가 참이므로 서버는 변경을 미루고 비밀번호 입력 화면을 돌려줍니다. 비밀번호가 맞으면 서버는 적어 둔 시각을 지금으로 고칩니다. 그리고 멈춰 둔 동작을 이어서 처리합니다.

로그인을 외부에 맡긴 서비스

로그인을 직접 하지 않고 다른 서비스에 맡기는 경우도 있습니다. 그런 서비스는 재인증도 로그인을 맡긴 쪽에 부탁합니다.

OpenID Connect는 로그인을 다른 서비스에 맡기고 그 결과를 받아 오는 표준입니다. 어느 사이트에서 다른 회사 계정으로 로그인하는 버튼을 누르면 그 회사의 로그인 화면이 뜹니다. 이런 버튼 뒤에서 이 표준이 도는 경우가 많습니다.

로그인을 맡아 주는 쪽을 제공자라고 부릅니다. 앞의 예에서는 계정을 가진 그 회사가 제공자입니다. 서비스는 비밀번호를 직접 받지 않고 제공자가 확인해 준 결과만 받습니다.

사용자를 제공자의 로그인 화면으로 보낼 때 서비스는 조건을 붙입니다. 「이미 로그인돼 있어도 다시 확인해 달라」거나 「몇 분 안에 확인한 것이어야 한다」는 조건입니다. 제공자는 확인을 마친 뒤 결과를 ID 토큰(Identity Token, 신원 토큰)에 담아 돌려줍니다.

ID 토큰은 누가 언제 로그인했는지를 제공자가 서명해 담은 토큰입니다. 서비스는 그 안에 적힌 마지막 확인 시각을 읽어 부탁한 시간 안인지 직접 검사합니다. 제공자가 조건대로 다시 확인했다는 증거는 돌아온 토큰뿐이기 때문입니다.

재인증을 거는 범위

재인증을 어디에 걸지는 번거로움과 막는 힘을 견주어 정합니다. 재인증은 사용자의 흐름을 끊습니다. 너무 자주 물으면 사용자가 지칩니다. 너무 드물게 물으면 막는 힘이 약해집니다.

그래서 모든 요청에 걸지 않습니다. 계정의 주인을 바꿀 수 있는 동작, 돈이 나가는 동작, 되돌릴 수 없는 삭제에 겁니다. 글 목록을 보거나 댓글을 다는 것처럼 되돌리기 쉬운 동작에는 걸지 않습니다.

허용 시간도 같은 기준으로 정합니다. 잘못됐을 때 잃는 것이 클수록 짧게 잡습니다.

관련 항목

재인증이 다시 확인하는 로그인 상태

인증 · 세션 · 쿠키 · 세션 쿠키 · 액세스 토큰 · 리프레시 토큰 · 로그아웃

재인증을 부르는 세션 기한 방식

세션 만료 · 유휴 만료 · 절대 만료 · 슬라이딩 만료 · 토큰 만료

재인증에 쓰는 확인 수단

비밀번호 · 다중 인증 · 일회용 비밀번호 · 패스키 · WebAuthn · 생체 인증

재인증이 막는 공격

세션 하이재킹 · 세션 고정 · 계정 탈취 · 클릭재킹 · 크로스 사이트 요청 위조 · 교차 사이트 스크립팅

재인증을 걸지 정하는 판단 방식

스텝업 인증 · 위험 기반 인증 · 적응형 인증 · 인증 보증 수준

재인증을 부탁하는 로그인 표준

OpenID Connect · OAuth 2.0 · ID 토큰 · 인가 서버 · 신뢰 당사자

재인증이 속하는 상위 분류

인증과 인가 · 세션 관리 · 웹 보안 · 계정 보안

다른 이름: reauthentication · re-authentication · 재로그인