솔티드 시도 응답 인증 메커니즘

암호학에서 **솔티드 시도 응답 인증 메커니즘**(Salted Challenge Response Authentication Mechanism, **SCRAM**)은 현대적이고 비밀번호 기반의 시도-응답 인증 메커니즘 계열로, 사용자가 서버에 인증할 수 있도록 해준다. 이는 단순 인증 및 보안 계층(SASL)에 명시되어 있어 LDAP, HTTP, SMTP, POP3, IMAP, JMAP(이메일), XMPP(채팅) 또는 몽고DB와 PostgreSQL(데이터베이스)과 같은 서비스에 비밀번호 기반 로그인을 위해 사용될 수 있다. XMPP의 경우 이를 지원하는 것이 필수적이다.access-date=2023-08-04
# **동기**
앨리스가 밥의 서버에 로그인하고자 한다. 그는 자신이 주장하는 사람임을 증명해야 한다. 이 인증 문제를 해결하기 위해 앨리스와 밥은 비밀번호에 합의했는데, 앨리스는 이를 알고 있고 밥은 이를 확인하는 방법을 알고 있다. 이제 앨리스는 암호화되지 않은 연결을 통해 밥에게 비밀번호를 평문 형태로 보내 확인할 수 있다. 그러나 이렇게 하면 통신선을 도청하고 있는 맬로리가 비밀번호에 접근할 수 있게 된다. 앨리스와 밥은 연결을 암호화하여 이를 우회하려 할 수 있다. 하지만 앨리스는 이 암호화가 밥에 의해 설정된 것인지, 아니면 맬로리가 중간자 공격을 통해 설정한 것인지 알 수 없다. 따라서 앨리스는 CRAM-MD5나 DIGEST-MD5에서처럼 비밀번호의 해시 버전을 대신 보낸다. 해시이기 때문에 맬로리는 비밀번호 자체를 얻지 못한다. 해시가 챌린지로 솔팅되었기 때문에 맬로리는 이를 단 한 번의 로그인 과정에만 사용할 수 있다. 그러나 앨리스는 밥에게 기밀 정보를 주고 싶어 하며, 그것이 맬로리가 아닌 밥이라는 것을 확실히 하고 싶어 한다. 이를 해결하기 위해 밥은 인증 기관(CA)에 등록하여 자신의 인증서에 서명을 받았다. 앨리스는 그 서명 시스템만을 신뢰할 수 있지만, 그것에 약점이 있다는 것을 알고 있다. 추가적인 보증을 위해 밥은 자신이 비밀번호(또는 그것의 솔티드 해시)를 알고 있다는 증명을 만들고, 이 증명에 자신의 인증서를 포함시킨다. 이러한 포함을 채널 바인딩이라고 하는데, 하위 암호화 채널이 상위 애플리케이션 채널에 '바인딩'되기 때문이다. 그러면 앨리스는 밥의 인증을 받고, 밥은 앨리스의 인증을 받게 된다. 이를 종합하면 상호 인증이 이루어진다. DIGEST-MD5는 이미 상호 인증을 가능하게 했지만, 종종 부정확하게 구현되었다.url-status=access-date=2023-08-04 맬로리가 중간자 공격을 실행하고 CA 서명을 위조하면 비밀번호의 해시를 얻을 수 있다. 그러나 그는 단일 로그인 세션에서조차 앨리스를 사칭할 수 없는데, 앨리스가 자신의 해시에 맬로리의 암호화 키를 포함시켜 밥으로부터 로그인 실패를 초래하기 때문이다. 완전히 투명한 공격을 하려면 맬로리는 앨리스가 사용한 비밀번호나 밥의 비밀 암호화 키를 알아야 할 것이다. 밥은 서버 데이터베이스의 데이터 유출에 대해 들었고, 사용자들의 비밀번호를 평문으로 저장하고 싶지 않다고 결정했다. 그는 CRAM-MD5와 DIGEST-MD5 로그인 방식에 대해 들었지만, 이러한 로그인 방식을 사용자들에게 제공하려면 약하게 해시되고 솔팅되지 않은 비밀번호를 저장해야 한다는 것을 알고 있다. 그는 이 아이디어가 마음에 들지 않아 비밀번호를 평문으로 요구하기로 결정한다. 그러면 bcrypt, scrypt 또는 PBKDF2와 같은 안전한 해싱 방식으로 해시하고 원하는 대로 솔팅할 수 있다. 그러나 그렇게 하더라도 밥과 앨리스는 여전히 위에서 설명한 문제에 직면하게 된다. 이 문제를 해결하기 위해 그들은 SCRAM을 사용하는데, 여기서 밥은 비밀번호를 PBKDF2를 사용하여 솔티드 형식으로 저장할 수 있다. 로그인 중에 밥은 앨리스에게 자신의 솔트와 PBKDF2 알고리즘의 반복 횟수를 보내고, 그러면 앨리스는 이를 사용하여 밥이 데이터베이스에 가지고 있는 해시된 비밀번호를 계산한다. SCRAM의 모든 추가 계산은 둘 다 알고 있는 이 값을 기반으로 한다.
# 프로토콜 개요
모든 클라이언트와 서버가 SHA-1 해싱 알고리즘을 지원해야 하지만, SCRAM은 CRAM-MD5나 DIGEST-MD5와 달리 기본 해시 함수와 독립적이다.access-date=2023-08-04 IANA에서 정의한 모든 해시 함수를 대신 사용할 수 있다.access-date=2023-08-04 동기 부분에서 언급했듯이 SCRAM은 PBKDF2 메커니즘을 사용하는데, 이는 서버에서 데이터 유출이 발생했을 때 무차별 대입 공격에 대한 강도를 높인다. `H`를 서버가 광고하고 클라이언트가 선택한 알고리즘 이름으로 주어진 선택된 해시 함수라고 하자. 예를 들어 'SCRAM-SHA-1'은 SHA-1을 해시 함수로 사용한다. **비밀번호 기반 유도 키, 또는 솔티드 비밀번호** 클라이언트는 비밀번호, 솔트, 그리고 계산 반복 횟수로부터 다음과 같이 키 또는 솔티드 비밀번호를 유도한다. `*SaltedPassword* = H(*password*, *salt*, *iteration-count*) = PBKDF2(HMAC, *password*, *salt*, *iteration-count*, H의 출력값 길이)`. **메시지** RFC 5802는 서버와 클라이언트 간의 연속적인 4개의 메시지를 명명한다. # client-first: 클라이언트 최초 메시지는 GS2 헤더(채널 바인딩 플래그와 선택적 인증 정보 이름 포함), 원하는 사용자명 `*username*`, 그리고 무작위로 생성된 클라이언트 논스 `*c-nonce*`로 구성된다. # server-first: 서버는 이 클라이언트 논스에 자체 논스 `*s-nonce*`를 추가하고 이를 서버 최초 메시지에 포함시키는데, 여기에는 서버가 사용자의 비밀번호 해시를 솔팅하는 데 사용하는 솔트와 반복 횟수 `*iteration-count*`도 포함된다. # client-final: 그 후 클라이언트는 채널 바인딩, base64로 인코딩된 GS2 헤더와 채널 바인딩 데이터, 클라이언트와 서버 논스의 연결, 그리고 클라이언트 증명 `*proof*`를 포함하는 클라이언트 최종 메시지를 보낸다. # server-final: 통신은 서버 서명 `*verifier*`를 포함하는 서버 최종 메시지로 마무리된다. **증명** 클라이언트와 서버는 서로에게 동일한 `*Auth*` 변수를 가지고 있음을 증명한다. `*Auth*`는 다음과 같이 구성된다. : `*Auth* = *client-first-without-header* + , + *server-first* + , + *client-final-without-proof*`(쉼표로 연결됨) 보다 구체적으로, 이는 다음과 같은 형태를 취한다. : `= n=*username*,r=*cnbhnonce*,[*extensions*,]r=*cnbhnonce*‖*snbhnonce*,s=*salt*,i=*iterationnbhcount*,[*extensions*,]c=base64(*channelnbhflag*,[a=*authzid*],*channelnbhbinding*),r=*cnbhnonce*‖*snbhnonce*[,*extensions*]` 증명은 다음과 같이 계산된다: : `*ClientKey* = HMAC(*SaltedPassword*, 'Client Key')` : `*ServerKey* = HMAC(*SaltedPassword*, 'Server Key')` : `*ClientProof* = *p* = *ClientKey* XOR HMAC(H(*ClientKey*), *Auth*)` : `*ServerSignature* = *v* = HMAC(*ServerKey*, *Auth*)` 여기서 XOR 연산은 동일한 길이의 바이트 문자열에 적용되며, `H(*ClientKey*)`는 `*ClientKey*`의 일반적인 해시이다. `'Client Key'`와 `'Server Key'`는 그대로의 문자열이다. 서버는 `ClientProof`로부터 `ClientKey`를 계산한 다음 `H(ClientKey)`를 저장된 값과 비교하여 클라이언트를 인증할 수 있다. 클라이언트는 `ServerSignature`를 직접 계산하고 비교하여 서버를 인증할 수 있다. **저장된 비밀번호** 서버는 사용자명, `*salt*`, `*iteration-count*`, `H(*ClientKey*)`, `*ServerKey*`만을 저장한다. 서버는 클라이언트 증명으로부터 복구된 `*ClientKey*`에 일시적으로 접근할 수 있는데, 이는 `H(*ClientKey*)`로 암호화되어 있다. 클라이언트는 `*password*`만 필요로 한다. **채널 바인딩** 채널 바인딩이란 상호 인증을 제공하는 애플리케이션 계층을 하위(주로 암호화) 계층에 '바인딩'하여 연결의 끝점이 두 계층에서 동일함을 보장함으로써 중간자 공격을 방지하는 전략을 말한다. 채널 바인딩에는 두 가지 일반적인 방향이 있다: 고유 채널 바인딩과 끝점 채널 바인딩이다. 전자는 특정 연결이 사용되고 있음을 보장하고, 후자는 끝점이 동일함을 보장한다. 여러 채널 바인딩 유형이 있으며, 각 유형은 고유한 채널 바인딩 접두사를 가진다.access-date=2023-08-04 각 채널 바인딩 유형은 채널과 끝점에 대한 고유한 정보를 제공하는 채널 바인딩 데이터의 내용을 지정한다. 예를 들어, tls-server-end-point 채널 바인딩의 경우 서버의 TLS 인증서가 이에 해당한다.access-date=2023-08-04 SCRAM을 애플리케이션 계층으로 사용하는 채널 바인딩의 예시적 사용 사례로는 전송 계층 보안(TLS)을 하위 계층으로 사용하는 경우가 있을 수 있다. TLS는 통신이 암호화되어 있어 수동적 도청을 방지한다. 그러나 클라이언트가 서버를 인증하지 않으면(예: 서버의 인증서를 확인하지 않음) 중간자 공격을 방지할 수 없다. 이를 위해 끝점들은 서로에게 자신의 신원을 보장해야 하는데, 이는 SCRAM에 의해 제공될 수 있다. gs2-cbind-flag SCRAM 변수는 클라이언트가 채널 바인딩을 지원하는지 여부, 또는 서버가 채널 바인딩을 지원하지 않는다고 생각하는지를 지정하고, c-bind-input에는 gs2-cbind-flag와 함께 채널 바인딩 고유 접두사 및 채널 바인딩 데이터 자체가 포함된다. 채널 바인딩은 SCRAM에서 선택 사항이며, gs2-cbind-flag 변수는 다운그레이드 공격을 방지한다. 서버가 채널 바인딩을 지원할 경우, 광고된 SCRAM 알고리즘 이름에 '-PLUS' 문자 시퀀스를 추가한다.
# 장점
* 강력한 비밀번호 저장: 올바르게 구현될 경우, 서버는 비밀번호를 솔티드, 반복 해시 형식으로 저장할 수 있어 오프라인 공격을 더 어렵게 만들고 데이터베이스 유출의 영향을 감소시킨다.access-date=2024-05-27 * 단순성: SCRAM의 구현은 DIGEST-MD5보다access-date=2023-08-04 더 쉽다.access-date=2023-08-04 * 국제적 상호운용성: RFC는 CRAM-MD5와 달리 사용자 이름과 비밀번호에 UTF-8을 사용하도록 요구한다.access-date=2023-08-04 * 전체 로그인 과정에서 비밀번호의 솔티드 및 해시된 버전만 사용되고 서버의 솔트가 변경되지 않기 때문에, 비밀번호를 저장하는 클라이언트는 해시된 버전을 저장할 수 있으며 공격자에게 평문 비밀번호를 노출시키지 않는다. 이러한 해시된 버전은 한 서버에 바인딩되어 있어 비밀번호 재사용에 유용하다.url-status=dead