CERTBOT으로 인증서 발급하기(HTTPS 적용)

이진송·2025년 6월 18일

nginx를 활용해 http://mydomain.com 사이트를 올리는데 성공했다.
이번엔 Certbot을 활용해 인증서를 발급받고, https프로토콜을 적용함으로 보안적인 요소를 확보해보겠다.

먼저 HTTP에 대해서 알아보자

1. HTTP

HTTP는 클라이언트와 서버가 서로 통신하는 방법을 표준화하는 TCP/IP 기반 애플리케이션 계층 통신 프로토콜이다.

HTTP는 인터넷에서 데이터를 주고받기 위해 사용되는 프로토콜이다. 클라이언트와 서버 간의 통신을 담당하며, 웹 브라우저와 웹 서버 간의 데이터 전송을 위해 주로 사용된다.

HTTP는 요청(Request)과 응답(Response)의 형태로 이루어져 있다. 클라이언트는 HTTP 요청 메시지를 서버에 전송하고, 서버는 이 요청에 대한 응답 메시지를 클라이언트에게 전송한다. 요청과 응답은 각각 헤더와 바디로 구성된다. 헤더는 요청이나 응답에 대한 메타데이터를 포함하고, 바디는 요청이나 응답에 대한 실제 데이터를 포함한다.

2. HTTPS

HTTPS(Hypertext Transfer Protocol Secure)는 HTTP의 확장 버전 또는 더 안전한 버전입니다. HTTPS에서는 브라우저와 서버가 데이터를 전송하기 전에 안전하고 암호화된 연결을 설정합니다.

HTTP보다 HTTPS를 선택하는 이유는 무엇인가요?

HTTP로 웹사이트를 만들면 귀찮게 SSL인증서를 발급받고 적용할 필요가 없을텐데, 왜 굳이 사람들은 HTTPS를 사용할까?

1. 보안

HTTP 메시지는 일반 텍스트로, 권한이 없는 당사자가 인터넷을 통해 쉽게 엑세스하고 읽을 수 있다. 반면 HTTPS는 모든 데이터를 암호화된 형태로 전송한다.

2. 권위

검색 엔진은 HTTP의 신뢰성이 더 낮기 때문에 보통 HTTP 웹 사이트 콘텐츠의 순위를 HTTPS 웹 페이지보다 낮게 지정합니다. 고객도 HTTP보다 HTTPS 웹 사이트를 더 선호합니다. 브라우저는 브라우저 주소 표시줄에서 웹 사이트 URL 옆에 있는 자물쇠 아이콘을 배치하여 사용자에게 HTTPS 연결을 표시합니다. 사용자는 이러한 추가 보안 및 신뢰 요소 때문에 HTTPS 웹 사이트 및 애플리케이션을 선호합니다.

3. 성능 및 분석

HTTPS 웹 애플리케이션은 HTTP 애플리케이션보다 로드 속도가 더 빠릅니다. 마찬가지로, HTTPS는 참조 링크도 더 잘 추적합니다. 추천 트래픽은 광고 또는 소셜 미디어 백링크와 같은 서드 파티 소스에서 생성되는 웹 사이트 트래픽입니다. 분석 소프트웨어가 신뢰할 수 있는 트래픽 소스를 정확하게 식별하도록 하려면 HTTPS를 활성화해야 합니다.

출처

SSL (Secure Sockets Layer)

아래의 그림에서 처럼 HTTP 방식은 어플리케이션 계층에서 직접 TCP 레이어와 통신하지만, SSL을 사용하면 HTTP와 SSL이 통신하고, SSL과 TCP 가 통신하게 됩니다. 이렇게 통신하는 방식을 HTTPS 라고 부르는 것 입니다.

1) 대칭키 암호화 방식

대칭키 암호화 방식이란, 하나의 암호화키(key)를 평문으로 암호화하고, 다시 암호문을 원해의 평문으로 복호화할 때 사용하는 방식

2) 공개키 암호화 방식

공개키 암호화 방식은 공개키, 개인키 이렇게 두 개의 키를 한 쌍(키페어: key pair)로 각각 암호화/복호화에 사용

공개키 방식은 대칭키 방식에 비해 안전하지만, 계산 과정이 복잡하고 연산 도중 컴퓨터 자원을 많이 사용, 실제 IT에서는 공개키, 대칭키 방식을 혼합하여 사용함

SSL 핸드셰이크 과정

1. 클라이언트에 해당하는 브라우저가 웹 서버에 접속(Client Hello)

다음 정보를 Client Hello 단계에서 보냄

  • 브라우저가 사용하는 SSL 혹은 TLS 버전 정보
  • 브라우저가 지원하는 암호화 방식 모음(Cipher suite)
  • 브라우저가 순간적으로 생성한 임의의 난수
  • 만약 이전에 SSL 핸드셰이크가 완료된 상태라면, 그때 생성된 세션 아이디(Session ID)
  • 기타 정보

(Cipher suite)보안의 목표

  • 안전한 키 교환
  • 전달 대상 인증
  • 암호화 알고리즘
  • 메시지 무결성 확인 알고리즘

2. 웹 서버는 1.에 응답하면서 아래 정보를 클라이언트에 제공(Server Hello)

  • 브라우저의 암호화 방식 정보 중에서 서버가 지원하고 선택한 암호화 방식(Cipher suite)
  • 서버의 공개키가 담긴 SSL 인증서. 인증서는 CA의 비밀키로 암호화되어 발급된 상태
  • 서버가 순간적으로 생성한 임의의 난수
  • 클라이언트 인증서 요청

