이전 글 이후에 진도는 나가야 겠는데 프로토콜이 생각처럼 잘 안만들어져서 계속 이 핑계 저 핑계 대며 미뤄왔던 죠쉬 입니다. 이대로 놀게영 필자에서 짤리는가 하고 슬퍼하던 와중에 OAuth라는 괜춘한 놈을 발견 했습니다. 사실, 내가 왜 이런 참한 규수를 그동안 모르고 있었는지 부끄러울 정도로 깔쌈한 인증 프로토콜을 가지고 있네요. 이 OAuth의 인증 절차에 대해 이야기 하면서 숨겨져있는 방어책에 대해 이야기 해 보도록 하겠습니다.
0. 참고사항
OAuth Specification: http://oauth.net/core/1.0/
1. OAuth 사전지식
사용되는 용어
프로바이더(Service Provider): OAuth를 제공하는 웹 어플리케이션, i.e. Twitter, Facebook, Daum
사용자(User): 서비스 제공자의 웹 사이트에 계정을 갖고있는 사용자.
컨슈머(Consumer): OAuth를 이용해 서비스 제공자의 인증절차를 사용하고자 하는 웹 사이트나 어플리케이션
보호된 자원(Protected Resources): 서비스 제공자가 관리하는 데이터로, 컨슈머는 인증을 받아야 액세스 할 수 있는 데이터
컨슈머 키(Consumer Key): 컨슈머가 서비스 제공자에게 스스로를 식별하기 위해 사용되는 값
컨슈머 비밀정보(Consumer Secret): 컨슈머가 컨슈머 키에 대한 소유권을 확인하기 위해 사용되는 정보, 암호화 키 정도로 생각하면 된다.
리퀘스트 토큰(Request Token): 컨슈머가 사용자에게서 권한을 얻고 액세스 토큰을 확보하기 위해 사용되는 값
액세스 토큰(Access Token): 컨슈머가 특정 사용자와 연관되어 있는 보호된 자원에 액세스 하기 위해 필요한 값
토큰 비밀정보(Token Secret): 컨슈머가 주어진 토큰의 소유권을 확인하기 위해 사용되는 값
OAuth 프로토콜의 매개변수(OAuth Protocol Parameters): oauth_ 로 시작되고, http/https 프로토콜을 통해 전달되는 매개변수들
2. OAuth Authentication Flow Diagram
3. OAuth에서 사용되는 방어기법들
위의 표 에서는 뭔가 주저리 주저리 길게 설명을 해 놓았는데, OAuth가 사용하는 절차가 꽤 길어 보인다. 그러나 여기에서 설명하려고 하는 것은 'Request Token'을 얻어내는 절차 뿐이다. 먼저, 컨슈머가 서버에게 리퀘스트 토큰을 얻기 위해 전송하는 매개값 들을 살펴 보도록 하자. 컨슈머가 보내는 정보중에 oauth_consumer_key, oauth_signature, oauth_timestame 그리고 oauth_nonce 이 넷이 사용자의 정보를 보호하고, 사용자가 원치 않는 일이 벌어지지 않도록 막기 위해 사용되는 정보들이다.
가장먼저, oauth_consumer_key는 컨슈머와 프로바이더 간에 공유하는 비밀키 이다. 이 키는 재주껏 잘 보호 해야 한다. 이 키가 노출되면 그걸로 끝이다. 아무리 강력한 암호화 기법이 있어도 이 키를 공격자에게 빼앗긴다면 그냥 게임 오버다... 는 훼이크 이고, 프로바이더 입장에서는 상대가 미리 자신과 약속한 상대가 맞다는 것을 확인하고, 그 상대의 정보를 찾기 위한 용도로 사용된다.
이게 어디서 훼이크질이야!
실제로 인증하기 위해 사용되는 비밀키는 oauth_consumer_secret 이고, 이 키야 말로 공격자에게 노출되면 게임오버이다. 그러므로, 혹시나 클라이언트에 이 키를 심으려고 생각한다면 잘 생각해서 재주껏 숨겨야 한다. 만일 가능하다면 어차피 웹에 한 번 노출 되어야 하니 웹서버를 하나 두고 OAuth 프로세스는 웹서버에서 하고, 그 결과만 클라로 옮겨오는 방법을 쓰는 것도 나쁘지 않다.
oauth_signature는 공격자가 요청 내용의 일부를 변형해서 보내는 것을 막기 위해 사용된다. 이전에도 한번 언급을 했지만 HASH 함수들은 입력 데이터의 한 비트가 바뀌면 결과 값이 완전히 변경되는 데다가, 특정 결과 값을 얻어낼 수 있는 데이터를 찾아내는 것도 아주 어렵다. 그렇기 때문에 전송하는 데이터를 전부 다 해시 함수의 입력으로 넘겨주게 되면 공격자는 그 내용을 알더라도 조작이 불가능 하다. 조작을 할 수 있는 경우는 서명할 때 사용되는 oauth_consumer_secret을 알아내는 방법 뿐이다.
서명은 다음과 같은 방법으로 만든다. 아래 식에 사용된 버티컬 바는 비트 연산이 아니라 문자열의 합체!(Concatenation)이다.
string = |
oauth_callback | oauth_consumer_key | oauth_nonce | oauth_signature_method | oauth_timestamp | oauth_version |
key = | oauth_consumer_secret | oauth_token_secret |
일 때 | |
oauth_signature = |
HASH(string, key) |
이다 |
그냥 한 마디로 설명 하자면, OAuth 가 필요로 하는 데이터들을 끌어 모아 시크릿 키로 해시값을 계산해 낸다. 그러므로, 공격자가 내용을 고쳐서 공격하려고 한다면, oauth_consumer_secret을 알아야 하며, 일단 인증된 다음에는 oauth_token_secret 까지 알아야 전달되는 내용을 수정할 수 가 있다. 물론, 수정할 수 없다는 말은 그 내용을 볼 수 없다는 말과는 완전히 다른 말이다. 위의 플로우 다이어그램에서 보듯이 전달되는 내용들을 보는 데에는 아무런 제한이 없다. 1
다음 설명을 하기 전에 다음 그림을 잠시 보자. 다음 그림은 흔히 시행되는 Replay Attack 되시겠다. 공격자 맬러리는 Alice의 아이디와 패스워드도, 서명을 만들기 위한 시크릿 값도 알아낼 필요 없다. 그냥 앨리스가 밥에게 보내는 메시지를 캡쳐 해두었다가 필요할 때 재 전송 하는 것으로 앨리스 인 척 할 수 있게 된다. 2
인터넷의 흔한 리플레이 어택
oauth_timestamp와 oauth_nonce는 바로 이 Replay Attack을 방어하기 위한 용도로 추가된 내용이다. oauth_timestamp는 컨슈머가 프로바이더에게 보내는 정보를 리플레이 공격하지 못하도록 하는 용도로 사용된다. 프로바이더는 컨슈머가 보낸 요청에 포함된 oauth_timestamp를 보고 요청이 제한된 시간 내에 전송되었는지 여부를 확인함으로서 재전송 공격여부를 판별한다. 예를 들자면, 프로바이더는 컨슈머가 보내준 시간을 보고 요청이 1초 이내에 도착했는지 확인을 해서 1초가 넘었다면 일단 재전송 공격이 시도된 것으로 본다는 것이다. 3
타임 스탬프를 이용한 리플레이 어택의 방어
얻다대고 혀짧은 소리야!
그러나 여기에도 문제가 하나 있다. 위의 예 에서와 같이 1초를 그 한계시간으로 전했다면, 공격자가 인증 패킷을 가로채서 1초 내에 재 전송을 한다면 공격에 성공할 가능성이 아예 없는 것은 아니다.
타임 스탬프를 이용한 방어의 회피법, 그냥 조낸 빨리 보내면 된다.
그러한 공격을 막기 위해 사용되는 값이 oauth_nonce이다. nonce는 원타임 패스워드로 이해하는 경우도 있는, '한 번만 사용되는 값'을 의미한다. 프로바이더는 마지막으로 사용된 oauth_nonce를 저장해두고, 컨슈머가 제한 시간 내에 보내온 요청값이 마지막으로 사용되었던 nonce 값과 동일한지를 확인하여 동일한 값이라면 재전송 공격을 한 것으로 간주한다. 이렇게 함으로서 공격자가 패킷을 받자마자 재 전송 함으로서 제한 시간 내에 공격 패킷을 보내는 경우에 공격이 성공되는 것을 막는다. 4
타임 스탬프와 Nonce를 이용한 리플레이 어택의 방어
nonce를 사용하는 경우에, 공격자가 컨슈머가 보낸 패킷을 가로채는 방법을 이용해 공격에 성공하려면, 공격자의 패킷이 컨슈머가 보낸 패킷보다 먼저 프로바이더에게 공격자의 패킷을 보내야만 한다. 조건을 극도로 제한하면 이 방법으로 공격에 성공할 수 도 있겠지만, 일반적인 상황에서는 쉽지 않을 것 이다.
위의 절차를 거쳐 '안전하게' '실제 권한을 요청할 자격이 있는 컨슈머 임을 확인한' 다음에 사용자는 서비스 프로바이더 - 즉, 페이스 북 이나 트위터 같은 서비스 제공자의 사이트에 가서 필요한 경우 로그인을 한 후에, 컨슈머에게 권한을 부여할지 여부를 결정할 수 있게 된다. 사용자는 자기가 의도하지 않은 방향으로 자신의 정보가 제공 되거나 영역이 침범되지 않을 것을 확신 할 수 있게 된다.
마지막으로, 프로토콜을 공격하는 방법중에 가장 잘 알려진 두 가지 방법은 위에서 구구절절히 설명한 Replay Attack과 Man In The Middle Attack이 있다. 위에서 설명 했듯이 OAuth는 Replay 공격에 대한 대책이 잘 마련 되어있다. 사실 Replay Attack은 쓰기 쉬운 공격 법 이기 때문에 로그인 아이디와 패스워드를 날릴때 단순히 암호화 했다고 해서 안심하지 말고, 습관적으로 time stamp와 nonce를 추가해 주면 어이없이 보안이 뚫리는 상황이 만들어지지는 않을 것이다.
또한, 인증서를 사용해서 SSL을 통해 데이터를 전송한다 해도 비공인 인증서라면 MITM(Man In the Middle) Attack으로 뚫릴 수 있다. 그러나, SSL을 통해 전달되는 데이터를 한 번 더 살짝 위에서 설명한 OAuth의 테크닉으로 싸서 보내면 좀 더 안전해 질 수 있다. 정확히는 SSL의 MITM 공격이 성공 하더라도 공격자는 할 수 있는 일이 없게 된다. 왜? OAuth 자체가 MITM에도 안전하기 때문이다. OAuth는 미리 공유한 oauth_consumer_secret을 이용하기 때문에 설령 공격자가 전달되는 내용을 볼 수 있다 하더라도 서명에 포함된 정보의 수정이 불가능 하기 때문이다. 즉, 궂이 SSL을 사용하지 않더라도, OAuth의 방법을 사용한다면 공격자는 미리 서버와 클라이언트 간에 공유된 비밀 정보를 알아내지 않는 한 전달되는 정보를 수정할 수 없으므로 MITM에 강해진다. 그러니 SSL로 보내는 데이터를 OAuth에서 사용한 방법으로 살짝 싸주면 공격 당해도 안심! 인거다. 이경우, 패스워드를 숨겨서 전달하고 싶다면 다음과 같이 해주면 깔끔하고 안전하게 전달(?)할 수 있다. 5
string = | userid | nonce | timestamp |
key = | secret | userpassword |
일 때 | |
signature = | HASH(string, key) |
이다. |
|
더 이상 심플 할 수 없을 만큼 심플하다. 그냥 키의 제일 마지막에 사용자의 패스워드를 덧붙여서 키로 사용하면 된다. 서버 쪽에서 계산해낸 signature와 클라이언트에서 보낸 signature가 다르다면 패스워드가 틀린 것 이다. 뭐, 시크릿을 공유하는 것 조차 싫다면, 그냥 사용자의 패스워드를 키로 사용해주면 된다. string에 nonce와 timestamp가 들어가므로 설령 같은 id와 password가 사용된다 해도, 공격자가 훔쳐보는 내용은 매번 다른 데이터가 암호화 되어 전달 되는 것 처럼 보일 것이다.
4. 결언
설령 OAuth 자체를 사용할 일이 없다 하더라도 OAuth에서 사용한 프로토콜은 게임에서 사용되는 데이터를 주고 받을 때 그 정보를 숨겨야 하는 상황에서, 혹은 전달되는 데이터를 임의로 수정하는 일이 없어야 하는 상황에서 아주 유용하게 사용할 수 있다. time stamp, nonce 그리고 HASH를 이용한 signature의 사용법을 익히는 것 만으로도 꽤 안전한 통신을 즐길 수 있으리라 믿는다. OAuth는 수 많은 전문가들이 오랜시간 고민해서 만든 프로토콜 스펙이니 만큼, 이걸 뜯어 보는 것 만으로도 충분히 안전한 통신 프로토콜을 만드는데 필요한 지식들을 얻을 수 있으리라 본다.
추가적으로, OAuth 자체를 칭찬한다면, OAuth는 인증(Authentication)과 권한부여(Authorization)를 분리해 준다는 것 이다. 인증 - 즉 로그인은 무조건 프로바이더 페이지를 통해야만 하도록 함으로서 사용자의 비밀 정보가 빠져나갈 가능성 자체를 차단해 주었다. 그리고 권한부여 라는 절차를 거쳐서 서드파티가 제공하는 기능들을 편안하게 이용할 수 있도록 하였다.
사실, OAuth 자체는 웹 환경에 최적화 되어있는 프로토콜 이기에 앱에 도입 하기엔 불편함이 많은 것은 사실이다. 위에서 말한 장점인 '반드시 프로바이더 페이지를 통해서 로그인'하는 기능을 앱에서 사용하려면 실제 사용할 앱 외에 브라우저를 실행시키고, 브라우저에서 제공하는 정보를 다시 앱에 입력해야 하는 절차를 거쳐야만 한다. 당연히 사용하려는 프로그램 외의 다른 프로그램을 실행시켜야 한다는 것은 부담이 될 수 도 있고, 불편한 것은 사실이다. 그러나, 비록 조금 불편 하다 할 지라도 고객은 새로운 사이트에 별도의 회원가입을 하지 않아도 된다는 잇점에 대해선 만족할 것이며, 기존에 회원 가입했던 트위터나 페이스북을 통해 인증을 받을 뿐 아니라, 그런 서비스를 함께 쓸 수 있다는 것에 대해서도 만족할 것이다.
OAuth의 메인 목적은 인증이 아니라 권한부여 쪽이다. 그러므로 서비스 프로바이더의 인증 기능을 이용하기 위해 OAuth를 이용하는 것은 그다지 좋은 생각은 아니다. 만약 인증을 받는 쪽에 대하여 고민을 하고 있다면 OAuth가 아니라 Open ID에 관심을 가져보길 권한다.
* * *
OAuth 덕분에 이 미천한 필자의 놀게영 체제 기간이 늘었습니다. 우화화화홧! 다음 번 부터는 보안 쪽 말고 완전 초보자를 위한 초보자의 게임 제작 삽질기 라도 올려야 겠습니다. 그러다가 OAuth 같이 섹쉬한 보안 프로토콜을 발견하면 소개해 드리는 쪽으로 가닥을 잡기로 했습니다. 이상, 놀게영에서 천년만년 필자질을 하려고 음모를 꾸미고 있는 죠쉬 였습니다.
- OAuth를 실제로 사용하고자 하는 사람들이 주의할 점이 있다면, 여러번 반복하게 되는데, 해시 함수는 한 비트만 달라져도 결과가 완전히 달라지므로, 해시 연산을 하기 위한 데이터의 순서가 아주 중요하다. [본문으로]
- Replay Attack ; 이전에 정당한 사용자가 보낸 메시지를 캡쳐하여 저장해 두었다가 단순 재전송 함으로서 공격하는 방법 ; 아이디와 패스워드가 암호화 되어 전송되는 경우, 공격자는 정당한 사용자가 입력한 아이디와 패스워드를 알아내기 위해서는 암호를 깨야 한다. 그러나, 아이디와 패스워드가 항상 같은 방법으로 암호화 된다면, 공격자는 궂이 암호를 깰 필요없이 암호화된 아이디와 패스워드를 그냥 재전송 해주는 것으로 공격에 성공할 수 있다. [본문으로]
- 물론, 실제로 1초라는 것은 아닙니다. 그 시간은 서비스 프로바이더가 임의로 결정하게 됩니다. [본문으로]
- nonce는 원래 그 자체로도 replay attack을 막는 용도로 사용이 됩니다. 어떤 데이터를 서버로 암호화 해서 보낼 때 서버에서 nonce를 받아 그 값을 암호화 하는 데이터에 포함시켜 보내줌으로서 replay attack을 막습니다. 물론 서버는 클라이언트가 nonce를 요청할 때마다 다른 값을 리턴해 주어야만 replay attack을 막을 수 가 있겠지요 [본문으로]
- 실제로 패스워드 자체가 보내지는 것은 아니니 패스워드를 전달 한다고 말 할 수는 없다. 단지 패스워드를 검증하기 위한 정보가 전달될 뿐이다. [본문으로]
'기타' 카테고리의 다른 글
온라인 MMO는 중앙은행의 꿈을 꾸는가? (1) | 2012.06.30 |
---|---|
Shoot to Thrill - 가마수트라 아티클 번역본 (0) | 2012.06.22 |
유용한 프로그램 Hotkey Master (2) | 2012.06.13 |
컨텐츠 소모 방지 시스템 (7) | 2012.05.23 |
Graphics vs Aesthetics (5) | 2012.05.12 |