NGINX에서 TLSv1, TLSv1.1을 비활성화해보자.

2022. 12. 20. 20:26Programming/Server

반응형
참고:

HTTP_SSL_MODULE 문서 페이지
Nginx - Disable SSL, TLS 1.0, and TLS 1.1


Nginx에서 TLSv1과 TLSv1.1을 비활성화하는 방법

오늘은 NGINX에서 TLSv1, TLSv1.1을 비활성화하는 방법에 대해 살펴보겠다. 는 NGINX에서는 간단하게 아래와 같이 ssl_protocols값만 설정해주면 끝난다. 참고로 TLSv1.3은 OpenSSL 1.1.1 이상의 버전이 설치되어있을때만 사용 가능하며, 그 이하의 버전이 설치되어있는 경우에는 에러가 출력된다. 이럴 때는 OpenSSL을 업데이트해주거나 혹은 과감하게 TLSv1.3을 빼주도록하자.

보안보다는 퇴근이 우선이다

ssl_protocols TLSv1.2 TLSv1.3

몹시 간단하게 끝났는데, 이렇게 적어주면 TLSv1과 TLSv1.2가 비활성화되는 이유는 HTTP_SSL_MODULE 문서 페이지에서 찾아볼 수 있다.

Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];
Default: ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Context: http, server

ssl_protocols 기본값에 TLSv1과 TLSv1.1이 포함되어 있기 때문이다. 아, 물론 SSL 설정에 대한 내용은 이 글에서 다루지 않는다. 이미 다른 글들이 충분히 자세하게 다루고 있을뿐더러, 이 글은 좀 더 뻘하게 삽질한 내용을 기록하기 위한 글이기 때문이다. 일단 이렇게 설정이 끝났다면, 아래와 같이 openssl을 사용해서 접속 테스트를 해볼 수 있다.

openssl s_client -tls1 -connect localhost:443 2> /dev/null | grep -i -E "cipher|protocol"
openssl s_client -tls1_1 -connect localhost:443 2> /dev/null | grep -i -E "cipher|protocol"
openssl s_client -tls1_2 -connect localhost:443 2> /dev/null | grep -i -E "cipher|protocol"

각각 위에서부터 순서대로 TLSv1, TLSv1.1, TLSv1.2로 접속을 시도하고, 결과물 중에서 cipherprotocol만 찾아서 출력하도록 하고 있다. 만약 설정이 잘 되었다면, TLSv1과 TLSv1.1로 시도했을 때는 아래와 같이 실행 결과물에서 cipher를 제대로 찾지 못하는 걸 볼 수 있다.

New, (NONE), Cipher is (NONE)
    Protocol  : TLSv1
    Cipher    : 0000

반대로, TLSv1.2로 시도했을때는 실행 결과물에서 Cipher를 제대로 찾는 것을 볼 수 있다.

New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384

좋아, 이것으로 오늘은 NGINX에서 TLSv1, TLSv1.1을 비활성화 하는 방법에 대해 살펴봤다.

하지만, 숨쉬는 것도 귀찮은 내가 삽질도 안했는데 블로그에 글을 쓰고 있을리가 없지 않은가.

서버에 설치된 OpenSSL 버전이나 Nginx버전에 따라 다를텐데, 다음과 같이 무슨 짓을 해도 TLSv1.1을 통과하는 모습을 보이는게 아닌가. 이쯤되면 기가막히고 코가 막힐 노릇이다. OpenSSL만 문제면 다행이다만, TLSv1과 TLSv1.1만 막아놓고 룰루랄라 퇴근하려던 나의 기쁜 마음을 가볍게 즈려밟듯 IE 보안설정에서 TLSv1.1만 활성화시켜놓은 채 접속해봐도 아무런 에러 페이지 없이 접속되는 모습을 보여주고 있었다.

New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
    Protocol  : TLSv1.1
    Cipher    : ECDHE-RSA-AES256-SHA

