[Security-11] TLS 1.2 Pt.1

유영석·2022년 11월 14일
0

Security

목록 보기
11/12
post-thumbnail

인터넷을 사용하는 여러분들이라면 한 번쯤 TLS 라는 것을 들어보았을만 합니다. TLS(Transprot Layer Security) 는 인터넷 상의 커뮤니케이션을 위한 개인 정보와 데이터 보안을 용이하기 위해 설계되어 현재 가장 널리 채택된 보안 프로토콜입니다. 우리가 사용하는 웹 브라우저는 서버와의 커뮤니케이션을 암호화하기 위해 TLS를 사용하고 있습니다.

제가 사용하고 있는 구글 크롬 브라우저만 하더라도 주소창 좌측에 잠겨진 자물쇠 표시로 현재 TLS 에 의한 보안이 이루어지고 있음을 시각화해서 보여줍니다. 앞선 글에서 웹 보안에서 다룰 때 HTTPS 가 보안에 강한 점이 HTTP 에 TLS 가 묶여서라고 일방적으로 설명만 들었는데, 이번 글에서 TLS 에 대한 보다 구체적인 이해를 도모하려 합니다.

다시 내용을 상기하자면 인터넷, 즉 웹이 들어오면서 유용함과 동시에 많은 보안적 취약함이 드러나기 시작했습니다. 대표적으로 크게 아래 4 가지의 위험이 존재하고 그에 대한 해결책도 지금까지 글에서 설명을 드렸습니다.

  • Integrity - Cryptographic checksums (해시)
  • Confidentiality - Encryption (Cipher 내용)
  • Denial of service - Difficult to prevent (ㅠㅠ)
  • Authentication - Cryptographic techniques (이전 글)

이런 공격들과 해결책들을 한 데 모으는 웹 보안 프로토콜의 필요성이 대두되면서 여러 프로토콜에 대한 시도들이 있었습니다.

가장 먼저는 (a) Network Level(3계층) 즉 IP 영역에 보안 프로토콜을 적용할 생각을 하였습니다. 이름 하여 IPsec 입니다. 어차피 Application Level(5계층) 에 있는 HTTP 는 IP 에 싸여져 있기 때문에, 한마디로 IP 의 payload 중 하나에 불과하기 때문에 IP 의 payload 자체를 암호화하면 자동적으로 HTTP 또한 암호화가 될 거라는 생각에서 비롯된 것이지요. 그런데 이렇게 되버리면 HTTP 뿐 아니라, 예를 들어 FTPSMTP 등 5 계층의 다른 프로토콜들 또한 선택 없이 무조건적으로 다 암호화가 되버리기 때문에 불편함이 발생하게 되었습니다.

그래서 그 다음은 (b) 5 계층의 프로토콜마다 보안 프로토콜을 적용하는 방법을 생각해 내었습니다. 그런데 이렇게 되면 어플리케이션이 10개만 10개 마다 따로따로 프로토콜을 적용해야 되니 비효율성 문제가 금방 새겼습니다.

최종적으로 결국 Transport Level(4계층) 과 Application (5계층) 사이의 5 계층에 보안 프로토콜을 위치 시키게 되었습니다. 예를 들어 HTTP 에서 이전처럼 바로 소켓을 TCP 로 여는 것이 아닌 TLS 의 함수를 호출시키면 그 뒤는 TLS 가 자기 일을 처리하고 TCP 에 넘겨주는 것입니다. 이와 같은 방식으로 어플리케이션 프로토콜마다 자신이 원하는 프로토콜을 쉽게 적용시킬 수 있는 것이죠. 4 계층만 되어도 커널, 즉 OS 의 영역이기 때문에 무언가를 바꾸고 업데이트 하는 것이 굉장히 무거운 일이지만 어플리케이션 영역에 위치하기 때문에 업데이트도 빠르게 되는 기막힌 장점 또한 존재합니다.

다시 한 번 정리하자면, TLS 는 인터넷 영역에서 웹 뿐만 아니라 이메일, 비디오, 보이스 등을 위한 여러 어플리케이션에 secure communication 을 제공해주는 프로토콜입니다. 이를 위해 TLS 는 다음과 같은 함수들을 제공합니다.

  • Authentication among communicating parties
  • Message Integrity
  • Confidentiality by encryption

