[Nginx] HTTPS (SSL)

minholee_93·2020년 2월 25일
0

Nginx

목록 보기
14/20
post-thumbnail

참고 자료 : https://www.udemy.com/course/nginx-fundamentals/ , https://www.tuwlab.com/ece/26978


이번글에서는 nginx의 HTTPS를 사용해 보안성을 향상시키는 방법에 대해 알아보도록 하겠습니다.

1. HTTPS란?

HTTP의 보안성을 강화한 차세대 프로토콜입니다.

HTTPS는 소켓 통신에서 일반 텍스트를 이용하는 대신, SSL이나 TLS 프로토콜을 통해 세션 데이터를 암호화합니다. 따라서 데이터의 적절한 보호를 보장합니다.

HTTPS의 기본 TCP/IP 포트는 443입니다. 😎

2. HTTPS 적용하기

이전 글(https://velog.io/@minholee_93/Nginx-HTTP2)에서 우리는 Self Signed SSL을 사용해 HTTPS 통신을 하는 방법에대해 간단히 알아보았습니다.

이번 글에서는 HTTPS에 대해 좀더 자세히 알아보고, Nginx의 HTTPS를 optimize 하는 방법에대해 알아보도록 하겠습니다.

이번글에서 사용할 nginx.conf는 아래와 같습니다.

nginx.conf

worker_processes auto;

events {
    worker_connections  1024;
}


http {
        include mime.types;

        server {
		# listen 80;
                listen 443 ssl http2;
                server_name [SERVER_IP];
                
                root /sites/demo;
                index index.html;

                ssl_certificate /etc/nginx/ssl/self.crt;
                ssl_certificate_key /etc/nginx/ssl/self.key;


                location / {
                        try_files $uri $uri/ =404;
                }

                location ~\.php$ {
                        # Pass php requests to the php-fpm service (fastcgi)
                        include fastcgi.conf;
                        fastcgi_pass unix:/run/php/php7.10fpm.sock;

                }
        }
}

2-1) redirect http

현재의 nginx.conf는 server가 https만 listen하고 있으므로 http로 접근시 아래와 같이 접근이 거절됩니다.

이를 해결하기 위해 http와 https를 모두 사용할 수도 있습니다.

하지만 현대에는 https가 어느새 표준으로 자리잡았기 때문에, 더이상 http를 사용할 이유가 없어졌습니다.

또한 HTTP/2를 사용하기 위해선 HTTPS를 사용해야하므로, 가장 optimize한 방법은 아래와 같이 HTTP로 접근한 모든 request를 HTTPS로 redirect 하는 것 입니다.

http로 접근하는 모든 request를 https로 redirect 해보겠습니다.

nginx.conf를 아래와 같이 수정합니다.

nginx.conf

worker_processes auto;

events {
    worker_connections  1024;
}


http {
        include mime.types;

	
        # Redirect all traffic to HTTPS
        server {
                listen 80;
                server_name [SERVER_IP];
                return 301 https://$host$request_uri;
        }


        server {

                listen 443 ssl http2;
                server_name [SERVER_IP];
                
                root /sites/demo;
                index index.html;

                ssl_certificate /etc/nginx/ssl/self.crt;
                ssl_certificate_key /etc/nginx/ssl/self.key;


                location / {
                        try_files $uri $uri/ =404;
                }

                location ~\.php$ {
                        # Pass php requests to the php-fpm service (fastcgi)
                        include fastcgi.conf;
                        fastcgi_pass unix:/run/php/php7.10fpm.sock;

                }
        }
}

위의 코드를 살펴보면 80 port를 listen하는 server 블럭을 생성해서 80 port로 호출된 모든 request_uri를 301(permanently moved) 응답코드와 함께 https로 redirect합니다.

reload 후 http로 접근해보면 아래와 같이 https로 redirect 되는 것을 확인할 수 있습니다.

curl 명령어로 확인시 아래와 같이 redirect된 location을 확인할 수 있습니다.

2-2) improve SSL encryption

이번에는 SSL 통신의 encryption을 개선해보겠습니다.

앞서 작성한 nginx.conf에는 아래와 같은 구문이 작성되어 있습니다.