NGINX와 TLSv1, TLSv1.1이라는 세 가지의 키워드를 아무리 조합해서 검색을 하더라도 비슷비슷한 내용만 나오던 찰나, Nginx - Disable SSL, TLS 1.0, and TLS 1.1 글을 보고 다음과 같이 ssl_ciphers 옵션을 사용해서 허용할 cipher list를 작성해줬다.

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA";

이렇게 하면 서버에서 cipher list에 포함되어있는 내용들만 허용하게 되므로, TLSv1.1 이하의 프로토콜로 요청이 들어왔을 때는 Cipher를 정상적으로 출력하지 않을 것이다. 자, 그럼 얼른 실행해보자.

openssl s_client -tls1_1 -connect localhost:443 2> /dev/null | grep -i -E "cipher|protocol"
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
    Protocol  : TLSv1.1
    Cipher    : ECDHE-RSA-AES256-SHA

어째서 nginx에서는 이토록 상큼하게 설정해놓은 Cipher list를 무시하고 TLSv1.1을 허용해주는걸까. 이미 퇴근시간인 6시를 가리키고 있는 시계를 바라보며 머리를 쥐어뜯던 나는, 이건 분명 지금 사용하는 nginx의 버그로 인해, ssl_chipers 옵션이 정상 동작하지 않는 것이다라는 자기 합리화의 길로 들어서기 시작했다. 이것을 입증하는 방법은 상당히 간단했다. TLSv1.2를 테스트한 결과물에서 TLSv1.2의 Cipher가 뭔지 알고 있으므로, 이 녀석만 ssl_chipers에 넣어서 돌려보면 되는 것이다. 만약 ssl_chipers가 정상적으로 동작하고 있다면, 이번에도 TLSv1.1로 테스트했을 때 실행 결과에서 정상적으로 Cipher값을 찾을 수 있을 것이다.

openssl s_client -tls1_2 -connect localhost:443 2> /dev/null | grep -i -E "cipher|protocol"

New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384

다시 한 번 TLSv1.2 테스트 결과를 살펴보자. 여기서 ECDHE-RSA-AES256-GCM-SHA384가 바로 TLSv1.2의 Cipher임을 알 수 있다.

ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384";

자, 그럼 이제 ECDHE-RSA-AES256-GCM-SHA384 값을 ssl_ciphers에 넣고 실행 결과를 살펴보도록 하자. 예상과 마찬가지로 특정 버전의 nginx가 가지고 있는 버그라면, TLSv1.1로 요청해도 정상동작 할 것이다.

openssl s_client -tls1_1 -connect localhost:443 2> /dev/null | grep -i -E "cipher|protocol"
New, (NONE), Cipher is (NONE)
    Protocol  : TLSv1.1
    Cipher    : 0000

...? 어째서인지 예상과는 다르게 정상적으로 TLSv1.1로 요청한 내용은 차단되는 것을 확인할 수 있다. 즉, ssl_ciphers는 버그 없이 정상적으로 동작하고 있다는 것을 알 수 있다. 그렇다면 대체 뭐가 문제일까.

이미 배부르게 저녁을 먹고 집에가고싶다는 생각이 스멀스멀 커다란 머리통의 대부분을 차지하려는 순간, TLSv1.1의 cipher값이 눈에 들어왔다. 당장 설정해놓은 cipher list에서 ECDHE-RSA-AES256-SHA으로 검색해도 아무것도 나오지 않지만, 앞부분만 잘라내서 검색해보면 겹치는 녀석이 존재하는 게 아닌가.

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA";

그렇다, 문제는 ssl_ciphers값에 설정해준 cipher 리스트 중, TLSv1.1에서 사용하는 ECDHE-RSA-AES256-SHA보다 짧은 ECDHE-RSA-AES128-SHA이 포함되어있기 때문에, nginx가 그다지도 상큼하게 TLSv1.1에 대한 요청을 허용해주고 있던 것이었다.

아래와 같이 ssl_ciphers에 설정된 값들 중, ECDHE-RSA-AES128-SHA을 제거하고 테스트 한 뒤에야 원하는대로 TLSv1.1이 비활성화 된 것을 확인할 수 있었다. 이제 집에 가야지. 휴.

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
반응형