모두 지금까지의 글들에서 기본 개념에 대해 공부한 내용들이죠? 그럼 이제 본격적으로 TLS 가 어떻게 동작하는지 알아보도록 합시다. 주의깊게 봐야할 포인트는 어떻게 인증하고 어떻게 키를 만드는지에 대한 것입니다.

TLS(Transport Layer Security)

TLS 는 먼저 IP 와 IP, 클라이언트와 서버 간에 하나의 TLS session 이 존재합니다. 그리고 그 사이에 여러 개의 TLS connection 이 있습니다. 매 connection 마다 피치 못하는 overhead 가 발생하게 될 겁니다. 이를 위해 처음 세션을 맺을 때 오버헤드를 가지고 그 뒤에는 이를 가지고 connection 을 하면서 오버헤드를 줄이고 있습니다.

TLS 에는 위와 같이 4 개의 Upper-Layer 와 1 개의 Lower-Layer 로 구성되어 있습니다. 여기서 HandshakeRecord 가 중요한 레이어입니다. Handshake 에서 초기에 서로를 인증하고 세션 키를 나눠가지는 Authentication and key management(AKM) 을 수행하기 때문입니다. 이후에 Record 에서 이를 가지고 데이터를 보낼 때마다 암호화를 하고 ingerity 를 지키는 것입니다.

(여기서 보이는 SSL 은 TLS 의 초기 버전입니다. 다른 말로 SSL 을 발전시킨 것이 TLS인 셈이죠.)

설명드린 TLS 의 Layer 들을 반영한 네트워크 계층 모델은 위와 같습니다. (Application Data는 그림에서 빠져 있습니다.) 말씀드린 대로 SSL/TLS 도 결국 어플리케이션이기 때문에 쉽고 깔고 지울 수 있습니다. 다만 우리가 통상적으로 쓰는 여러 웹 브라우저들에서 자동으로 깔아줄 뿐이죠.

가장 먼저 Record Layer 의 과정을 살펴보도록 하겠습니다.

데이터가 들어오면 이를 16384 바이트 (2142^{14}) 크기로 쪼갭니다. 즉 여러 블럭으로 Fragmentation 한다고 표현할 수도 있습니다. 그리고 Optional 하게 압축을 진행합니다. 그 다음은 HMAC-MD5, HMAC-SHA256 등을 써서 MAC 을 만들어 붙이죠. 그리고 바로 Encryption 을 진행합니다. Handshake 에서 만들어진 세션 키(비밀 키) 를 이용해 symmetric cipher(stream or block cipher) 를 사용해 암호화를 진행합니다. asymmetric 방식은 무겁기도 하고 무엇보다 메시지 크기에 제한이 있으니까요. 그 다음 총 5바이트의 4개 필드로 구성된 헤드를 붙입니다. 구성은 오른쪽 그림과 같습니다.

하지만 이 과정을 자세히 보시면 MAC-then-Encrypt 네요. 우리는 Encrypt-then-MAC 을 해야함을 9 장에서 공부했습니다. 그림은 그 부분에서 잘못된 것이 맞습니다.🙁

Handshake

TCP 에서 3-way-handshake 로 connection 을 맺고 나면 TLS 또한 TLS connection 을 열기 위해 Handshake 를 시작합니다. 말씀드렸던 대로 한쪽만 또는 서로를 인증하고 Record Layer 에서 필요한 키를 나눠가지는 과정을 수행하며, 총 4 개의 단계로 구성되어 있습니다. 아래 그림에서 회색빛이 도는 부분은 필수적이지 않은 메세지입니다. 따라서 최대 13 개, 최소 8 개의 메세지로 구성되어 있습니다. 각 메시지는 하나의 TCP segment 로써 전송되게 되지요. 구체적으로 들어가봅시다.

client_hello 에서는 client 가 보안에 필요한 자신의 여러가지 정보를 제공합니다.

  • TLS version - 사용하는 TLS 버전을 명시합니다.
  • Random 32 bytes - 지나간 것을 막기 위한 4 바이트의 timestamp 와 replay 를 막기 위한 28 바이트의 random number 를 명시합니다.
  • Session ID - 새로운 세션이라면 0 을 이미 존재하는 세션이라면 해당 세션 ID 를 명시합니다.
  • candidates for cipher suite - 클라이언트가 자신이 쓸 수 있는 암호화 알고리즘 리스트를 명시합니다. 서버는 이를 보고 적절한 것을 고릅니다.
  • Compression method - Record Layer 에서 선택적으로 데이터를 압축할 수 있는데 이 때 필요한 compression method 를 공유하기도 하고요. 다만 압축을 잘 사용하지는 않습니다.
  • Extensions - 여러 확장이 있고, 그 중 하나로 Server Name Indication(SNI) 을 기억해 놓으시기 바랍니다.

