WebSocket - 주요 보안 위협과 방어 전략

강동현·2025년 4월 26일
0

Spring Websocket

목록 보기
7/9

WebSocket은 현대 웹 애플리케이션에서 실시간 양방향 통신을 가능하게 하는 기술이다. websocket은 초기 핸드셰이크 과정에서 HTTP와 호환되도록 설계되었지만, 바로 이 점과 연결이 지속된다는 특성 때문에 기존 HTTP와는 다른 보안 위협에 노출된다. 단순 HTTP의 보안 지식만으로는 websocket을 지키기 어렵다.

WebSocket의 보안 취약점

websocket을 사용할 때는 반드시 알아야될 주요 취약점이 존재한다. 이러한 취약점을 효과적으로 대응하기 위해서 방어 전략 OWASP 가이드라인, RFC 6455 표준 등을 기반으로 분석해보려고 한다. Cross-Site WebSocket Hijacking(CSWSH)부터 전송 계층 암호화(WSS), 서비스 거부 공격, 데이터 검증, 그리고 인증/인가 문제까지 알아보자.

CSWSH : 내 세션이 탈취당한다고?

가장 대표적인 WebSocket 취약점 중 하나인 CSWSH는 WebSocket 핸드셰이크 과정의 CSRF 취약점을 이용한 공격이다.

공격이 어떻게 이루어질까?

  1. 로그인 및 쿠키 : 사용자가 정상 웹사이트에 로그인하면, 브라우저에 세션 쿠키가 저장된다.
  2. 악성 사이트 방문 : 사용자가 공격자가 만든 악성 사이트를 방문
  3. 위조된 핸드셰이크 : 악성사이트의 스크립트가 원래 사이트의 websocket 엔드포인트로 연결 요청을 보낸다. 이때 브라우저는 자동으로 원래 사이트의 세션 쿠키를 이 요청에 첨부한다. 요청의 Origin 헤더는 악성 사이트가 된다.
  4. SOP 우회 및 서버의 착각 : 브라우저는 교차 출처 WebSocket 연결 시도 자체를 막지 않는다. 만약 서버가 쿠키만으로 사용자 인증을 하고 Origin 헤더를 검증하지 않으면 이 요청을 정상 사용자의 요청으로 착각하고 연결을 수락한다.
  5. 세션 하이재킹 : 공격자는 이제 피해자의 인증된 세션으로 WebSocket 연결을 확보하고, 서버와 양방향 통신을 하며 데이터를 훔치거나 조작할 수 있게 된다. 서버 응답까지 읽을 수 있어 일반 CSRF보다 훨씬 위험하다.

CSWSH 막기 : 다층 방어 필수

CSWSH는 여러 요인이 결합되어 발생하기 때문에 방어도 여러 계층에서 이루어져야 한다.

  • Origin 헤더 검증 : 핸드셰이크 요청의 Origin 헤더 값이 신뢰할 수 있는 도메인 목록에 있는지 서버에서 확인, 없으면 연결을 거부해야 된다. 하지만 브라우저 외 클라이언트는 Origin 헤더를 위조할 수 있기 때문에 이것만으로는 부족하다.

  • CSRF 토큰 사용 : 예측 불가능한 CSRF 토큰을 사용한다.

    • 서버는 사용자 세션별 고유 토큰을 생성해서 웹페이지에 전달한다.
    • 클라이언트는 WebSocoket 연결 시 이 토큰을 URL 쿼리 파라미터 등으로 핸드셰이크 요청에 포함시킨다.
    • 서버는 요청의 토큰과 세션의 토큰을 비교 검증해서 공격자는 유효한 토큰을 알 수 없으므로 효과적으로 방어할 수 있다.
  • SameSite 쿠키 속성 활용 : 세션 쿠키에 SameSite=Lax 또는 SameSite=Strict 속성을 설정해서 브라우저가 악성 사이트에서 보낸 교차 출처 요청에 세션 쿠키를 첨부하는 것을 방지한다.

Lax가 많은 브라우저의 기본값이지만, 일부 GET 요청 등에서는 우회될 수 있기 때문에 다른 방어책과 함께 사용하는 것이 좋다. Strict는 강력하지만 합법적인 교차 사이트 기능에 영향을 줄 수 있다.

최종적으로 Origin 검증 + CSRF 토큰 + SameSite 쿠키 조합으로 다층 방어를 구축하는게 가장 안전하다.

전송 계층 보안 : WSS는 기본

wss는 WebSocket 통신을 TLS/SSL로 암호화하는 것을 의미한다. 웹의 https와 동일한 역할이다.

WSS는 왜 필수일까?

  • 기밀성 : TLS핸드셰이크를 통해 교환된 대칭 키로 모든 메시지를 암호화한다. 중간에서 트래픽을 가로채도 내용을 읽을 수 없다.
  • 무결성 : 메시지 전송 중에 변조되지 않음을 보장한다.

만약 암호화되지 않은 ws를 사용하면 공용 와이파이등에서 쉽게 메시지를 엿보거나 조작 가능하다 운영 환경에선 wss를 사용하자