listen 443 ssl http2;

하지만 SSL은 현재 outdated 되었으며, 보안 기능을 강화한 TLS를 사용하는 것을 권장합니다.

따라서 nginx.conf을 아래와 같이 변경합니다.

nginx.conf

worker_processes auto;

events {
    worker_connections  1024;
}


http {
        include mime.types;

	
        # Redirect all traffic to HTTPS
        server {
                listen 80;
                server_name [SERVER_IP];
                return 301 https://$host$request_uri;
        }


        server {

                listen 443 ssl http2;
                server_name [SERVER_IP];
                
                root /sites/demo;
                index index.html;
           
              	ssl_certificate /etc/nginx/ssl/self.crt;
                ssl_certificate_key /etc/nginx/ssl/self.key;
						
        	# Disable SSL
                ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        
        	# Optimise cipher suits
                ssl_prefer_server_ciphers on;
                ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;

		# Enable DH Params
                ssl_dhparam /etc/nginx/ssl/dhparam.pem;

		# Enable HSTS
                add_header Strict-Transport-Security "max-age=31536000" always;

                # SSL sessions
                ssl_session_cache shared:SSL:40m;
                ssl_session_timeout 4h;
                ssl_session_tickets on;

                location / {
                        try_files $uri $uri/ =404;
                }

                location ~\.php$ {
                        # Pass php requests to the php-fpm service (fastcgi)
                        include fastcgi.conf;
                        fastcgi_pass unix:/run/php/php7.10fpm.sock;

                }
        }
}

추가된 코드를 살펴보면 다음과 같습니다.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2 : https에 사용할 protocol을 TLS로 변경합니다.

ssl_prefer_server_ciphers on : client와의 cipher suite 협상 과정에서 서버측에서 지정해 놓은 순서를 우선시하도록 설정합니다.

예를 들어, on으로 설정된 경우 Client에서 낮은 우선순위에 지정했던 Cipher Suite가 서버측에서는 높은 우선순위로 지정되어 있으면 서버측의 설정에 우선하여 해당 Cipher Suite를 채택하게 됩니다.

ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5 : HTTPS 통신 과정에서 사용할 암호화 알고리즘을 지정합니다. !가 붙은 알고리즘은 사용하지 않도록 지정합니다. 암호화 알고리즘의 조합은 각 서비스의 환경에 맞게 조합해 사용하면 됩니다.

ssl_dhparam /etc/nginx/ssl/dhparam.pem : dhparam에 사용할 pem key를 지정합니다. dhparam을 사용하면 client와 server간의 key를 exchange할 때 perfect security를 보장합니다.

dhparam.pem을 생성하기 위해 아래와 같이 명령어를 입력합니다. 이때 주의점으로는 dhparam의 사이즈는 생성한 ssl private key와 반드시 size가 같아야 합니다. 저는 2048을 사용하도록 하겠습니다.

openssl dhparam 2048 -out /etc/nginx/ssl/dhparam.pem

add_header Strict-Transport-Security "max-age=31536000" always : client의 browser에게 http로 어떠한 것도 load 하지 말라고 규제합니다. 이를 통해 http에서 https로 redirect 되는 request를 minimize 할 수 있습니다.

ssl_session_cache shared:SSL:40m : HTTPS 통신을 하기위해선 client와 server간에 서로를 확인하는 handshake 과정이 필요합니다. 따라서 이를 session cache로 저장해 놓으면 매번 통신마다 handshake하는 과정을 줄일 수 있습니다.

이때 shared를 명시하면 모든 nginx worker들 끼리 session cache를 공유합니다.

ssl_session_timeout 4h : session을 유지할 time을 정의합니다.

ssl_session_tickets on : session에 대한 정보를 ticket으로 발행해 client에게 전달합니다. 이를 통해 server는 더이상 session을 유지하지 않아도 되며, 단지 ticket이 유효한지만 검증하면 됩니다.

모든 설정이 완료되었습니다. 👏👏👏

reload 후 확인해보면 아래와 같이 정상작동하는 것을 확인할 수 있습니다.

profile
Hello World 😎

0개의 댓글