{{위키데이터 속성 추적}}

암호학에서 '''솔티드 시도 응답 인증 메커니즘'''(Salted Challenge Response Authentication Mechanism, '''SCRAM''')은 현대적이고 비밀번호 기반의 [[시도-응답 인증]] 메커니즘 계열로, 사용자가 서버에 인증할 수 있도록 해준다. 이는 [[SASL|단순 인증 및 보안 계층]](SASL)에 명시되어 있어 [[LDAP]], [[HTTP]], [[SMTP]], [[POP3]], [[인터넷 메시지 접속 프로토콜|IMAP]], [[JMAP]]([[전자우편|이메일]]), [[XMPP]](채팅) 또는 [[몽고DB]]와 [[PostgreSQL]](데이터베이스)과 같은 서비스에 비밀번호 기반 로그인을 위해 사용될 수 있다. XMPP의 경우 이를 지원하는 것이 필수적이다.<ref>{{웹 인용|url=https://datatracker.ietf.org/doc/html/rfc6120#section-13.8.3|title=Extensible Messaging and Presence Protocol: For Confidentiality and Authentication with Passwords|date=March 2011|publisher=[[IETF]]|access-date=2023-08-04}}</ref>

== '''동기''' ==
[[앨리스와 밥|앨리스가 밥]]의 서버에 로그인하고자 한다. 그는 자신이 주장하는 사람임을 증명해야 한다. 이 인증 문제를 해결하기 위해 앨리스와 밥은 비밀번호에 합의했는데, 앨리스는 이를 알고 있고 밥은 이를 확인하는 방법을 알고 있다.

이제 앨리스는 암호화되지 않은 연결을 통해 밥에게 비밀번호를 평문 형태로 보내 확인할 수 있다. 그러나 이렇게 하면 통신선을 도청하고 있는 맬로리가 비밀번호에 접근할 수 있게 된다. 앨리스와 밥은 연결을 암호화하여 이를 우회하려 할 수 있다. 하지만 앨리스는 이 암호화가 밥에 의해 설정된 것인지, 아니면 맬로리가 [[중간자 공격]]을 통해 설정한 것인지 알 수 없다. 따라서 앨리스는 [[CRAM-MD5]]나 [[다이제스트 인증|DIGEST-MD5]]에서처럼 비밀번호의 해시 버전을 대신 보낸다. 해시이기 때문에 맬로리는 비밀번호 자체를 얻지 못한다. 해시가 챌린지로 솔팅되었기 때문에 맬로리는 이를 단 한 번의 로그인 과정에만 사용할 수 있다. 그러나 앨리스는 밥에게 기밀 정보를 주고 싶어 하며, 그것이 맬로리가 아닌 밥이라는 것을 확실히 하고 싶어 한다.

이를 해결하기 위해 밥은 [[인증 기관]](CA)에 등록하여 자신의 인증서에 서명을 받았다. 앨리스는 그 서명 시스템만을 신뢰할 수 있지만, 그것에 약점이 있다는 것을 알고 있다. 추가적인 보증을 위해 밥은 자신이 비밀번호(또는 그것의 솔티드 해시)를 알고 있다는 증명을 만들고, 이 증명에 자신의 인증서를 포함시킨다. 이러한 포함을 채널 바인딩이라고 하는데, 하위 암호화 채널이 상위 애플리케이션 채널에 '바인딩'되기 때문이다.

그러면 앨리스는 밥의 인증을 받고, 밥은 앨리스의 인증을 받게 된다. 이를 종합하면 [[상호 인증]]이 이루어진다. DIGEST-MD5는 이미 상호 인증을 가능하게 했지만, 종종 부정확하게 구현되었다.<ref>{{웹 인용|url=https://people.apache.org/~elecharny/ldapcon/Kurt%20Zeilenga-paper.pdf|title=SCRAM in LDAP Better Password-based Authentication|last=Kurt Zeilenga|date=19 May 2010|access-date=2023-08-04}}</ref><ref>{{웹 인용|url=https://josefsson.org/talks/gnu-network-security-labyrinth.pdf|title=GNU Network Security Labyrinth|last=Simon Josefsson|date=2011-02-06|access-date=2023-08-04}}</ref>

맬로리가 중간자 공격을 실행하고 CA 서명을 위조하면 비밀번호의 해시를 얻을 수 있다. 그러나 그는 단일 로그인 세션에서조차 앨리스를 사칭할 수 없는데, 앨리스가 자신의 해시에 맬로리의 암호화 키를 포함시켜 밥으로부터 로그인 실패를 초래하기 때문이다. 완전히 투명한 공격을 하려면 맬로리는 앨리스가 사용한 비밀번호나 밥의 비밀 암호화 키를 알아야 할 것이다.

밥은 서버 데이터베이스의 데이터 유출에 대해 들었고, 사용자들의 비밀번호를 평문으로 저장하고 싶지 않다고 결정했다. 그는 CRAM-MD5와 DIGEST-MD5 로그인 방식에 대해 들었지만, 이러한 로그인 방식을 사용자들에게 제공하려면 약하게 해시되고 솔팅되지 않은 비밀번호를 저장해야 한다는 것을 알고 있다. 그는 이 아이디어가 마음에 들지 않아 비밀번호를 평문으로 요구하기로 결정한다. 그러면 [[bcrypt]], [[scrypt]] 또는 [[PBKDF2]]와 같은 안전한 해싱 방식으로 해시하고 원하는 대로 솔팅할 수 있다. 그러나 그렇게 하더라도 밥과 앨리스는 여전히 위에서 설명한 문제에 직면하게 된다. 이 문제를 해결하기 위해 그들은 SCRAM을 사용하는데, 여기서 밥은 비밀번호를 PBKDF2를 사용하여 솔티드 형식으로 저장할 수 있다. 로그인 중에 밥은 앨리스에게 자신의 솔트와 PBKDF2 알고리즘의 반복 횟수를 보내고, 그러면 앨리스는 이를 사용하여 밥이 데이터베이스에 가지고 있는 해시된 비밀번호를 계산한다. SCRAM의 모든 추가 계산은 둘 다 알고 있는 이 값을 기반으로 한다.

== 프로토콜 개요 ==
모든 클라이언트와 서버가 [[SHA-1]] 해싱 알고리즘을 지원해야 하지만, SCRAM은 [[CRAM-MD5]]나 [[다이제스트 인증|DIGEST-MD5]]와 달리 기본 해시 함수와 독립적이다.<ref>{{웹 인용|url=https://datatracker.ietf.org/doc/html/rfc5802#section-4|title=Salted Challenge Response Authentication Mechanism (SCRAM) SASL and GSS-API Mechanisms: SCRAM Mechanism Names|date=July 2010|publisher=IETF|access-date=2023-08-04}}</ref> IANA에서 정의한 모든 해시 함수를 대신 사용할 수 있다.<ref>{{웹 인용|url=https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xhtml|title=Hash Function Textual Names|date=2019-12-16|publisher=[[IANA]]|access-date=2023-08-04}}</ref> 동기 부분에서 언급했듯이 SCRAM은 [[PBKDF2]] 메커니즘을 사용하는데, 이는 서버에서 데이터 유출이 발생했을 때 무차별 대입 공격에 대한 강도를 높인다. <code>H</code>를 서버가 광고하고 클라이언트가 선택한 알고리즘 이름으로 주어진 선택된 해시 함수라고 하자. 예를 들어 'SCRAM-SHA-1'은 SHA-1을 해시 함수로 사용한다.

'''비밀번호 기반 유도 키, 또는 솔티드 비밀번호'''

클라이언트는 비밀번호, 솔트, 그리고 계산 반복 횟수로부터 다음과 같이 키 또는 솔티드 비밀번호를 유도한다.

<code>''SaltedPassword'' = H(''password'', ''salt'', ''iteration-count'') = [[PBKDF2]]([[HMAC]], ''password'', ''salt'', ''iteration-count'', H의 출력값 길이)</code>.

'''메시지'''

RFC 5802<nowiki/>는 서버와 클라이언트 간의 연속적인 4개의 메시지를 명명한다.

# client-first: 클라이언트 최초 메시지는 GS2 헤더(채널 바인딩 플래그와 선택적 인증 정보 이름 포함), 원하는 사용자명 <code>''username''</code>, 그리고 무작위로 생성된 클라이언트 논스 <code>''c-nonce''</code>로 구성된다.
# server-first: 서버는 이 클라이언트 논스에 자체 논스 <code>''s-nonce''</code>를 추가하고 이를 서버 최초 메시지에 포함시키는데, 여기에는 서버가 사용자의 비밀번호 해시를 솔팅하는 데 사용하는 솔트와 반복 횟수 <code>''iteration-count''</code>도 포함된다.
# client-final: 그 후 클라이언트는 채널 바인딩, base64로 인코딩된 GS2 헤더와 채널 바인딩 데이터, 클라이언트와 서버 논스의 연결, 그리고 클라이언트 증명 <code>''proof''</code>를 포함하는 클라이언트 최종 메시지를 보낸다.
# server-final: 통신은 서버 서명 <code>''verifier''</code>를 포함하는 서버 최종 메시지로 마무리된다.

'''증명'''

클라이언트와 서버는 서로에게 동일한 <code>''Auth''</code> 변수를 가지고 있음을 증명한다. <code>''Auth''</code>는 다음과 같이 구성된다.

: <code>''Auth'' = ''client-first-without-header'' + , + ''server-first'' + , + ''client-final-without-proof''</code>(쉼표로 연결됨)

보다 구체적으로, 이는 다음과 같은 형태를 취한다.

: <code>= n=''username'',r=''c{{nbh}}nonce'',[''extensions'',]r=''c{{nbh}}nonce''‖''s{{nbh}}nonce'',s=''salt'',i=''iteration{{nbh}}count'',[''extensions'',]c=base64(''channel{{nbh}}flag'',[a=''authzid'']<!--mandatory comma repetition intended-->,''channel{{nbh}}binding''),r=''c{{nbh}}nonce''‖''s{{nbh}}nonce''[,''extensions'']</code>

증명은 다음과 같이 계산된다:

: <code>''ClientKey'' = HMAC(''SaltedPassword'', 'Client Key')</code>
: <code>''ServerKey'' = HMAC(''SaltedPassword'', 'Server Key')</code>
: <code>''ClientProof'' = ''p'' = ''ClientKey'' XOR HMAC(H(''ClientKey''), ''Auth'')</code>
: <code>''ServerSignature'' = ''v'' = HMAC(''ServerKey'', ''Auth'')</code>

여기서 [[배타적 논리합|XOR]] 연산은 동일한 길이의 바이트 문자열에 적용되며, <code>H(''ClientKey'')</code>는 <code>''ClientKey''</code>의 일반적인 해시이다. <code>'Client Key'</code>와 <code>'Server Key'</code>는 그대로의 문자열이다.

서버는 <code>ClientProof</code>로부터 <code>ClientKey</code>를 계산한 다음 <code>H(ClientKey)</code>를 저장된 값과 비교하여 클라이언트를 인증할 수 있다.

클라이언트는 <code>ServerSignature</code>를 직접 계산하고 비교하여 서버를 인증할 수 있다.

'''저장된 비밀번호'''

서버는 사용자명, <code>''salt''</code>, <code>''iteration-count''</code>, <code>H(''ClientKey'')</code>, <code>''ServerKey''</code>만을 저장한다. 서버는 클라이언트 증명으로부터 복구된 <code>''ClientKey''</code>에 일시적으로 접근할 수 있는데, 이는 <code>H(''ClientKey'')</code>로 암호화되어 있다.

클라이언트는 <code>''password''</code>만 필요로 한다.

'''채널 바인딩'''

채널 바인딩이란 상호 인증을 제공하는 [[OSI 모형|애플리케이션 계층]]을 하위(주로 암호화) 계층에 '바인딩'하여 연결의 끝점이 두 계층에서 동일함을 보장함으로써 [[중간자 공격]]을 방지하는 전략을 말한다. 채널 바인딩에는 두 가지 일반적인 방향이 있다: 고유 채널 바인딩과 끝점 채널 바인딩이다. 전자는 특정 연결이 사용되고 있음을 보장하고, 후자는 끝점이 동일함을 보장한다.

여러 채널 바인딩 유형이 있으며, 각 유형은 고유한 채널 바인딩 접두사를 가진다.<ref>{{웹 인용|url=https://datatracker.ietf.org/doc/html/rfc5056#section-7.1|title=On the Use of Channel Bindings to Secure Channels: Registration Procedure|date=November 2007|publisher=IETF|access-date=2023-08-04}}</ref> 각 채널 바인딩 유형은 채널과 끝점에 대한 고유한 정보를 제공하는 채널 바인딩 데이터의 내용을 지정한다. 예를 들어, tls-server-end-point 채널 바인딩의 경우 서버의 TLS 인증서가 이에 해당한다.<ref>{{웹 인용|url=https://datatracker.ietf.org/doc/html/rfc5929#section-4|title=Channel Bindings for TLS: The 'tls-server-end-point' Channel Binding Type|date=July 2010|publisher=IETF|access-date=2023-08-04}}</ref> SCRAM을 애플리케이션 계층으로 사용하는 채널 바인딩의 예시적 사용 사례로는 [[전송 계층 보안]](TLS)을 하위 계층으로 사용하는 경우가 있을 수 있다. TLS는 통신이 암호화되어 있어 수동적 도청을 방지한다. 그러나 클라이언트가 서버를 인증하지 않으면(예: 서버의 인증서를 확인하지 않음) 중간자 공격을 방지할 수 없다. 이를 위해 끝점들은 서로에게 자신의 신원을 보장해야 하는데, 이는 SCRAM에 의해 제공될 수 있다.

gs2-cbind-flag SCRAM 변수는 클라이언트가 채널 바인딩을 지원하는지 여부, 또는 서버가 채널 바인딩을 지원하지 않는다고 생각하는지를 지정하고, c-bind-input에는 gs2-cbind-flag와 함께 채널 바인딩 고유 접두사 및 채널 바인딩 데이터 자체가 포함된다.

채널 바인딩은 SCRAM에서 선택 사항이며, gs2-cbind-flag 변수는 [[다운그레이드 공격]]을 방지한다.

서버가 채널 바인딩을 지원할 경우, 광고된 SCRAM 알고리즘 이름에 '-PLUS' 문자 시퀀스를 추가한다.

== 장점 ==

* 강력한 비밀번호 저장: 올바르게 구현될 경우, 서버는 비밀번호를 솔티드, 반복 해시 형식으로 저장할 수 있어 오프라인 공격을 더 어렵게 만들고 데이터베이스 유출의 영향을 감소시킨다.<ref>{{웹 인용|url=https://www.isode.com/whitepaper/scram-a-new-protocol-for-password-authentication/|title=SCRAM: A New Protocol for Password Authentication|date=19 May 2010|access-date=2024-05-27}}</ref>
* 단순성: SCRAM의 구현은 DIGEST-MD5보다<ref>{{웹 인용|url=https://datatracker.ietf.org/doc/html/rfc6331|title=RFC6331: Moving DIGEST-MD5 to Historic|date=July 2011|publisher=IETF|access-date=2023-08-04}}</ref> 더 쉽다.<ref name="ayena-digest2">{{웹 인용|url=https://ayena.de/blog/scram-digest-md5/|title=Scram DIGEST-MD5!|last=Tobias Markmann|date=2 December 2009|archive-url=https://web.archive.org/web/20210417232208/https://ayena.de/blog/scram-digest-md5/|archive-date=2021-04-17|url-status=dead|access-date=2023-08-04}}</ref>
* 국제적 상호운용성: RFC는 CRAM-MD5와 달리 사용자 이름과 비밀번호에 UTF-8을 사용하도록 요구한다.<ref name="ayena-digest2" /><ref>{{웹 인용|url=https://datatracker.ietf.org/doc/html/draft-ietf-sasl-crammd5-to-historic-00|title=CRAM-MD5 to Historic|date=November 2008|publisher=IETF|access-date=2023-08-04}}</ref>

* 전체 로그인 과정에서 비밀번호의 솔티드 및 해시된 버전만 사용되고 서버의 솔트가 변경되지 않기 때문에, 비밀번호를 저장하는 클라이언트는 해시된 버전을 저장할 수 있으며 공격자에게 평문 비밀번호를 노출시키지 않는다. 이러한 해시된 버전은 한 서버에 바인딩되어 있어 비밀번호 재사용에 유용하다.<ref>{{웹 인용|url=http://ayena.de/blog/sleep-tight-at-night-knowing-that-your-passwords-are-safe/|title=Sleep Tight at Night Knowing That Your Passwords Are Safe|last=Tobias Markmann|date=2023-08-04|archive-url=https://web.archive.org/web/20210417234209/http://ayena.de/blog/sleep-tight-at-night-knowing-that-your-passwords-are-safe/|archive-date=2021-04-17|url-status=dead}}</ref>

== 각주 ==
{{각주|30em}}

== 외부 링크 ==

* {{IETF RFC|5802|link=no}}, 솔티드 시도 응답 인증 메커니즘(SCRAM) SASL 및 GSS-API 메커니즘
* {{IETF RFC|5803|link=no}}, 솔티드 시도 응답 인증 메커니즘(SCRAM) 비밀을 저장하기 위한 경량 디렉터리 액세스 프로토콜(LDAP) 스키마
* {{IETF RFC|6120|link=no}}, 확장 가능한 메시징 및 프레즌스 프로토콜(XMPP): 코어
* {{IETF RFC|6331|link=no}}, DIGEST-MD5를 역사적 상태로 이동
* {{IETF RFC|7677|link=no}}, SCRAM-SHA-256 및 SCRAM-SHA-256-PLUS: 단순 인증 및 보안 계층(SASL) 메커니즘
* {{IETF RFC|7804|link=no}}, 솔티드 시도 응답 HTTP 인증 메커니즘
* {{IETF RFC|8600|link=no}}, 보안 정보 교환을 위한 확장 가능한 메시징 및 프레즌스 프로토콜(XMPP) 사용
* {{IETF RFC|8621|link=no}}, 메일용 JSON 메타 애플리케이션 프로토콜(JMAP)
* {{IETF RFC|9051|link=no}}, 인터넷 메시지 액세스 프로토콜(IMAP) - 버전 4rev2
* {{IETF RFC|9266|link=no}}, TLS 1.3용 채널 바인딩
* [https://datatracker.ietf.org/doc/html/draft-ietf-sasl-crammd5-to-historic-00 draft-ietf-sasl-crammd5-to-historic-00] & [https://datatracker.ietf.org/doc/html/draft-zeilenga-luis140219-crammd5-to-historic-00 draft-zeilenga-luis140219-crammd5-to-historic-00], CRAM-MD5를 역사적 상태로
* [https://datatracker.ietf.org/doc/html/draft-melnikov-scram-sha-512 draft-melnikov-scram-sha-512], SCRAM-SHA-512 및 SCRAM-SHA-512-PLUS 단순 인증 및 보안 계층(SASL) 메커니즘
* [https://datatracker.ietf.org/doc/html/draft-melnikov-scram-sha3-512 draft-melnikov-scram-sha3-512], SCRAM-SHA3-512 및 SCRAM-SHA3-512-PLUS 단순 인증 및 보안 계층(SASL) 메커니즘
* [https://datatracker.ietf.org/doc/html/draft-melnikov-scram-bis draft-melnikov-scram-bis], 솔티드 시도 응답 인증 메커니즘(SCRAM) SASL 및 GSS-API 메커니즘
* [https://datatracker.ietf.org/doc/html/draft-ietf-kitten-scram-2fa draft-ietf-kitten-scram-2fa], 2단계 인증을 위한 솔티드 시도 응답(SCRAM) 확장
* [https://datatracker.ietf.org/doc/html/draft-melnikov-sasl2 draft-melnikov-sasl2], 확장 가능한 단순 인증 및 보안 계층(SASL)
* [https://github.com/scram-sasl/info/issues/1 State of Play], SCRAM SASL 지원에 대한 더 자세한 내용

[[분류:암호 프로토콜]]