WSS 설정 방법

WSS를 사용하려면 서버 또는 리버스 프록시에 TLS 인증서 설정이 필요하다.
Spring 내장 서버에서 TLS 설정

# HTTPS (WSS 기반) 활성화
server.ssl.enabled=true
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore/server.p12
server.ssl.key-store-password=your_keystore_password
server.ssl.key-alias=server_alias
server.ssl.enabled-protocols=TLSv1.3,TLSv1.2

서비스 거부 공격 : 연결만 맺어도 위험?

WebSocket의 지속적 연결 특징은 Dos공격에 악용될 수 있다.

  • 공격 벡터 :
    • 연결 폭주 : 서버의 처리 용량을 넘어서는 대량의 연결 시도
    • 대용량 페이로드 : 비정상적으로 큰 메시지를 보내 메모리/대역폭 고갈 시도
    • 자원 소모적 작업 유발 : 메시지를 통해 서버에 부하가 큰 작업 요청
  • 방어 전략 :
    • 연결 제한 : 서버 인스턴스당, IP 주소당 최대 동시 연결 수를 제한
    • 메시지 크기 제한 : WebSocket 라이브러리/프레임워크 설정에서 수신 메시지의 최대 크기를 합리적으로 제한한다.
      - IP 기반 속도 제한 : 단위 시간당 IP별 연결 시도/메시지 전송 횟수를 제한한다.
    • 인증 우선 & 비활성 타임아웃 : 가능한 빨리 인증하고 인증되지 않은 연결이나 오랫동안 활동이 없는 연결은 타임아웃으로 종료시켜 불필요한 자원 소모를 막자

데이터 검증

WebSocket은 데이터 내용을 검사하진 않는데 데이터 내용을 애플리케이션 레벨에서 철저히 검증을 해야된다.

  • XSS : 악성 스크립트가 포함된 메시지가 다른 사용자에게 전달되어 실행될 수 있다.
  • Injection 공격 : 페이로드 데이터가 안전하지 않게 DB 쿼리 등에 사용될 경우 발생한다.

다음과 같이 적용해보자.

  • 서버측 검증 : 모든 입력에 대해 형식, 타입, 길이, 범위 등을 엄격하게 검증한다.
  • 출력 인코딩 : 데이터를 UI에 표시할 때는 컨텍스트에 맞게 인코딩하여 XSS를 방지한다.
  • 안전한 API 사용 : DB 접근 시 Parameterized Query/Prepared Statement를 사용한다.
  • 클라이언트 측 검증은 보조 : 사용자 경험 향상 목적이며, 보안 목적으로 신뢰해선 안된다.

인증과 인가 누가 하지?

WebSocket 자체는 인증/인가 표준이 없다. 애플리케이션에서 직접 구현해야 된다.

  • 인증 접근 방식 :
    • 핸드셰이크 인증:
      • 쿠키 : 구현은 쉽지만 CSWSH에 취약해서 안된다.
        • 쿼리 파라미터 토큰 : 구현은 쉽고 교차 출처 가능하지만 URL에 토큰이 노출되어 로깅 위험. 단기/일회성 토큰 사용을 고려해야 된다.
    • 연결 후 인증 : URL/헤더 노출은 없지만 자체 프로토콜 구현이 필요하고 dos 위험이 존재한다.
    • 티켓 기반 인증 : 연결 전 별도 HTTP 요청으로 단기 티켓 발급 후 WebSocket 연결 시 사용 복잡성이 높지만 보안 강화됨.

Spring Security 통합

  • 주로 핸드셰이크 인증 방식을 사용
  • @EnableWebSocketSecurity 어노테이션으로 보안 기능을 활성화 한다.
  • 인가 : AuthorizationManager<Message<?>>를 구현해서 목적지, 유형, 역할등에 세밀한 접근 제어 설정이 가능하다.

안전한 WebSocket을 위한 다층 방어 전략

WebSocket은 강력하지만 그 만큼 보안에 신경써야될 부분이 많다.

  1. WSS 사용 : wss://로 전송 계층을 강화하자.
  2. CSWSH 방어 : Origin 검증, CSRF 토큰, SameSite 쿠키를 조합해서 핸드셰이크를 보호하자
  3. 모든 페이로드 검증 : 입력값 검증 출력 인코딩 필수
  4. 인증/인가 : security와 함께 사용하여 목적지, 유형, 역할등 세밀한 접근 제어 설정하자.
  5. dos 완화 : 연결 수, 메시지 크기, 요청 속도 제한 타임아웃 설정하자

마무리

이렇게 WebSocket 보안에 대해서 살펴봤는데 본인이 생각하지 못한 부분도 함께 공부할 수 있어서 좋았고 이렇게 구성하는 과정에서 막히는 부분이 있다면 이후에 그 부분에 대해서 다뤄보려고 한다.

profile
스스로에게 질문하고 답을 할 줄 아는 개발자

0개의 댓글