요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

웹소켓(Web Socket) 프로토콜은 HTTP와는 다른 통신 프로토콜로 웹 서버와 웹 브라우저가 서로 실시간 메시지를 교환하는 데에 사용된다. 웹소켓 연결을 맺기 위한 첫 번째 핸드셰이크를 주고받은 이후 지속적으로 연결이 유지되는 것이 특징이며, 매번 메시지 전송 시에 새롭게 연결을 맺을 필요가 없어 빠르고 효율적이다. 

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

웹소켓으로 개발하기 전 알아야 할 것들

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

 

웹소켓(Web Socket)
<출처: freepik>

 

웹소켓이란?

웹소켓(Web Socket) 프로토콜은 HTTP와는 다른 통신 프로토콜로 웹 서버와 웹 브라우저가 서로 실시간 메시지를 교환하는 데에 사용된다. 웹소켓 연결을 맺기 위한 첫 번째 핸드셰이크를 주고받은 이후 지속적으로 연결이 유지되는 것이 특징이며, 매번 메시지 전송 시에 새롭게 연결을 맺을 필요가 없어 빠르고 효율적이다. 

 

웹소켓은 TCP 소켓과 이름만 유사할 뿐 브라우저의 소켓이며, 웹소켓 프로토콜은 HTTP 와 동일하게 애플리케이션 계층에서 동작한다. 그리고 평문 메시지 전송 방식이므로, SSL/TLS 보안 계층으로 암호화되어야 데이터 탈취를 방지할 수 있다. 

 

최근 웹소켓을 활용해 소규모 메타버스 서비스를 개발하는 토이 프로젝트에 참여했다. 배포가 목적이었는데 이 과정에서 예상치 못한 보안 이슈를 만났다. 이때 문제 해결 과정에서 웹소켓 통신 역시 보안이 필요하다는 것을 알게 되어 이번 글에서 살펴보고자 한다.

 

 

웹소켓 서버에서의 보안 이슈

1) CORS 에러

이번 프로젝트에서는 메신저 기능을 위해 브로드캐스팅 기능을 지원하는 socket.io 라이브러리를 활용했다. 백엔드 파트에 API 요청을 처리하는 메인 서버와 메시지를 처리하는 웹소켓 서버를 따로 두었고, 각각 별도의 포트를 할당했다. 되돌아보면 이때 처음부터 각 서버가 사용하는 모든 소스코드를 분리하지 않고 포트만 분리한 것이 실수였다. 로컬 호스트 환경에서 개발을 할 때와 도메인을 붙여 배포할 때의 통신 방식은 완전히 달라져야 하기 때문이다.

 

클라이언트가 접속하는 웹서버와 요청을 처리하는 백엔드 서버에 도메인과 인증서를 추가하고 HTTPS 통신을 시작하자, CORS(cross-origin-resource-sharing) 오류 메시지와 함께 일부 요청이 도달하지 못하는 문제가 발생했다. 의문스러운 상황이었다. 기본적으로 CORS 오류는 브라우저의 보안 원칙인 ‘동일 출처 정책’에 의해 발생한다. 이는 서로 다른 사이트 간에 리소스를 주고받는 것이 잠재적으로 위험할 수 있으니 제한하는 정책이다. 

 

하지만 양쪽에서 서로의 URL을 허용해두면 브라우저와 서버는 서로를 허용된 출처로 인식하고, 리소스를 공유할 수 있게 된다. 도메인을 붙인 후 서로의 도메인을 허용하도록 했는데 CORS 오류가 발생하여 매우 당황스러웠다. 

 

웹소켓 서버 보안 이슈
<출처: Bannerbear Blog>

 

문제는 클라이언트와의 통신이 아닌, 메인 서버와 웹소켓 서버 간 통신에 있었다. 같은 도메인이더라도 포트가 다르면 서로 같은 출처가 아니게 되고, 이 둘 사이의 통신에서 CORS 오류가 발생한 것이다. 로컬호스트에서 개발할 때는 미처 예상하지 못했던 문제였다. 이를 해결하기 위해서는 두 서버 사이에 중개자 역할을 하는 프록시 서버를 두거나 추가적인 API를 만들어야 했다.

 

2) SSL 프로토콜 에러