client_hello 에서 보내는 Cipher Suite 에 들어갈 수 있는 요소는 아래와 같습니다.

  • Key exchange
    RSA, DHE, DH-Anon, ECDHE, PSK...
  • Authentication
    RSA, DSS, ECDSA
  • Encryption
    AES, RC4, 3DES
  • MAC
    HMAC with MD5, SHA1, SHA256

이것들을 차례대로 아래 그림과 같이 명시합니다.

client_hello 에 대한 답으로 서버는 server_hello 를 보냅니다. 구성은 client_hello 와 동일합니다. 다만 random number 는 당연히 서버만의 값으로 독립적입니다. 그리고 Session ID 는 만약 0 으로 들어왔을 시 서버가 만들어서 주고요. CipherSuite 에는 서버가 고른 값이 들어가야 할 겁니다.

이후로 서버는 선택적으로 여러 정보(패킷)을 줄 수도 있습니다. 먼저 Certificate 는 서버가 Man In The Middle 공격을 피하고 자신의 공개키를 증명하기 위해 클라이언트에게 보내는 것으로, 공신력 있는 Trusted Third PartyCertificate Authority(CA) 가 서명한 공개키를 포함합니다. 또한 포함된 Fully Qulified Domain Name(FQDN) (e.g.www.amazon.com) 가 내가 접속하려는 서버의 도메인과 일치할 경우에 유효한 겁니다. 이 Certificate 는 이론적으로는 필수는 아니지만 웬만하면 거의 다 갑니다.

이후에는 만약 DHE 와 같이 server key 가 있다면 줄 수있고, 심지어 클라이언트에게도 서명을 요청하는 certificate_request 를 보낼 수도 있습니다. 우리나라는 이 Handshake 단계에서 client_request 로 서버가 클라이언트를 인증하려 하는 경우가 많습니다. 이것이 바로 여러분들이 한 번쯤 사용해본 공인인증서 입니다. 이후에는 server_hello_done 으로 끝을 알립니다.

그 다음에 클라이언트는 서버가 certificate_request 를 보냈을 경우 여기에 맞는 certificate 를 보냅니다. 그리고 정말 중요한, key 를 공유하는 client_key_exchange 를 보내죠. 그리고 서버의 certificate_verify 는 클라이언트가 certificate 로 보낸 자신의 공개키에 대한 개인키를 가지고 있다는 것을 증명하기 위한 메세지입니다. 지금까지의 handshake 에 대한 내용들은 해싱하여 크기를 줄이고 이것을 자신의 개인키로 암호화한 것입니다. 서버는 이것을 certificate 로 받은 공개키로 푼다음에 자신이 계산한 해싱값과 맞는지 확인함으로 증명되는 것이지요.

이와 같이 키를 공유하고 인증하는 일련의 Negotiation 과정이 끝나고 이제 암호화를 시작한다는 뜻으로 change_cipher_spec(ccs) 를 날립니다. 그리고 Handshake 의 종료를 알리는 finished 부터 암호화를 해서 날리는 것이지요. 이제부터 뒤에서는 암호화가 된 HTTP 가 이루어지는 겁니다. 즉, HTTPS 로 이제부턴 평문이 아니기 때문에 밖에서 확인을 할 수가 없는 겁니다.

Key Exchange 에 대해 좀 더 알아보도록 합다.

RSA 의 경우에Key 는 최초에 클라이언트, 즉 브라우저가 생성합니다. 그리고 서버의 공개키로 암호화해서 서버에게 보내는 것이죠. 서버가 이를 받으면 자신의 개인키로 풀어서 key가 공유될 수 있을 것입니다. 이 방식이 가장 널리 사용되는 방식입니다. 조금 더 자세히 알아볼까요?

먼저 클라이언트는 자신이 정한 random number 와 ServerHello 에서 받은 random number 를 이용하여 48 바이트의 Pre-Master Secret(PMS) 를 만듭니다. 이게 key 입니다. 그리고 Certificate 에 포함된 서버의 RSA 공개키를 사용해서 이를 암호화해서 ClientKeyExchange 로 보냅니다. 그렇다면 서버는 이를 자신의 개인키로 풀겠죠? 그러면 key exchange 는 됬는데 인증은 어떻게 하는 걸까요?

