1. HPKP (HTTP Public Key Pinning) 이란?

이미 잘 알려져 있다시피 SSL/TLS 로 이루어진 암호화 통신은 중간자 공격 (Man in the Middle) 에 취약합니다. 특히 요즘에는 여러 보안장비들이 SSL Decryption 기능을 제공하며 이 중간자 공격 (Man in the Middle) 형태로 SSL 및 TLS 세션을 복호화 하여 내용을 검증하도록 되어 있습니다. 이 때 보안장비는 공격자와 마찬가지로 암/복호화에서 사용할 인증서를 임의로 교체하여 중간에서 데이터를 복호화 하여 검증 하도록 구성되어 있습니다.

인증서를 교환을 통한 중간자 공격으로 Plain Text를 얻어낸 Intercaption Proxy

위 예제와 같이 클라이언트 (유저) 는 최종적으로 중간의 보안장비 또는 공격자가 제공한 인증서를 기반으로 암호화 통신을 진행하게 되며 이를 통하여 Man in the Middle 에 노출되게 됩니다. 

그렇다면 클라이언트 (유저) 가 SSL/TLS 암호화 통신에 사용할 인증서를 최종서버의 인증서로 고정 (Pinning) 을 해둔다면 어떨까요? 그렇다면 중간에서 보안장비 또는 공격자가 제공한 임의의 인증서를 거부하고 말이죠. 이렇게 특정 사이트 접속시 암호화 통신을 할 인증서를 고정 (Pinning) 하는 것을 HPKP (HTTP Public Key Pinning) 이라고 합니다.

클라이언트 (유저)는 HPKP를 통하여 접속할 사이트의 합법인증서 체인과 공격자의 변조인증서 체인을 구분 할 수 있음

 

쉽게 말해 클라이언트가 사용하는 브라우저에서 특정 웹사이트 (예를 들어 rsec.kr) 로 접속 할 때, 나는 특정 웹사이트 접속시 (rsec.kr 접속시) A라는 인증서만 사용하겠다 라고 고정하여 두는 것이라고 보면 됩니다. 보안장비 또는 공격자가 B라는 인증서로 대신 보내주어 통신을 유도 할 경우 브라우저는 이를 거부하게 됩니다. 

 

2. HPKP (HTTP Public Key Pinning) 핀 확인 및 생성 방법

HPKP를 웹사이트에 적용하기 위해서는 사용하고 있는 인증서와 인증서 체인에 대한 PIN을 확인하여야 합니다. 인증서의 PIN을 확인하는 방법은 여러가지가 있는데 간단하게 아래와 같이 SSL 검증 사이트를 통하여 인증서 체인의 PIN을 확인 할 수 있습니다.

  • OpenSSL 명령을 이용하여 수동으로 확인
root@rsec:/rsec.kr# openssl x509 -pubkey < fullchain.pem | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
O2ELvEUIPxmsA7vJAbZxoiABBkonE3U+INXiuRkWTu4=

이렇게 웹사이트에서 사용될 인증서를 Base64 형식으로 해쉬한 값을 이용하여 웹사이트 암호화에 사용할 인증서를 고정 (Pinning) 할 수 있게 됩니다. 

하지만 인증서는 항상 유효기간이 있고 (rsec.kr 인증서의 경우 현재 유효기간이 74일 남음) 인증서가 만료된 이후에는 인증서가 갱신 됩니다. 다시말해 이 인증서가 갱신됨으로 인증서를 기반으로 생성한 Pin 값도 바뀔 수 밖에 없습니다. 따라서 이러한 부분을 해결하기 위하여 Backup Pin을 생성하여 사용하게 됩니다. 

  • Backup Pin  생성 방법
root@rsec:/rsec# openssl genrsa -out rsec.kr.backup.key 4096
Generating RSA private key, 4096 bit long modulus
..................................................................++
..................................................++
e is 65537 (0x10001)
root@rsec:/rsec# openssl req -new -key rsec.kr.backup.key -sha256 -out rsec.kr.backup.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:KR
State or Province Name (full name) [Some-State]:South Korea
Locality Name (eg, city) []:SEOUL
Organization Name (eg, company) [Internet Widgits Pty Ltd]:RSEC
Organizational Unit Name (eg, section) []:RSEC
Common Name (e.g. server FQDN or YOUR name) []:rsec.kr
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
root@rsec:/rsec.kr# openssl req -pubkey < rsec.kr.backup.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
T3QGJPkeB/a0wqVRQLP1AMsOTOxc8OJOR6nczDNd7H4=

