RESTful API 서버를 위협하는 한 글자, 슬래시 – CVE-2016-5007

🧐 | 2022-01-24

Spring Security Path Matching Inconsistency 취약점

안녕하세요, 넷마블 보안실 보안개발팀 전희창입니다.

이번에 소개할 취약점은 스프링 프레임워크 기반 RESTful API 서버 환경에서 발생하는 취약점입니다.

CVE-2016-5007

CVE-2016-5007
최초 공개: 2016년 7월 7일

CVE-2016-5007 취약점은 2016년 7월 7일에 최초 공개됐습니다. 어느덧 최초 공개일로부터 5년을 훌쩍 넘었습니다. 하지만 최근까지도 스프링 프레임워크 기반 RESTful API 서버를 사용하는 여러 서비스에서 심각한 보안 위협이 발견되고 있어서, 연구 과제로 선정했습니다.

스프링 시큐리티(Spring Security)는 스프링에서 애플리케이션 인증과 권한 부여 같은 보안 기능을 제공하는 하위 프레임워크입니다. CVE-2016-5007 취약점은 스프링 시큐리티가 지원하는 권한 검증과 URL 패턴 검사 로직을 우회하도록 허용시켜서 접근 권한 관리를 무력화시킵니다. 즉, 접근 권한이 없는 URL에 우회 접근할 수 있다는 의미입니다.

특히 스프링 시큐리티에서 취약한 함수를 사용한다면, 버전에 상관없이 취약점이 발생한다는 점만으로도 매우 위협적인 취약점이라 할 수 있습니다.

스프링 프레임워크와 RESTful API

스프링 프레임워크는 자바(Java) 기반 동적 웹 프레임워크로, 웹 개발에서 널리 사용하고 있습니다. 스프링 프레임워크는 하위 프레임워크인 스프링 시큐리티를 통해 보안 기능을 제공하며, RESTful API도 지원합니다.

RESTful API란 REST(Representational State Transfer) 아키텍처 디자인 원칙을 따르는 API로, HTTP 통신 프로토콜을 사용합니다. 어떤 플랫폼이든지 API 규칙에 맞춰서 요청하면 서버에서 리소스를 응답받을 수 있어, 다양한 웹 서비스에서 사용하고 있습니다. 공공기관 홈페이지나 주요 포털 사이트에서 제공하는 OPEN API가 REST 아키텍처를 사용하고 있습니다. 그래서 이를 활용해 다양한 클라이언트나 서버 환경에서 OPEN API 리소스를 활용할 수 있습니다.

RESTful API 서버 인증 우회 공격

RESTful API 설계 규칙에서는 URI값에 슬래시(/) 구분자를 삽입하는 방법으로 계층을 구분합니다. 또한, URI값은 유일한 식별자로 사용해야 하므로, 맨 끝은 혼동을 주지 않도록 반드시 문자로 끝나야 합니다. 만약 계층 구분용으로 사용해야 하는 슬래시(/)를 URI값 맨 끝에 넣는다면 부정확한 값으로 인식돼 혼동을 줄 수 있습니다.

스프링 프레임워크 기반 RESTful API 서버라면, 스프링 시큐리티로 URI 규칙을 미리 등록해두는 방식으로 접근 제어 규칙을 설정합니다. 그래서 사용자가 자원을 요청하면 미리 등록한 접근 제어 규칙과 매칭해서 허용 또는 차단을 결정합니다.

위 두 가지 특징으로 인해 RESTful API 서버에 인증 또는 인가한 사용자만 접근할 수 있는 자원을 요청하는 URI값 맨 끝에 슬래시(/)를 붙이면, 스프링 시큐리티의 접근 제어 규칙을 우회하는 틈이 생깁니다. 이 틈을 파고들면 허가받지 않은 사용자가 내부 자원에 접근할 수 있습니다. (내부 자원에 접근할 수 있는 만큼, 중요 정보를 조회하고 유출할 가능성이 생긴다고 할 수 있습니다.)

