1. 소개
역사적으로 계정 생성, 로그인 또는 계정 복구 중 이메일 주소를 검증하는 작업은 수동 대역 외 메커니즘에 의존해 왔다. 일반적인 흐름에서는 웹사이트가 일회용 암호(OTP) 또는 "매직 링크"를 사용자의 이메일 주소로 전송한다. 그런 다음 사용자는 이메일 받은편지함으로 이동하여 코드를 가져오거나 링크를 클릭한 뒤 웹사이트로 돌아와 소유권을 증명해야 한다.
이 과정은 상당한 마찰을 유발하여 사용자 경험과 전환율에 영향을 준다. 또한 악성 사이트가 사용자를 속여 OTP를 제공하도록 할 수 있는 피싱 공격에 취약하다.
이메일 검증 프로토콜(EVP)은 이메일 소유권을 암호학적으로 검증하는 브라우저 중개 메커니즘을 도입한다. 사용자와 이메일 제공자(발급자) 간의 활성 세션을 활용하여 브라우저는 암호학적으로 서명된 이메일 검증 토큰(EVT)을 요청하고 이를 웹사이트(검증자)에 제시할 수 있다.
이 명세는 검증자가 EVT를 요청하는 데 사용하는 HTML 확장과 발급자와 조정하여 토큰을 획득하는 클라이언트 측 브라우저 동작을 정의한다.
1.1. 프로토콜 개요
이 프로토콜에는 세 가지 주요 당사자가 관여한다:
-
검증자: 이메일 검증을 요청하는 웹사이트.
-
사용자 에이전트(UA): 요청을 중개하는 브라우저.
-
발급자: 이메일 주소에 대한 권한을 가진 이메일 제공자.
일반적인 흐름은 다음 단계로 구성된다:
-
로그인: 사용자가 이메일 제공자에 로그인한다. 제공자는 브라우저의 로그인 상태를 로그인됨으로 업데이트한다( 로그인 상태 API 사용).
-
요청: 검증자의 웹사이트는
autocomplete="email-verification-token"과nonce속성이 있는 숨겨진 입력 필드를 포함한다. -
검색 및 검증: 사용자가 이메일 주소를 선택하면(예: 자동 완성을 통해) UA는 DNS를 통해 권한 있는 발급자를 검색한다. UA는 사용자가 해당 발급자와 활성 세션을 가지고 있는지 확인한다.
-
발급: UA가 발급자에게 EVT를 요청한다.
-
바인딩 및 제시: UA는 검증자의 출처와 nonce를 EVT에 바인딩하여 키 바인딩 JWT(KB-JWT)를 생성하고, 양식 제출 전에 이를 검증자의 입력 필드에 채운다.
-
검증: 검증자는 EVT와 KB-JWT를 검증하여 확인 절차를 완료한다.
1.2. 예제
이 절은 비규범적이다.
가입 중 사용자의 이메일 주소를 검증하려는 검증자 웹사이트
https://rp.example을 고려한다.
1.2.1. 검증자 HTML 양식
검증자는 표준 이메일 입력과 이메일 검증 토큰(EVT)을 위한 숨겨진 입력을
autocomplete="email-verification-token" 및 고유한 nonce와 함께 포함한다:
< form action = "/signup" method = "post" > < label for = "email" > 이메일 주소:</ label > < input type = "email" id = "email" name = "email" autocomplete = "email" > <!-- EVP 숨겨진 입력 --> < input type = "hidden" name = "evt" autocomplete = "email-verification-token" nonce = "xyz123456789" > < button type = "submit" > 가입</ button > </ form >
1.2.2. 브라우저 상호작용
-
사용자가
email입력에 포커스하면 브라우저는 자동 완성 이메일 주소를 제안한다 (예:user@email.example). -
사용자가
user@email.example을 선택한다. -
브라우저는 발급자 검색을 수행하고(
email.example이issuer.example에 위임한다는 것을 확인), 발급자와의 사용자 세션을 검증한 뒤 프롬프트를 표시한다: 이메일을 검증하시겠습니까?user@email.example의 검증된 토큰을rp.example과 공유하시겠습니까? [허용] [거부] -
사용자가 "허용"을 선택하면 브라우저는
issuer.example에서 EVT를 가져와rp.example및 noncexyz123456789에 바인딩하고 바인딩된 토큰을 저장한다.
1.2.3. 양식 제출
사용자가 양식을 제출하면 브라우저는 바인딩된 토큰을 숨겨진 입력 필드에 자동으로 삽입한다. 검증자는 다음 POST 페이로드를 수신한다:
POST /signup HTTP / 1.1 Host : rp.example Content-Type : application/x-www-form-urlencoded = user%40email.example & evt = eyJhbGciOiJFZERTQSIsImtpZCI6IjIwMjQtMDgtMTkiLCJ0eXAiOiJldnQrand0In0...
그런 다음 검증자는 검증 이메일을 보낼 필요 없이 등록을 완료하기 위해 evt 토큰을 구문 분석하고
검증한다(서명, 출처 rp.example, nonce xyz123456789 및 이메일
user@email.example의 일치 여부를 검증).
2. HTML 확장
이 명세는 새로운 자동 완성 필드 이름을 도입하고
nonce 속성의 사용을 확장하여 HTML 표준 [HTML]을 확장한다.
2.1. email-verification-token 자동 완성 값
email-verification-token 키워드는 [HTML]에 정의된 자동 완성 필드 이름 목록(구체적으로는 세부 토큰)에 추가된다.
<input> 요소의 autocomplete 속성이
email-verification-token으로 설정되면, 이는 사용자가
동일한 양식에서 이메일 주소를 선택하고 검증한 경우 사용자 에이전트가 양식 제출 시
암호학적으로 바인딩된 이메일 검증 토큰(EVT)으로 이 필드를 채우도록 시도하는 것이 좋음을 나타낸다.
일반적으로 이 키워드는 <input type="hidden"> 요소에 사용된다.
2.2. input 요소의 nonce 속성
이 명세는 원래 [HTML]의 <script>
및 <style> 요소에 정의되고
콘텐츠 보안 정책 [CSP]에서 사용되는
nonce 속성을
<input> 요소로 확장한다.
autocomplete="email-verification-token"이 있는 <input> 요소에 존재하는 경우,
nonce 속성에는 서버 측에서 생성된 암호학적으로 강력한 무작위 값(
암호학적 nonce)이 포함된다.
이 nonce는 생성되는 EVT를 특정 양식 표시에 바인딩하여 재전송 공격을 방지하는 데 사용된다.
nonce 속성의 값은 각 페이지 렌더링마다 고유해야 한다.
3. 브라우저 처리 모델
3.1. 자동 완성 선택 처리
사용자가 <form> form 내의
<input> 요소 emailInput에 대한 사용자 에이전트의 자동 완성 제안에서 이메일 주소 email을 선택하면:
-
form에서 값이
email-verification-token인autocomplete속성을 가진 첫 번째<input>요소를 evtInput으로 둔다. -
그러한 evtInput이 존재하지 않으면 이 단계를 종료한다.
-
evtInput의
nonce속성 값을 nonce로 둔다. -
nonce가 비어 있으면 이 단계를 종료한다.
-
form의 EVP 상태를 다음과 같이 설정한다:
-
email: email -
inputElement: emailInput -
token: null
-
-
백그라운드에서 다음 단계를 수행한다:
-
email에 대해 [EVP-Protocol]에 정의된 발급자 검색 단계를 수행한 결과를 issuer로 둔다.
-
issuer가 null이면 이 단계를 종료한다.
-
issuer를 사용하여 email에 대해 계정 검증을 수행한 결과를 accountMatch로 둔다.
-
accountMatch가 false이면 이 단계를 종료한다.
-
issuer로 이메일 주소 email을 검증할 권한을 요청하는 사용자 프롬프트를 표시한다.
-
사용자가 권한을 거부하면 이 단계를 종료한다.
-
issuer에서 email에 대해 EVT 발급을 수행한 결과를 evtResult로 둔다.
-
evtResult가 null이면 이 단계를 종료한다.
-
evtResult[0]을 evt로 둔다.
-
evtResult[1]을 keyPair로 둔다.
-
keyPair의 비공개 키 구성요소를 사용하고 nonce 및 문서의 출처를 지정하여 evt에 대해 [EVP-Protocol]에 정의된 키 바인딩 생성 단계를 수행한 결과를 kbEvt로 둔다.
-
form의 EVP 상태를 state로 둔다.
-
state가 null이 아니고 state의
email이 대소문자를 구분하지 않고 email과 같으면:-
state의
token을 kbEvt로 설정한다.
-
-
3.2. 양식 제출 통합
<form> form이 제출되면:
-
form의 EVP 상태를 state로 둔다.
-
state가 null이거나 state의
token이 null이면 표준 양식 제출 단계를 계속한다. -
state의
inputElement값을 currentEmail로 둔다. -
currentEmail이 대소문자를 구분하지 않고 state의
email과 같으면:-
form에서 값이
email-verification-token인autocomplete속성을 가진 첫 번째<input>요소를 evtInput으로 둔다. -
evtInput이 존재하면:
-
evtInput의 값을 state의
token으로 설정한다.
-
-
-
[HTML]에 정의된 표준 양식 제출 단계를 계속한다.
3.3. 계정 검증
이메일 주소 email과 발급자 도메인 issuer가 주어지면 사용자 에이전트는 계정을 검증하기 위해 다음 단계를 수행해야 한다:
-
로그인 상태 API [login-status]를 사용하여 issuer의 로그인 상태를 확인한다.
-
상태가 로그아웃됨이면 false를 반환한다.
-
[fedcm]에 정의된 대로 issuer의 well-known 파일을 가져온 결과를 wellKnown으로 둔다.
-
wellKnown이 null이면 false를 반환한다.
-
wellKnown의
accounts_endpoint멤버 값을 accountsEndpoint로 둔다. -
accountsEndpoint가 존재하지 않거나 유효한 URL이 아니면 false를 반환한다.
-
issuer 및 accountsEndpoint에 대해 [fedcm]에 정의된 계정 가져오기 알고리즘을 실행한 결과를 accounts로 둔다.
-
accounts가 null이거나 비어 있으면 false를 반환한다.
-
accounts의 각 account에 대해:
-
account의
email멤버 값을 accountEmail로 둔다. -
accountEmail이 대소문자를 구분하지 않고 email과 같으면 true를 반환한다.
-
-
false를 반환한다.
3.4. EVT 발급
이메일 주소 email과 발급자 도메인 issuer가 주어지면 사용자 에이전트는 EVT를 얻기 위해 다음 단계를 수행해야 한다:
-
임시 비대칭 키 쌍 keyPair을 생성한다(발급자의 메타데이터에 정의된 발급자가 지원하는 알고리즘을 사용하며, 지정되지 않은 경우 Ed25519를 기본값으로 사용).
-
JSON 웹 토큰 [JWT] requestToken을 구성한다:
-
헤더는 다음을 포함해야 한다:
-
alg: 서명 알고리즘(keyPair의 알고리즘과 일치). -
jwk: keyPair의 공개 키 구성요소.
-
-
페이로드는 다음을 포함해야 한다:
-
aud: issuer의 식별자(도메인). -
iat: 현재 시간. -
email: email 주소.
-
-
keyPair의 비공개 키 구성요소를 사용하여 requestToken에 서명한다.
-
-
발급자의 토큰 발급 엔드포인트(발급자의 메타데이터에서 얻음)를 issuanceUrl로 둔다.
-
issuanceUrl로 보내는 HTTP POST 요청 request를 구성한다:
-
Content-Type헤더를application/x-www-form-urlencoded로 설정한다. -
Sec-Fetch-Dest헤더를email-verification으로 설정한다. -
issuer의 자사 쿠키를 포함한다.
-
요청 본문을 다음의 URL 인코딩 표현으로 설정한다:
request_token= requestToken (문자열로 직렬화).
-
-
request를 전송하고 응답 callResponse를 기다린다.
-
callResponse의 상태 코드가 200 OK가 아니거나
Content-Type이application/json이 아니면 null을 반환한다. -
callResponse의 본문을 JSON으로 구문 분석하고 그 결과를 json으로 둔다.
-
json에
issuance_token이 포함되어 있지 않으면 null을 반환한다. -
json의
issuance_token을 evt로 둔다. -
evt를 검증한다:
-
evt가 발급자가 서명한 유효한 SD-JWT [SD-JWT]인지 검증한다.
-
evt의
cnf클레임에 keyPair의 공개 키와 일치하는 공개 키가 포함되어 있는지 검증한다. -
evt의
email클레임이 email과 일치하는지 검증한다.
-
-
검증에 실패하면 null을 반환한다.
-
(evt, keyPair)을 포함하는 튜플을 반환한다.
4. 검증자 처리 모델
검증자가 이메일 주소 email과 바인딩된 토큰
boundToken(autocomplete="email-verification-token"이 있는 입력 필드에서 가져옴)이 포함된 제출된 양식을
수신하면:
-
검증자의 출처 및 예상 nonce에 대해 boundToken에 [EVP-Protocol]에 정의된 토큰 검증 단계를 수행한다.
-
검증에 실패하면 검증을 실패 처리한다.
-
검증된 토큰에서 추출한 이메일 주소를 verifiedEmail로 둔다.
-
verifiedEmail이 대소문자를 구분하지 않고 email과 같지 않으면 검증을 실패 처리한다.
-
그렇지 않으면 검증에 성공한다.
5. 보안 및 개인정보 보호 고려사항
5.1. 개인정보 보호
5.1.1. 발급자 블라인딩
EVP의 주요 개인정보 보호 목표는 발급자가 사용자가 어떤 검증자와 상호작용하는지 알지 못하도록 하는 것이다.사용자 에이전트는 Well-Known 가져오기와 계정 가져오기가 검증자의
출처를 유출하지 않도록 해야 한다.
발급 요청은 자격 증명을 포함하더라도 요청 헤더에 검증자의 출처를 포함해서는 안 된다
(예: Referer 또는 Origin은 생략해야 한다).
5.1.2. 추적 위험
발급 요청은 쿠키를 사용하므로 발급자는 사용자가 활동 중임을 알게 된다. 그러나 이는 사용자가 사용자 주도 동작으로 검증할 이메일을 적극적으로 선택할 때만 알게 된다.5.2. 보안
5.2.1. 재전송 공격
제시 토큰(EVT+KB)은 키 바인딩 JWT를 통해 특정nonce 및 audience(검증자
출처)에 바인딩된다.
검증자는 다음을 검증해야 한다:
-
audience가 자신의 출처와 일치한다. -
nonce가 양식 표시를 위해 생성한 값과 일치한다. -
exp클레임이 만료되지 않았다.
이는 공격자가 EVT+KB를 가로채 다른 사이트나 다른 컨텍스트에서 재전송하는 것을 방지한다.