핵심은 클라이언트가 서버가 잘 풀어서 PMS 를 갖고 있는지 확인하는 것이겠죠? 그래서 Server가 마지막 Finished 메세지를 PMS 를 써서 MAC 을 구해 보냅니다. 그러면 클라이언트에서 자신의 PMS 로 구한 MAC 값과 비교를 해서 맞으면 인증이 되는 겁니다. 2번의 RTT(Round Trip Time, 왕복시간) 가 소요되고, 클라이언트만 서버를 인증하는 방식이기에 클라이언트의 certificate 가 필요하지 않습니다. 모든 Handshake 과정을 마치고 데이터들이 https 로 암호화되서 왔다갔다 하는 Record Layer 에서 서버는 Password 를 통해 클라이언트를 인증하는 형태가 되겠습니다.

만약 DHE 라면 어떨까요? RSA 는 클라이언트가 일방적으로 키를 만들어주는데 반해 Diffie-Hellman 은 양쪽을 타협하여 만들기 때문에 더 좋습니다. Ephemeral 이기에 Session Key 가 저장되지 않음으로써 Perfect For Secrecy 를 달성할 수도 있구요.

RSA 와 다른 점은 ServerKeyExchange 가 추가되었다는 점입니다. 여기서 서버는 자신의 Ephemeral DH public key 를 클라이언트에게 주는 겁니다. 그러면 클라이언트가 자신도 이것의 페어인 DH 공개키를 만들어서 ClientKeyExchange 일 때 보내는 것이죠. 물론 이 직전에 클라이언트는 먼저 서버가 준 공개키에 자신의 개인키를 이용해 공유키인 PMS 를 만들겁니다. 그러면 여기서 인증은 어떻게 할까요?

핵심은 서버가 준 이 DH Public Key 가 정말 서버가 만든 값이 맞냐는 겁니다. 인증은 Key Exchange 에도 사용할 수 있었던 RSA 로 합니다. 서버는 자신의 RSA 개인키로 이 공개키를 암호화해서 줍니다. 클라이언트는 서버의 RSA 공개키를 이용해서 이를 풀 수 있으니까요. 그런데 이러면 이 서버의 RSA 공개키를 어떻게 증명할까요? 바로 서버가 보내는 CA 의 서명이 들어간 Certificate 로 검증하는 것이지요. 결과적으로 DH Public Key 를 인증하기 위한 RSA Public Key 를 인증하기 위한 Certificate... 이렇듯이 인증을 위한 인증들이 체인처럼 맞물려 있는 점을 들어 Chain of Trust 라고 하는 겁니다.

이 과정을 조금 더 자세히 나타낸 그림입니다. 클라이언트가 ClientHello 에 DHE 를 하겠다고 Key Exchange Method 로 사용하겠다는 것을 알립니다. 그러면 서버가 자신의 DH private keyx 를 생성하고, 그 다음 매우 큰 소수인 N(앞선 글에서 pp 로 설명) 와 이의 원시근이 G(앞선 글에서 aa 로 설명) 를 만들어서 줍니다. 물론 이들은 자신의 RSA 개인키로 서명한 값과 클라이언트가 가지고 있을 공개키를 증명하기 위한 Certificate 를 보내야겠죠?

마지막으로 이 큰 그림을 보시죠.

TCP 의 handshake 이후에 TLS 의 4 개의 phase, 즉 2번의 RTT 가 걸립니다. 따라서, 서버로부터 요청한 데이터를 하나 받으려면 무려 4번의 RTT 가 걸리는 것입니다...😢 그래서 구글에서 더 빠른 속도를 위해 handshake 가 필요한 TCP 를 버리고 UDP 를 차용하기 시작한 겁니다. 또한, TLS 1.3 에서는 위와 같은 RTT 를 반으로 줄여버렸죠. 그러면 이제 2 번만에, 즉 한 번의 RTT 면 데이터 요청을 바로 보낼 수 있게 된 겁니다. 다만, 네트워크 글에서 얘기했듯이 UDP 는 Reliabilty 를 제공하지 않았죠? 그걸 HTTP 가 하도록 하겠다는 것이 구글의 취지입니다...ㄷㄷ

profile
백엔드 개발자

0개의 댓글