대처 방법

이 취약점은 기본적으로 스프링 시큐리티 접근 제어 규칙을 화이트리스트 기반으로 설정하면 막을 수 있습니다. 하지만 서비스 운영상 불가피하게 블랙리스트 기반으로 규칙을 사용해야 한다면, URI값 맨 끝에 붙인 슬래시(/)로 접근 제어 규칙 우회를 불가능하도록 설정해야 합니다.

화이트리스트와 블랙리스트
화이트리스트는 허용하는 조건 이외에는 모두 차단하는 접근 제어 방식입니다. 반대로, 블랙리스트는 차단하는 조건 이외에는 모두 허용하는 접근 제어 방식입니다.

블랙리스트 기반 규칙인 경우, 아래 두 예시를 기준으로 취약한 버전과 안전한 버전을 비교해보겠습니다.

// 잘못된 설정 예시
.antMatchers(HttpMethod.GET, "/api/admins").hasRole("ADMIN")

// 올바른 설정 예시
.antMatchers(HttpMethod.GET, "/api/admins/**").hasRole("ADMIN")

취약한 버전

취약한 버전의 서버 환경 조건은 아래와 같습니다.

서버 환경

  • 스프링 부트(Spring boot)로 구현한 RESTful API 서버
  • 스프링 시큐리티 라이브러리를 사용한 보안 설정
  • 블랙리스트 기반 접근 제어

블랙리스트 기반 접근 제어 방식에서는 ‘/api/admins/’라는 URI로 요청이 올 경우, 스프링 시큐리티의 ‘antMatchers’ 함수에서 ‘/api/admins’와 문자열을 비교해 서로 일치하지 않는 것으로 판단하고 차단(deny)으로 넘어가야 합니다.

하지만 REST 아키텍처 규칙상 URI 끝에는 슬래시(/)가 붙지 않아야 하므로, ‘/api/admins/’ URI 요청을 ‘/api/admins’로 인식해버려 접근을 허용해버립니다. 이런 상황이라면, 비인증 상태로도 내부 자원에 접근할 수 있습니다.

아래 화면은 최초에는 비인증 상태일 때 API 요청을 해도 자원에 접근할 수 없었으나, URI 끝에 슬래시(/)를 삽입해 비인증 상태임에도 자원에 접근한 공격 결과입니다.

안전한 버전

화이트리스트 기반 접근 제어

화이트리스트 기반 접근 제어는 스프링(Spring) 측 공식 가이드에도 올라온 내용입니다. 접근할 수 있는 URI만 명시하고, 그 외 모든 URI는 인증이나 권한을 검증해야 합니다.

블랙리스트 기반에서는 URI 끝에 와일드카드

블랙리스트 기반 접근 제어를 사용해야 한다면, URI 입력 규칙에서 인증 및 권한 검증이 필요한 URI 끝에 와일드카드(/**)를 붙여야 합니다.

mvcMatchers 함수 사용

스프링 시큐리티에서는 ‘antMatchers’ 함수가 가진 취약점을 보완하기 위해, 보안 기능을 추가한 ‘mvcMatchers’ 함수를 제공하고 있습니다. ‘mvcMatchers’ 함수를 사용해 ‘/api/admin’을 설정하면 ‘/api/admin/’, ‘/api/admin.html’, ‘/api/admin.xyz’ 모두 매핑할 수 있습니다.

조치 완료

안전한 버전으로 조치를 마치면, URI 끝에 슬래시(/)를 삽입해도 접근 제어 우회가 불가능한 것을 확인할 수 있습니다.

서비스를 운영하다 보면, 레퍼런스에 맞춰서 화이트리스트 접근 제어 방식만 고집하기는 쉽지 않을 것입니다. 규칙으로 정의한 값을 꾸준히 업데이트하고, 접근하는 URI 매칭 로직에 와일드카드를 적절히 활용해 융통성과 보안성이라는 두 마리 토끼를 모두 꼭 잘 챙기셨으면 좋겠습니다.