-
[웹] 홈페이지 SSL 인증서 오류 고치기 대소동IT 2021. 11. 23. 12:42
연구실 홈페이지 관리를 맡고 있는데 얼마 전부터 ssl 인증서에 문제가 생겨서 이걸 해결해보려는 과정에서 SSL에 대해 공부를 하게 되었다... 결국은 해결 못했지만....
SSL이란? SSL 인증이란?
SSL은 클라이언트와 서버 간에 데이터를 안전하게 주고 받기 위한 암호화 통신 프로토콜이다.
SSL 인증 과정은 다음과 같다.
먼저, 서버는 Certificate Authority (CA)에게서 발급받은 (CA의 private key로 암호화된) SSL 인증서를 갖고 있다. 이는 CA의 오로지 public key로만 복호화될 수 있고, 복호화가 잘 되는지 확인함으로써 '이 서버는 CA에게 인증받은 신뢰가능한 서버구나!'라는 것을 알 수 있다.
이러한 상황에서 클라이언트가 서버와 통신하기 전 아래와 같은 과정을 거친다.
- 클라이언트 측에서 서버로 SSL 세션에 대한 요청
- 서버는 CA의 private key로 암호화된 ‘서버의 SSL 인증서’를 클라이언트에게 전송. 인증서 내부에는 ‘서버의 public key’ 포함하여 전송한다.
클라이언트는 ‘CA의 public key’로 인증서를 복호화하여 인증서 확인 ⇒ “이 서버는 검증된 서버구나!” - 클라이언트가 대칭 세션키를 만든 후, 받은 ‘서버의 public key’로 암호화하여 서버로 전송.
서버는 자신의 private key로 세션키 복호화. - 이제 브라우저와 서버 둘만 아는 대칭 세션키가 있으니, 모든 데이터를 이거로 암호화/해독. 이 세션키는 이 세션에만 사용되고, 새로운 세션 열리면 다시 새로운 새션 키 생성.
* 대칭키: 암호화와 해독에 같은 키가 쓰임
* 비대칭키: 암호화와 해독에 다른 키가 쓰임. (public key, private key)
https://liveyourit.tistory.com/183
SSL Certificate (SSL 인증서)
해당 웹사이트가 안정한 통신을 하는 사이트인지 인증하기 위한 인증서이다. CA로부터 발급받는다.
서버가 자신의 인증서를 발급받는 과정
- Domain Validation: CA가 Challenge를 내면, 그걸 풀어서 자기가 이 도메인을 컨트롤하는 서버라는 걸 증명. (왜 이걸 prove해야 할까?)
- 자신의 인증서 내용물을 만들고, 상위 CA에 보내서, 인증서 내용물의 해쉬값을 CA의 private key로 암호화해달라고 부탁.
- 암호화된 해쉬값이 포함된 인증서가 준비되면 끝! 그거를 클라이언트들에게 보내면, 클라이언트들은 복호화 과정을 거쳐 해쉬값이 맞나 확인하고 ‘이 서버는 CA가 인증해준 서버구나’ 알고 신뢰.
인증서에 들어있는 내용
- 인증서 소유자 이름
- 인증서 유효기간
- 인증서 소유자의 public key
- 인증서 내용의 해시값을 ‘CA의 private key’로 암호화한 값
Certificate Chain
보통 서버 - CA 이렇게 구성되어있지 않고, CA는 계층 구조 (일반적으로 3계층)를 갖는다.
- Root CA: 누구나 신뢰할 수 있다고 가정하는 최상위 CA. OS나 브라우저들에 보통 얘네의 인증서와 public key가 내장되어 있다.
ex) Geotrust - Intermediate CA (ICA): Chain of trust에서, Root CA와 end-entity 사이에 있는 기관. 신뢰한다는 가정 없다.
ex) Google CA - End-entity / leaf / server : 최하위 개인 서버
Root CA인 Geotrust에게서 ICA인 Google CA가 인증서를 발급받는 과정을 예시로 살펴보자.
Google CA: “Geotrust는 신뢰 가능한 회사죠. Google CA의 인증서 내용물을 해시화한 값을 줄테니, 이걸 Geotrust의 private key로 암호화해주세요”
암호화된 Google CA 인증서를 얻으면, 아무 클라이언트들이 Geotrust의 public key를 가지고 이를 해독함으로써 ‘Google CA는 Geotrust가 인증해준 기관이네. Geotrust는 신뢰가능한 Root CA이니, Google CA도 신뢰 가능하네’라고 판단할 수 있다.
개인 서버 mysite.com에서 Google CA에게 인증서를 발급받는 과정도 비슷하다.
Google CA가 Geotrust에게 했듯이, mysite.com는 Google CA에게 같은 과정 수행하면,
클라이언트: ‘mysite.com는 Google CA가 인증해줬네 → Google CA는 Geotrust가 인증해줬네 → 신뢰’ (chain of trust)
* OS/브라우저에 Root CA들의 인증서 + 공개키들이 내장되어 있음. 그럼 chain 쭉 따라서 Root까지 가면 내장 리스트 보고 마지막으로 복호화한 후 맞으면 신뢰
* mysite.com에서 클라이언트에게 인증서 보낼때, 본인의 leaf 인증서뿐 아니라, chain 따라가는데 필요한 intermeidate certificate들도 같이 보내주는듯? ICA의 인증서에 ICA의 public key 있으니까 그거로 mysite.com의 인증서 확인 → Root CA의 인증서는 미리 갖고 있으니까 Root CA의 public key로 ICA 인증서 확인
* 왜 2단계로 안하고 굳이 ICA를 둬서 3단계 이상을 만들었을까? => 인증서가 손상되거나 private key가 유출될 경우를 대비하여 중간 단계를 둔 것이라고 한다. Root CA는 무조건 안전하다고 가정하고, ICA에 문제 생길 경우 어떻게든 핸들링하는건가?
Cross signing
Root certificate로 가는 path가 여러 개다. 즉, 하위 인증서를 인증해준 상위 CA가 여러 개 있다. 일반적으로 걔네 중 하나라도 인증되면 신뢰.
이걸 하는 이유: 옛날 디바이스들엔 new Root CA가 내장 리스트에 없을 수 있다. 따라서 intermediate certificate에다가 new root certificate, old root certificate 두 가지 모두 싸인해놓는 것.
홈페이지 인증서 오류: DST Root CA X3 만료!
여기까지 SSL에 대한 배경지식 설명이었다.
자 그럼 홈페이지 인증서 문제는 뭐냐!
얼마 전부터 홈페이지 접속 시, 일부 기기들은 잘 접속되는데 (왼쪽 사진), 일부 기기들 (주로 맥) (오른쪽 사진)에서는 안된다.
우리 홈페이지는 Let's Encrypt라는 곳에서 무료 인증서를 받아서 사용하고 있는데, 여기의 Root 인증서 중 하나인 DST Root CA X3가 2021년 9월 30일 부로 만료되었다. 이에 따라 신뢰할 수 없는 사이트가 되어버려 접속이 안되는 것. (https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/ 참조)
브라우저 캐쉬 문제?
브라우저에서 옛날 인증서를 캐쉬해놓고 사용하는 것을 수 있다.
따라서 Hard refresh 및 각종 캐쉬들을 제거해보았다.
하지만 문제는 해결되지 않았다.
오래된 os 문제?
구글링하면 거의 다 이거로 나온다.
https://www.stephenwagner.com/2021/09/30/sophos-dst-root-ca-x3-expiration-problems-fix/
앞서 말했듯이, os에 신뢰 가능한 Root certificate들의 리스트가 이미 내장되어있다. 근데 오래된 os의 경우 최신 certificate이 그 리스트에 포함되어있지 않을 수 있다.
따라서 접속에 문제가 있는 내 맥의 키체인에다가 ISRG 인증서를 추가하고 항상 신뢰하도록 설정했다.
하지만 해결되지 않았다.
Cross sign으로 인한 문제?
근데 왜 일부 디바이스에서는 여전히 접속이 되냐? 처음엔 인증서가 ISRG Root X1과 DST Root CA X3에 의해 cross sign되어 있어 발생한 문제인줄 알았다.
앞서 설명했듯이 원래 cross sign되어 있는 인증서가 있으면, path 중 하나라도 인증되면 신뢰하게 된다. 하지만 일부 브라우저/OS는 모든 path가 다 인증되어야 신뢰한다고 한다. 여기서 DST Root CA X3 쪽 path가 만료되었으니 신뢰가 불가하다고 생각했다.
따라서 인증서를 갱신하여 Root가 오로지 ISRG Root X1이 되도록 바꿨다. 우리는 certbot certonly 명령어를 통해 인증서를 갱신하는데, 이 때 --preferred-chain 'ISRG Root X1' 옵션을 주면 된다.
참고로 현재 인증서의 chain을 확인하는 건 아래와 같은 명령어를 통해 가능하다. 아래 나온 것처럼, Root가 ISRG Root X1임을 확인할 수 있다.
하지만 이렇게 했음에도 불구하고 여전히 문제가 해결되지 않았다.
서버에서 Intermediate certificate을 같이 안 보내줘서 생기는 문제?
위와 같이 openssl 명령어를 통해 certificate chain을 확인해보려고 하면, unable to verify the first certificate이 뜬다. 여기서 first certificate은 중간 인증서인 R3 인증서로 보인다. 즉, 서버에서 원래 chain of trust를 쭉 따라서 거슬러 올라가기 위한 중간 단계 인증서들을 보내줘야 하는데, 이를 안 보내준 것 같다.
왜 이럴까 좀 탐색해봤더니, jekyll serve 옵션으로 -ssl-cert cert.pem 이 들어가있는 것을 확인했다. cert.pem파일은 전체 chain을 포함하고 있지 않는다고 한다. 따라서 https://rein.kr/blog/archives/4525 에 나온 것처럼, -ssl-cert fullchain.pem 으로 옵션을 변경하였다.
하지만 이렇게 해서도 해결이 안됐다....
webrick 관련 옵션이 뭔가 있나?도 살펴봤는데 워낙 자료도 없고 결국엔 못찾았다... 지킬의 _config.yml 파일에 webrick headers 옵션 주는 곳이 있는데, 여기에 뭘 쓰는건지에 대한 documentation도 없다... nginx config랑 비슷하게 몇 개 설정해봤는데 작동을 안하더라...
결론
아무래도 웹서버 webrick 자체의 문제인 것으로 보이고 당장 근본적으로 해결하기는 힘들 것 같아서...
결국은 웹서버를 일단 nginx로 갈았더니 해결됐다! 역시 webrick의 문제였다.
일부 디바이스에서는 됐던 이유는 아마 그 디바이스들에 intermediate certificate이 내장되어있어서 이걸 보내주지 않았음에도 알아서 인증을 진행했던듯 싶다.
어쨌든 ssl 인증 과정에 대해 공부하고, webrick 코드들 뜯어보는건 나름 재밌었다...!
Reference
https://support.stackpath.com/hc/en-us/articles/360023757932-Introduction-to-SSL-Certificates
https://m.blog.naver.com/alice_k106/221468341565
https://letsencrypt.org/ko/how-it-works/
https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/
https://liveyourit.tistory.com/183
https://rein.kr/blog/archives/4525
https://www.stephenwagner.com/2021/09/30/sophos-dst-root-ca-x3-expiration-problems-fix/
'IT' 카테고리의 다른 글
[비디오 코덱] Error resilience (0) 2022.07.31 General optimization의 끝? (0) 2022.01.30 HCI design study에 대한 단상 (0) 2021.10.10 시스템 연구에 대한 단상 (0) 2021.09.16 [알고리즘] BFS 최단경로 (0) 2021.08.29