한편 클라이언트 웹서버와 웹소켓 서버가 웹소켓 통신을 할 때도 또 다른 보안 관련 문제가 발생했다. 클라이언트 웹서버가 HTTPS로 보호받고 있기 때문에, 웹소켓 통신에도 보안이 적용된 WSS 프로토콜을 사용해야 했다. WSS는 HTTPS와 마찬가지로 SSL/TLS에 의해 암호화된 프로토콜이다. socket.io의 경우 엔드 포인트의 HTTP URL을 받아서 폴링(polling)을 통해 연결을 수립한 후, 웹소켓 프로토콜이 지원되는지 확인하여 WS 프로토콜로 전환한다. SSL/TLS 암호화 처리가 되어있는 사이트끼리는 HTTPS 프로토콜에 의한 통신이 가능하고, 이들 사이의 웹소켓 통신 역시 암호화된다.

 

SSL 프로토콜 에러
<출처: Kinsta>

 

여기서 잠시 SSL/TLS가 무엇인지 살펴보면, 인터넷이 만들어진 초기에는 일반 사용자들이 이용하는 대부분의 사이트에서 암호화되지 않은 HTTP 프로토콜을 사용했다. 이 프로토콜을 사용하면 메시지가 평문으로 전송된다. 여기서 발생하는 보안 문제를 해결하고자, 1995년 넷스케이프에서 SSL이라는 보안 계층을 개발했다. 애플리케이션 계층에서 HTTP 메시지가 생성되면 다음 단계인 전송 계층으로 가기 전, SSL 계층을 거쳐서 암호화되도록 한 것이다. SSL은 여러 버전을 거치면서 TLS라는 이름으로 변경되었다.

 

HTTP vs HTTPS
<출처: CLOUDFLARE>

 

웹서버와 웹소켓 서버가 웹소켓 통신을 하기 위해서는 먼저 HTTPS 통신 프로토콜로 연결을 수립하고, 이후 웹소켓 업그레이드 핸드셰이크를 통해 WSS 프로토콜로 업그레이드한다. 이때 만약 한쪽이 SSL/TLS 인증서를 가지고 있지 않으면 인증서 오류로 인해 연결이 수립될 수 없다. 

 

지금까지 나온 내용을 정리하면, 웹소켓 서버는 HTTP 서버와 다르지 않으며 SSL/TLS 인증서를 통해 HTTPS 프로토콜로 클라이언트와의 핸드셰이크를 수행할 수 있어야 한다. HTTP 프로토콜에 따른 핸드셰이크 이후, WSS 프로토콜로 업그레이드가 이루어지면 웹소켓 통신이 시작되기 때문이다. 만약 웹소켓 서버와 메인 서버의 포트를 분리하고자 한다면 두 서버는 별도의 인증서를 발급받아야 하고, 두 서버는 서로 직접 통신할 수 없기 때문에 프록시 서버나 API를 거쳐야 한다.

 

 

웹소켓 서버 보안 이슈 해결 과정

이렇게 웹소켓 서버의 보안 이슈는 파악했지만, 막상 프로젝트 데모 버전 제출 기한이 채 3일도 남지 않은 상황이었다. 팀원들과 논의한 끝에 단기적인 해결과 장기적인 해결로 분리해 진행하기로 했다. 

 

1) NGINX 리버스 프록시

장기적인 해결책은 클라이언트 웹서버와 메인 서버, 웹소켓 서버에 대한 요청을 모두 NGINX에서 받아, 각각의 서버로 라우팅을 시켜주는 리버스 프록시 방식이었다. 이 방식이 효율적이고 적합하다는 것에 모두 동의했지만, 팀 내에 도커와 NGINX에 익숙한 사람이 없어 장기적인 스터디가 필요하다는 결론을 내렸다. 그래서 팀원 중 한 명이 먼저 스터디를 시작하고, 데모 버전 제출 이후 보완하여 적용하기로 했다.

 

작업 방식은 다음과 같다. 

 

  1. 먼저 백엔드 레포지토리에 섞여 있는 메인 서버와 웹소켓 서버의 소스코드를 별도의 파일로 분리하여 공유되는 전역 컨텍스트를 없앤다. 이로써 메인 서버, 웹소켓 서버는 모두 독립적으로 실행된다.
  2. 도메인과 인증서는 NGINX에 연결한다.
  3. NGINX가 받은 요청을 HTTPS용 443포트로 리다이렉트하고, 요청받은 url을 파싱하여 목적지를 확인한다. 이후 클라이언트 웹서버, 메인 서버, 웹소켓 서버 각각으로 분리해서 라우팅하도록 한다.

 

