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을 확인 할 수 있습니다.
- https://report-uri.io/home/pkp_hash 을 통하여 확인
- https://www.htbridge.com/ssl/ 을 통하여 확인
- 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번 항목을 참고하시기 바랍니다.
이상으로 HPKP의 개념과 어떻게 설정하는 바를 확인해 보았습니다. 읽어주셔서 감사합니다.