이렇게 생성하여 마지막에 나온 Base64 코드 (T3QGJPkeB/a0wqVRQLP1AMsOTOxc8OJOR6nczDNd7H4=) 를 이용하여 Backup Pin으로 설정하면 됩니다. 

 

3. HPKP (HTTP Public Key Pinning) 적용하기

HPKP 헤더의 형식은 아래와 같습니다.

Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"]
  • pin-sha256 : 위에서 생성된 Base64 형식의 PIN입니다. (Subject Public Key Information, SPKI)
  • max-age : 브라우저에서 PIN 정보를 유지할 시간입니다. 초단위로 설정합니다. 
  • includeSubDomains : SubDoamin 까지 HPKP를 확장시키고 싶을 때 사용합니다. (옵션)
  • report-uri : pin validation이 실패했을 경우 report 할 URI를 적어줍니다. (옵션)

NGINX는 아래와 같이 설정합니다.

        add_header Public-Key-Pins 'max-age=31536000;
                pin-sha256="O2ELvEUIPxmsA7vJAbZxoiABBkonE3U+INXiuRkWTu4=";
                pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=";
                pin-sha256="Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=";
                pin-sha256="T3QGJPkeB/a0wqVRQLP1AMsOTOxc8OJOR6nczDNd7H4=";
                includeSubDomains';
  • max-age=31536000 : HPKP의 유지시간 (1년)
  • pin-sha256=”O2ELvEUIPxmsA7vJAbZxoiABBkonE3U+INXiuRkWTu4=”; : rsec.kr 인증서의 PIN
  • pin-sha256=”YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=”; : rsec.kr 인증서의 Intermediate CA의 PIN (Let’s Encrypt)
  • pin-sha256=”Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=”; : rsec.kr 인증서의 Root CA의 PIN
  • pin-sha256=”T3QGJPkeB/a0wqVRQLP1AMsOTOxc8OJOR6nczDNd7H4=”; : rsec.kr 인증서의 Backup PIN
  • includeSubDomains’; : SubDomain 까지 HPKP 확장 (옵션)

 

설정 및 Nginx 데몬을 재시작 한 후, 아래와 같이 HPKP 적용여부를 확인 할 수 있습니다. 

HPKP 적용 후, Response 헤더에 Public-key-Pins 가 포함된 모습

https://www.htbridge.com/ssl 를 통하여 HPKP 적용 및 PIN의 매치여부 확인

 

4. 기타

HPKP는 이렇게 웹서버의 Response Header에 인증서의 PIN값을 브라우저에 전달하고, 전달된 PIN값을 근거로 암호화 통신에 사용할 인증서를 제한함으로써 DigiNotar 와 같이 잘못된 인증서를 사용하여 암호화 통신을 하거나 중간자 공격을 막을 수 있도록 구성되었습니다.

일반적으로 HPKP의 효용성은 DigiNotar 사건에서 알려졌는데, DigiNotar는 구글의 Root 인증기관이 아니지만 이를 통하여 구글 인증서가 발급되어 브라우저에서 안전함을 표시하면서 구글통신이 잘못된 인증서로 암호화 되어 통신되었던 사례에서 찾을 수 있습니다.

다른 브라우저는 잘못된 인증서로 통신한 것을 찾을 수 없었지만 구글은 인증서가 고정 (Public Key Pinning) 이 되어 DigiNotar를 통한 인증서로 구글 통신을 실행하던걸 경고표시 해 주었으며 이를 통하여 HPKP의 효용성이 다시한번 확인 되었습니다.

관련 사례는 아래 링크의 2번 항목을 참고하시기 바랍니다. 

DNS CAA레코드는 무엇일까?

이상으로 HPKP의 개념과 어떻게 설정하는 바를 확인해 보았습니다. 읽어주셔서 감사합니다. 

 

 

 

There are currently no comments.