Secure Remote Protocol의 약자이다. 결론적으로 네트워크 상에서 서로 다른 두 엔드포인트 간에 개인정보와 같은 노출되어서는 안되는 민감정보 데이터를 주고받을 때 사용하는 규약인데, 이게 어떤 원리로 동작하는지 그리고 왜 나왔는지를 설명하는 것이 주요 목적이다.
사용자 비밀번호를 예로 들어보자. 사용자 비밀번호를 클라이언트로부터 전송받아서 서버 DB에 적재를 하는 구조가 일반적인데, 가장 먼저 떠오르는 문제점이 바로 서버 DB에 비밀번호를 그대로 저장하면 이게 털리면 끝장난다는 사실이다. 그래서 비밀번호를 받아서 그대로 적재하는 것이 아니라 암호가 털려도 특정할 수 없도록 비밀번호를 암호화하여 저장한다. 이때, 해쉬함수(H)를 많이 사용한다. 즉, H(password)를 DB에 저장하는 것이다. 또한, 비밀번호를 서버단에서 암호화하면 서버에 도달하기 전에 탈취당할 우려가 있으므로 클라이언트 단에서 암호화된 비밀번호를 보내야 한다. 이러면, 사실 문제가 간단하게 해결된 것처럼 보이지만, 문제는 요즘 엄청난 성능을 자랑하는 GPU가 수십억번의 연산을 몇분만에 끝내버리면서 해쉬함수는 더이상 안전하지 않은 수단이 되게 되었다. 암호화된 비밀번호를 저장하든, 그냥 비밀번호를 저장하든 문제는 결국
{key(=input_password) : value(=H(input_password))}의 dictionary꼴이 되어버리게 되고, 따라서, DB만 털린다면 이 dictionary를 풀어버림으로써 보안체계가 뚫릴 수 있는 것이다. 이러한 문제점을 해결하기 위해 등장한 것이 바로 SRP 프로토콜이다.
SRP 프로토콜의 핵심은 서버 DB에 비밀번호를 저장하지 않고, verifier를 저장하는 것이다. verifier는 authentication을 위한 key역할을 하게 되는 놈이다.
x = H(Salt | User_name | Password) 라고 할 때,
verifier = pow(g, x, N)이다. 쉽게 말해서 verifier는 암호화된 비밀번호를 modulo로 나눈 나머지이다. 따라서, 이는 이론상 중복된 값이 나올 수 있다. 서버는 유저가 회원가입시에 이 verifier와 처음에 해쉬함수를 적용할 때 인풋으로 들어간 salt를 둘다 보관한다. SRP프로토콜은 로그인시에 클라이언트가 서버에게 비밀번호를 전송하는 전통적인 방식의 프로토콜이 아니다. 서버와 클라이언트 각각이 특정한 알고리즘에 의하여 ephemeral value를 만들어서 서로에게 전달해주고, 각자가 받은 이 값들을 특정한 공식에 대입하여 해쉬함수를 돌려버리면, 놀랍게도 같은 결과를 얻게 되고, 이렇게 함으로써 login authentication이 성공적으로 이루어지게 된다! 처음에는 어렵지만, 쉽게 생각해서 서버와 클라이언트가 각자 진짜 보물상자를 열 수 있는 열쇠들을 둘다 가지고 있다고 생각하면 쉽다. 원래는 열쇠를 주고 받았다면, 이제는 열쇠로 열 수 있는 임의의 상자를 주고 받는 셈이다. SRP 프로토콜은 안전하지만, 구현복잡성과 잘못 구현하였을시 발생할 영향도를 고려하여 아직까지는 보편적으로 사용되지 않고 있는 프로토콜이다. 하지만, 양자컴퓨터도 나온다는 마당에 안전성을 더욱 확보할 수 있는 방향으로 변화할 것이라고 충분히 예상할 수 있다. AWS에서 Amazon Cognito 서비스를 통하여 확장성이 높고, 안전하게 설계된 SRP프로토콜을 사용하는 서비스를 만들 수 있다.
N = """00:c0:37:c3:75:88:b4:32:98:87:e6:1c:2d:a3:32:
4b:1b:a4:b8:1a:63:f9:74:8f:ed:2d:8a:41:0c:2f:
c2:1b:12:32:f0:d3:bf:a0:24:27:6c:fd:88:44:81:
97:aa:e4:86:a6:3b:fc:a7:b8:bf:77:54:df:b3:27:
c7:20:1f:6f:d1:7f:d7:fd:74:15:8b:d3:1c:e7:72:
c9:f5:f8:ab:58:45:48:a9:9a:75:9b:5a:2c:05:32:
16:2b:7b:62:18:e8:f1:42:bc:e2:c3:0d:77:84:68:
9a:48:3e:09:5e:70:16:18:43:79:13:a8:c3:9c:3d:
d0:d4:ca:3c:50:0b:88:5f:e3"""
N = int("".join(N.split()).replace(":", ""), 16)
g = 2 # A generator modulo N
k = H(N, g) # Multiplier parameter (k=3 in legacy SRP-6)
I = "person" # Username
p = "password1234" # Password
s = cryptrand(64) # Salt for the user
x = H(s, I, p) # Private key
v = pow(g, x, N) # Password verifier
Client create ephemeral value : a
Server create ephemeral value : b
Both server and client knows : N, g, s, v
1. Client -> Server : I, A(= g^a)
2. Server -> Client : s, B(= kv + g^b)
3. Both calculate : u = H(A, B)
4. Client calculate : K = H((B-kg^x)^(a+ux))
(B-kg^x)^(a+ux)
= (kv+g^b-kg^x)^(a+ux)
= (kg^x+g^b-kg^x)^(a+ux)
= (g^b)^(a+ux)
5. Server calculate : K = H((Av^u)^b
(Av^u)^b
= (g^a * v^u)^b
= (g^a * (g^x)^u)^b
= (g^(a+ux))^b
The result of 4,5 is same.