3. 브라우저는 서버의 SSL 인증서 확인

대부분 브라우저에서는 공신력 있는 CA들의 정보와 CA가 만든 공개키가 이미 설치되어 있음. 서버가 보낸 SSL 인증서가 정말 CA가 만든것인지를 확인하기 위해, 내장된 CA공개키로 암호화된 인증서를 복호화
정상적으로 복호화 되었다면 CA가 발급한 것이 증명이 되고, 등록된 CA가 만든 인증서처럼 꾸몄다면 브라우저에 경고를 보냄

4. 브라우저는 자신이 생성한 난수와 서버의 난수를 사용하여 premaster secret을 만듬

웹 서버 인증서에 딸려 온 웹사이트의 공개키로 이것을 암호화 하여 서버로 전송

5. 서버는 사이트의 비밀키로, 브라우저가 보낸 premaster secret 값을 복호화

복호화 한 값을 master secret 값으로 저장, 이것을 사용하여 방금 브라우저와 만들어진 연결에 고유한 값을 부여하기 위한 세션 키 생성. 세션키는 대칭키 암호화에 사용할 키로 브라우저와 서버 사이에 주고받는 데이터를 암호화하고 복호화함

6. SSL 핸드셰이크를 종료하고 HTTPS 통신 시작

브라우저와 서버는 SSL 핸드셰이크를 종료하고 웹상에서 데이터를 세션키를 사용해 암호화/복호화하며, HTTPS 프로토콜을 통해 주고받을 수 있다.

단계암호화 방식목적
인증서 검증비대칭키서버 신뢰 검증
세션키 전달비대칭키안전한 세션키 교환
데이터 전송대칭키빠르고 효율적인 암호화

출처


Certbot 적용

acme-challenge 인증

HTTP를 적용하기 위해서 인증 기관으로부터 인증을 받아야한다.
나는 Webroot 방식을 활용했다.
실제 돌아가고 있는 웹 서버에 특정 파일 쓰기 작업을 통해 인증하는 방식으로, 접근할 수 있는 특정 디렉터리를 제공해서 접근이 가능한지 확인하는 방식이다.

server {
    listen 80;
    server_name mywebdomain;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

일단 인증을 받고, 아래 코드를 통해 서버가 시작될 때 인증서 유효기간 확인 및 갱신을 하고, 추후 프로젝트가 완성되면 유효기간(90일)에 맞춰 자동 갱신을 적용할 예정이다.

            sh "ssh -o StrictHostKeyChecking=no ubuntu@$AWS_IP 'cd ~/Myprojectname/cert/certbot && sudo docker-compose -f cert-compose.yml up -d'"
            sh "ssh -o StrictHostKeyChecking=no ubuntu@$AWS_IP 'cd ~/Myprojectname/cert/certbot && sudo docker-compose -f cert-compose.yml down'"

nginx/default.conf

nginx/default.conf 파일에 새로운 내용을 추가하여 https를 받을 수 있도록 했다.
주석을 남겨두었으니, 참고바람

# HTTP 요청 처리 (80포트)
server {
    listen 80;
    listen [::]:80;
    
    # 이 서버 블록이 처리할 도메인
    server_name mydomain;

	# 모든 HTTP 요청을 HTTPS로 리다이렉트
    return 301 https://mydomain$request_uri;
}

# HTTP 요청 처리 (443포트)
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name www.mydomain;
    
	# 인증서 경로 (Let's Encrypt로 발급받은 것)
    ssl_certificate     /etc/letsencrypt/live/mydomain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mydomain/privkey.pem;

	# www 붙은 도메인 요청도 non-www 도메인으로 리다이렉트
    return 301 https://mydomain$request_uri;
}

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    server_name mnmwa.kr;


    ssl_certificate     /etc/letsencrypt/live/mydomain/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mydomain/privkey.pem;
    # include             /etc/letsencrypt/option-ssl-nginx.conf;
    # ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;

    # React 정적 파일 서빙
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri /index.html;
    }

    # API 프록시 설정
    location /api/v1/ {
        # docker network 내부에서 통신
        proxy_pass http://mybackend:8080;
        # proxy 요청에 원래 호스트 헤더를 설정
        proxy_set_header Host $host;
        # 클라이언트의 실제 IP를 X-Real-IP 헤더에 설정. 백엔드 서버가 실제 클라이언트의 IP를 알기 위함. -> 로깅, IP 제한, 지리적 위치 서비스 등에 사용.
        proxy_set_header X-Real-IP $remote_addr;
        # X-Forwarded-For 클라이언트의 IP와 경유한 프록시 서버의 IP 목록을 전달. 백엔드 서버가 전체 경로를 추적할 수 있게 함.
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 프로토콜을 X-Forwarded-Proto 헤더에 설정. 이유는 프로토콜이 http인지 https인지 알기 위함 -> 보안 분석, 트래픽 분석 등에 사용.
        proxy_set_header X-Forwarded-Proto $scheme;
    }

}

위 같은 설정으로
http://www.mydomain -> https://mydomain
http://mydomain -> https://mydomain
https://www.mydomain -> https://mydomain
으로 리다이렉트 되어서 https 설정을 마칠 수 있었다.

profile
개발자 뉴비, 새로운 것을 배워가고 특정한 주제에 대해 고민하는 것을 즐거워합니다.

0개의 댓글