여태까지 진행한 프로젝트들에서는 HTTP로만 통신을 진행했었는데 HTTP는 따로 설정을 해주지 않아도 사용할 수 있어 편하게 사용할 수 있지만 데이터를 평문으로 주고받기 때문에 애플리케이션이 보안에 취약한 상태가 됩니다.
HTTPS를 사용하지 않고 HTTP를 사용할 경우 다음과 같은 보안 문제가 발생할 수 있습니다.
- Sniffing(스니핑)
TCP/IP를 통해 통신하는 방식은 여러 경로(스위치, 라우터)를 거쳐서 전송되기 때문에 평문으로 전송하는 패킷은 중간에 도청이 가능해집니다.
- 해커가 위장하는 경우 확인할 방법이 없음
HTTP의 경우 요청을 보내는 상대가 누구인지 확인하는 처리를 하지 않습니다. 이는 누구든지 요청이 가능하다는 것을 의미합니다. HTTP만으로는 의미없는 요청을 수신하지 않는다거나 특정 호스트를 거부한다던지 할 수 없게 됩니다.
- 중간에 데이터가 변조되더라도 확인할 방법이 없음
중간에서 요청이나 응답을 빼앗아 변조하는 중간자 공격(Man-in-the-Middle)의 매우 취약합니다.
하지만 HTTPS는 암호화되어 있기 때문에 평문과 달리 위변조하기 어렵습니다.
HTTPS를 사용하면 SEO의 이점을 얻는 등(구글에서 HTTPS를 사용하는 사이트에 가산점을 주기 때문에 HTTP를 사용하는 사이트보다 상위 노출 가능성 증가) 추가적인 장점도 존재합니다.
도메인 발급 받기
도메인 발급하기이제 한번 HTTPS를 직접 적용해봅시다. HTTPS를 적용하기 위해 let's encrypt라는 인증기관에서 인증서를 발급 받을 예정인데 이를 위해서는 먼저 도메인 주소가 필요합니다.
저는 도메인 주소를 발급받기 위해 무료로 도메인을 발급받을 수 있는 사이트인 freenom을 사용했습니다.
https://www.freenom.com/en/index.html?lang=en
해당 사이트는 특정 도메인을 1년간 무료 사용할 수 있는 장점이 있습니다.
먼저 도메인을 발급받기 위해서 다음 검색창에 원하는 도메인 이름을 입력합니다.
입력하면 다음과 같이 무료로 사용할 수 있는 도메인을 선택할 수 있습니다.
원하는 도메인에 Get it Now!를 클릭하면 됩니다.
다음과 같이 전부 Not available이 뜬다면 XXXX.tk 이런식으로 구체화시켜서 검색한 후 Get it Now!를 선택하면 해결됩니다.
오른쪽의 checkout을 선택하면 다음과 같이 결제 화면으로 넘어가고 Period에서 원하는 기간을 선택해서 continue를 누르면 됩니다.(12개월 까지 무료)
로그인을 하지 않았다면 로그인하라고 나오고 아니라면 바로 사용할 수 있습니다. 로그인을 완료하면 다음 Complete Order를 누르면 사용할 수 있습니다.
이제 구매한 도메인에 설정을 해봅시다 Service -> My Domains로 이동합시다.
그 다음 원하는 도메인에 Manage Domain을 선택합니다.
이제 DNS 서버에 등록하는 과정을 진행해봅시다. 해당 도메인과 매핑할 서버의 IP주소를 등록해줍시다.
등록을 위해 Manage Freenom DNS로 이동합니다.
여기서 Add Record에 매핑할 도메인 주소와 해당 서버의 IP주소를 적습니다.
- Name : 매핑할 도메인 주소를 입력합니다. 여기서 입력되는 문자열은 하위 도메인으로 추가됩니다.(xxx.naver.com 같은 형식)
- Type : 도메인 레코드 타입을 지정합니다. 현재 A의 의미는 도메인 이름을 해당하는 IP 주소로 변환한다는 의미입니다.
- TTL : 네트워크에서 데이터의 유효기간을 나타냅니다. 해당 시간이 지나면 패킷이 버려집니다.
- Target : 레코드 타입으로 지정한 값이 들어갑니다. A를 사용하면 여기에 서버의 IP주소가 들어가게 됩니다.
Nginx 서버 만들기
제공하는 서비스를 HTTPS로 제공하기 위해 서버와 사용자(클라이언트) 사이에 Nginx를 추가하여 SSL 서버로 사용할 수 있도록 하겠습니다.
먼저 저는 도커 컨테이너로 Nginx를 띄울 것이기 때문에 nginx를 띄울 곳에 미리 도커가 설치되어 있다고 가정합니다.
전체적인 구성도는 다음과 같습니다.
먼저 이들을 묶을 Docker-compose 파일을 작성해봅시다.
version: "3"
services:
web-server:
image: nginx
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
app:
image: ${YOUR_APPLICATION_IMAGE}
container_name: ${YOUR_APPLICATION_CONTAINER_NAME}
restart: always
expose:
- "8080"
volumes:
- ./:/home/ubuntu
- /etc/localtime:/etc/localtime
- version : docker-compose.yml 파일의 버전을 명시합니다.
- services : 작성할 컨테이너들에 대한 정보를 적습니다. 다음으로 작성할 컨테이너들이 오게 됩니다.(위에서는 web-server, app)
- image : 해당 컨테이너를 띄울 이미지를 선택합니다.
- container_name : 해당 컨테이너의 이름을 지정합니다. (해당 이름으로 접근 가능하게 됨)
- restart : 재시작 정책을 지정합니다. always의 경우 항상 컨테이너를 재시작시키게 됩니다.
- ports : 외부에 접근가능한 포트를 적습니다. {호스트 포트번호}:{컨테이너 포트번호} 같은 형태로 사용됩니다. 위와 같이 리스트로 노출 포트를 지정할 수 있습니다.
- expose : 컨테이너 내부에서만 접근가능한 포트를 적습니다. expose로 열 경우 해당 컨테이너는 외부에서는 연결할 수 없고 컨테이너 네트워크 내부에서만 접근가능합니다.
- volumes : 컨테이너와 호스트와 마운트할 경로를 적습니다. {호스트 경로}:{컨테이너 경로}로 작성되며 해당 경로에 마운트됩니다.
다음 명령으로 컨테이너를 띄워봅시다.
docker-compose up -d
docker-compose up을 사용하면 정의한 컨테이너를 실행시킵니다.
여기어 -d 옵션을 사용하면 백그라운드로 실행되게 됩니다. (기본 포그라운드)
이제 해당 IP로 접속해보면(로컬의 경우 localhost) 다음과 같은 화면이 뜨는 것을 확인할 수 있습니다.
Nginx에 HTTPS 적용하기
이제 Nginx를 띄웠으니 nginx가 SSL 서버 역할을 할 수 있도록 인증서를 발급받아봅시다.
HTTPS는 let's encrypt라는 인증기관의 도움을 받아 적용하겠습니다.
let's encrypt는 무료로 인증서를 발급해주는 인증기관으로 이를 인증받기 위해 certbot을 사용하여 간단하게 인증 받는 방법을 사용하겠습니다.
먼저 nginx 컨테이너를 다음 명령어로 접속합니다.
docker exec -it nginx /bin/bash
위의 명령어를 사용하면 bash로 nginx라는 이름의 컨테이너에 접속하여 터미널로 사용할 수 있습니다.
먼저 패키지 저장소를 update 하기 위해 다음 명령어를 입력합니다.
apt update
인증서 발급을 위해 certbot과 문서 편집을 위해 vim을 설치합니다.
apt install certbot vim
인증서를 발급받기 위해 미리 nginx 설정을 변경해 주어야 합니다.
다음 명령어를 입력하여 default.conf 파일을 수정합시다
vim /etc/nginx/conf.d/default.conf
# default.conf
server {
listen 80;
listen [::]:80;
server_name 내도메인 주소;
}
- listen : 열 포트를 설정합니다. 현재는 80포트를 열고 있습니다. (IPv4)
- listen [::] : 열 포트를 설정합니다. 현재는 80포트를 열고 있습니다 (IPv6)
- server_name : 서버의 이름으로 도메인 주소를 입력하면 됩니다.
변경사항을 적용하기 위해 nginx를 다음 명령어로 재시작 합니다.
service nginx reload
이제 certbot을 사용하여 인증서를 발급받아봅시다. 다음 명령어를 입력합니다.
certbot --nginx -d {도메인 주소} --email {내 이메일 주소}
이후 certbot에서 해당 도메인주소로 실제 연결이 되는지 확인하고 연결된 것이 확인되면 인증서가 발급되고 암호키를 전달받게 됩니다.
이제 마지막으로 /etc/nginx/conf.d/default.conf에 설정을 추가해줍시다
server {
listen 80;
listen [::]:80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/letsencrypt/live/{도메인주소}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{도메인주소}/privkey.pem;
server_name {내도메인 주소};
location / {
proxy_pass http://app:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
- return 301 https://$host$request_uri; : http로 들어오는 요청을 https로 변환해주기 위한 구문
- ssl_certificate : 서버 인증서의 경로를 지정합니다.
- ssl_certificate_key : 서버 개인키의 경로를 지정합니다.
- proxy_pass http://app:8080; : 요청을 전달할 서버의 주소
- proxy_set_header Host : 헤더에 들어가는 서버의 도메인 네임
- proxy_set_header X-Real-IP : 클라이언트 IP
- proxy_set_header X-Forwarded-For : 현재까지 거쳐온 서버의 IP에 대한 정보
이제 다시 nginx를 재시작하고 서버에 접속해보면 다음과 같이 https로 요청이 잘 전달되고 http로 보낼 경우에도 https로 변환되어 오는 것을 확인할 수 있습니다.
참고자료
https://rachel-kwak.github.io/2021/03/08/HTTPS.html
https://win100.tistory.com/360