NGINX에 SSL 인증서를 적용하는 과정은 생각보다 어려웠고, 보다 간편한 인증서 관리 방식인 AWS ACM 서비스를 적용해 보기로 했다.

 

2) AWS 로드밸런서와 ACM 인증

임시방편으로는 EC2 인스턴스 두 개에 각각 메인 서버와 웹소켓 서버를 설치하고, 로드밸런서를 연결했다. AWS로드밸런서의 역할은 NGINX와 마찬가지로 80포트로 들어온 요청을 HTTPS 기본 포트인 443포트로 리다이렉트하는 것이었다. 빠르게 ACM 인증서를 요청하여 로드밸런서에 연결한 후, 각각의 로드밸런서에서 HTTPS로 보내준 요청을 EC2 인스턴스로 포워딩하였다. 

 

서버 사이를 중개해 줄 프록시 서버가 없으므로 메인 서버와 소켓 서버 간 통신도 우선 차단하고 클라이언트가 API 요청에 대한 응답을 받은 후, 다시 소켓 서버에 직접 요청을 보내는 방식으로 변경했다.

 

이로써 웹소켓 서버 보안 문제는 해결했지만, 이 방식은 두 가지 단점이 있다. 먼저 EC2 인스턴스를 별도로 운영함으로써 운영체제를 레벨의 컴퓨팅 자원이 두 배로 소모된다는 점에서 자원의 비효율이 발생한다. 또한 클라이언트가 API 와 웹소켓 요청을 모두 직접 보내야 해서 시간의 비효율도 발생한다. 이러한 단점 때문에 빠른 시일 내 리버스 프록시 방식으로 전환할 예정이다.

 

 

웹소켓 통신, 안전하게 사용하기

오늘 알아본 것처럼 웹소켓 프로토콜은 HTTP와는 다른 별도의 통신 프로토콜이며, 브라우저에서 제공하는 웹소켓을 통해 지속적인 연결이 유지되는 실시간 양방향 통신 방식이다. 하지만 핸드셰이크를 위해 HTTP 프로토콜에 의존하는 애플리케이션 계층의 프로토콜에 불과하며, 자체적인 암호화나 보안 장치를 지원하지 않는다. 

 

따라서 HTTP 통신 프로토콜들과 마찬가지로 웹소켓 프로토콜 역시 보안상의 취약점이 존재하며, 암호화 레이어를 사용하더라도 100% 안전을 보장할 수 없다. 그럼에도 SSL/TLS 암호화는 웹 표준 네트워크 보안 규칙에 해당하므로 꼭 지켜야 하며, 최소한의 안전을 보장해 주는 장치다. 

 

브라우저는 안전하지 않은 통신을 결코 허용하지 않는다. 특히 일시적인 연결을 맺는 HTTP 통신이나 폴링(polling) 방식과는 달리, 서버와 클라이언트 간에 지속적인 연결이 유지되는 웹소켓 통신은 보안 연결을 맺지 않으면 피해가 더 커질 수 있다. 브라우저가 보내는 CORS 경고 혹은 SSL/TLS 인증서에 대한 요구는 이러한 피해를 최소화하기 위한 조치이니, 참고하여 웹소켓 통신을 안전하게 사용하도록 하자.

 

<참고 자료> 

스택오버플로우

생활코딩

CANIUSE

요즘IT, CORS는 왜 이렇게 우리를 힘들게 하는 걸까?

MDN Web Docs

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
프론트엔드 엔지니어
137
명 알림 받는 중

작가 홈

프론트엔드 엔지니어
137
명 알림 받는 중
2년차 프론트엔드 개발자입니다. 리액트와 자바스크립트를 좋아합니다.

좋아요

댓글

스크랩

공유

공유

지금 회원가입하고,
요즘IT가 PICK한 뉴스레터를 받아보세요!

회원가입하기
요즘IT의 멤버가 되어주세요! 요즘IT의 멤버가 되어주세요!
요즘IT의 멤버가 되어주세요!
모든 콘텐츠를 편하게 보고 스크랩해요.
모든 콘텐츠를 편하게 보고 스크랩 하기
매주 PICK한 콘텐츠를 뉴스레터로 받아요.
매주 PICK한 콘텐츠를 뉴스레터로 받기
로그인하고 무료로 사용하기