보안 (Security)

보안 (Security)

Redis의 보안 모델과 보안 기능을 소개하는 문서예요. 이 문서에서는 Redis가 제공하는 접근 제어, 코드 보안 이슈, 악의적인 입력을 이용한 외부 공격, 그리고 그 밖의 관련 주제를 다룹니다. 접근 제어, 데이터 보호와 암호화, 안전한 Redis 아키텍처, 안전한 배포 기법에 대해 더 자세히 배우고 싶다면 Redis University 보안 과정을 들어보세요.

보안 관련 문의는 GitHub에 이슈를 올리면 되고, 통신 보안을 반드시 지켜야 한다고 생각될 만큼 중요한 경우에는 GPG 키 파일을 내려받아 사용하면 됩니다.

보안 모델

Redis는 신뢰할 수 있는 클라이언트가 신뢰할 수 있는 환경 안에서 접근하도록 설계되었어요. 다시 말해 Redis 인스턴스를 인터넷에 직접 노출하거나, 신뢰할 수 없는 클라이언트가 Redis TCP 포트나 UNIX 소켓에 직접 접근할 수 있는 환경에 두는 것은 대개 좋지 않은 선택입니다.

예를 들어 Redis를 데이터베이스, 캐시, 또는 메시징 시스템으로 사용하는 일반적인 웹 애플리케이션을 떠올려 봅시다. 이때 애플리케이션의 프런트엔드(웹 측)에 있는 클라이언트는 웹 애플리케이션 사용자가 요청하거나 트리거한 작업을 수행하기 위해, 또는 페이지를 생성하기 위해 Redis를 조회하게 됩니다.

이 경우 웹 애플리케이션이 Redis와 신뢰할 수 없는 클라이언트(웹 애플리케이션에 접근하는 사용자 브라우저) 사이의 접근을 중개해 줘요. 일반적으로 Redis에 대한 신뢰할 수 없는 접근은 항상 ACL을 구현하고, 사용자 입력을 검증하며, Redis 인스턴스에 대해 어떤 작업을 수행할지 결정하는 계층이 중개해야 합니다.

네트워크 보안

네트워크에서 신뢰할 수 있는 클라이언트를 제외한 모든 접근은 Redis 포트에서 차단되어야 합니다. 그래서 Redis를 실행하는 서버는 Redis를 사용하는 애플리케이션을 구현하는 컴퓨터만 직접 접근할 수 있어야 해요.

인터넷에 직접 노출된 단일 컴퓨터(가상화된 Linux 인스턴스, 예: Linode, EC2 등)가 일반적인 경우라면, Redis 포트를 방화벽으로 막아 외부에서의 접근을 차단해야 합니다. 그래도 클라이언트는 루프백 인터페이스(loopback interface)를 통해 Redis에 접근할 수 있습니다.

Redis를 특정 인터페이스 하나에만 바인딩하려면 redis.conf 파일에 다음과 같은 줄을 추가하면 됩니다.

bind 127.0.0.1

Redis의 특성상 외부에서 Redis 포트를 보호하지 않으면 보안에 큰 영향을 줄 수 있어요. 예를 들어 외부 공격자가 단 하나의 FLUSHALL 명령만으로 전체 데이터셋을 삭제할 수 있습니다.

보호 모드 (Protected mode)

안타깝게도 많은 사용자가 Redis 인스턴스를 외부 네트워크에서의 접근으로부터 보호하지 않습니다. 많은 인스턴스가 공인 IP를 그대로 노출한 채 인터넷에 방치돼 있어요. Redis 3.2.0부터는 기본 구성(모든 인터페이스에 바인딩)으로 실행되고 접근 암호가 없는 경우, Redis가 **보호 모드(protected mode)**라는 특수 모드로 진입합니다. 이 모드에서 Redis는 루프백 인터페이스에서 온 쿼리에만 응답하고, 다른 주소에서 연결하는 클라이언트에는 문제를 설명하고 Redis를 올바르게 구성하는 방법을 안내하는 오류를 응답합니다.

보호 모드 덕분에 제대로 관리되지 않고 보호되지 않은 채 실행되는 Redis 인스턴스로 인한 보안 문제가 크게 줄어들 것으로 기대합니다. 다만 시스템 관리자는 여전히 Redis가 반환하는 오류를 무시하고 보호 모드를 끄거나 모든 인터페이스에 수동으로 바인딩할 수는 있습니다.

인증 (Authentication)

Redis는 클라이언트를 인증하는 두 가지 방법을 제공해요. Redis 6에서 도입된 권장 인증 방식은 **Access Control List(ACL)**로, 이름 있는 사용자를 만들고 세밀한 권한을 부여할 수 있습니다. Access Control List에 대한 자세한 내용은 여기에서 읽어보세요.

레거시 인증 방식은 redis.conf 파일을 편집하고 requirepass 설정으로 데이터베이스 암호를 지정하는 방식이에요. 이 암호는 모든 클라이언트가 공유해서 사용하게 됩니다.

requirepass 설정이 활성화되면 Redis는 인증되지 않은 클라이언트의 어떤 쿼리도 거부합니다. 클라이언트는 AUTH 명령 다음에 암호를 보내 자신을 인증할 수 있어요.

암호는 시스템 관리자가 redis.conf 파일에 평문(clear text)으로 설정합니다. 암호는 두 가지 이유로 무차별 대입(brute force) 공격을 막을 수 있을 만큼 충분히 길어야 해요.

  • Redis는 쿼리 처리가 매우 빨라서, 외부 클라이언트가 초당 수많은 암호를 시험할 수 있습니다.
  • Redis 암호는 redis.conf 파일과 클라이언트 구성 안에 저장됩니다. 시스템 관리자가 암호를 외울 필요가 없으므로 암호를 아주 길게 설정할 수 있어요.

인증 계층의 목표는 선택적으로 중복(redundancy) 계층을 제공하는 것이에요. 외부 공격자로부터 Redis를 보호하기 위해 만든 방화벽이나 다른 시스템이 실패하더라도, 인증 암호를 모르는 외부 클라이언트는 여전히 Redis 인스턴스에 접근할 수 없습니다.

AUTH 명령은 다른 모든 Redis 명령과 마찬가지로 암호화되지 않은 채 전송되기 때문에, 네트워크에 대한 충분한 접근 권한이 있어 도청(eavesdropping)을 수행할 수 있는 공격자에게는 보호 기능을 제공하지 못해요.

TLS 지원

Redis는 모든 통신 채널, 즉 클라이언트 연결, 복제 링크, Redis Cluster 버스 프로토콜에서 선택적으로 TLS를 지원합니다.

특정 명령 금지 (Disallowing specific commands)

경고:

이 방식은 더 이상 권장되지 않으며(디프리케이트) 향후 버전에서 제거될 수 있어요. 특정 명령을 금지하려면 ACL 규칙을 사용하세요.

Redis에서는 명령을 금지하거나 알아내기 어려운 이름으로 바꿔서(rename), 일반 클라이언트가 사용할 수 있는 명령 집합을 제한할 수 있습니다.

예를 들어 가상화 서버 제공자가 관리형 Redis 인스턴스 서비스를 제공한다고 해 봅시다. 이런 상황에서 일반 사용자는 인스턴스 구성을 변경할 수 있는 CONFIG 명령을 호출하면 안 되겠지만, 인스턴스를 생성·제거하는 시스템은 그렇게 할 수 있어야 할 겁니다.

이 경우 명령 테이블에서 명령을 rename 하거나 완전히 숨길 수 있어요. 이 기능은 redis.conf 구성 파일 안에서 사용할 수 있는 구문으로 제공됩니다. 예를 들면 다음과 같아요.

rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52

위 예시에서는 CONFIG 명령이 알아내기 어려운 이름으로 바뀌었습니다. 아래 예시처럼 빈 문자열로 rename 하면 명령을 완전히 금지할 수도 있어요.

rename-command CONFIG ""

외부 클라이언트의 악의적 입력으로 유발되는 공격

인스턴스에 대한 외부 접근 권한이 없어도 공격자가 외부에서 트리거할 수 있는 공격 종류가 있어요. 예를 들어 공격자는 Redis 내부에서 구현된 데이터 구조에 병리적(최악의 경우) 알고리즘 복잡도를 유발하는 데이터를 Redis에 삽입할 수 있습니다.

공격자는 웹 폼을 통해 해시 테이블에서 같은 버킷으로 해시되는 것으로 알려진 문자열 집합을 제공해서, O(1)의 기대 시간(평균 시간)을 O(N)의 최악의 경우로 바꿀 수 있어요. 이는 예상보다 많은 CPU를 소모하게 되어 결국 서비스 거부(Denial of Service)를 일으킵니다.

이 특정 공격을 막기 위해 Redis는 해시 함수에 실행별 의사 난수 시드(per-execution pseudo-random seed)를 사용합니다.

Redis는 SORT 명령을 qsort 알고리즘으로 구현합니다. 현재 이 알고리즘은 무작위화되지 않아서, 올바른 입력 집합을 신중하게 선택하면 2차(quadratic) 최악의 경우 동작을 유발할 수 있어요.

문자열 이스케이프와 NoSQL 인젝션

Redis 프로토콜에는 문자열 이스케이프 개념이 없기 때문에, 일반 클라이언트 라이브러리를 사용하는 정상적인 상황에서는 인젝션이 불가능합니다. 이 프로토콜은 길이가 접두사로 붙은 문자열(prefixed-length strings)을 사용하며 완전히 바이너리 안전(binary safe)합니다.

EVALEVALSHA 명령이 실행하는 Lua 스크립트도 같은 규칙을 따르므로, 이 명령들도 안전합니다.

드문 사용 사례지만, 애플리케이션은 신뢰할 수 없는 소스에서 얻은 문자열로 Lua 스크립트 본문을 조합하는 것은 피해야 합니다.

코드 보안

전형적인 Redis 구성에서 클라이언트는 명령 집합에 대한 전체 접근 권한을 갖지만, 인스턴스에 접근한다고 해서 Redis가 실행 중인 시스템을 제어할 수 있게 되어서는 안 됩니다.

내부적으로 Redis는 버퍼 오버플로, 포맷 버그, 그리고 기타 메모리 손상 문제를 막기 위해 안전한 코드를 작성하는 모든 잘 알려진 관행을 사용해요. 하지만 CONFIG 명령으로 서버 구성을 제어할 수 있게 되면, 클라이언트가 프로그램의 작업 디렉터리와 덤프 파일 이름을 변경할 수 있습니다. 이는 클라이언트가 RDB Redis 파일을 임의의 경로에 쓸 수 있게 만듭니다. 이는 보안 이슈로, 시스템을 침해하거나 Redis가 실행 중인 사용자와 같은 권한으로 신뢰할 수 없는 코드를 실행할 수 있는 가능성으로 이어질 수 있어요.

Redis는 실행하는 데 루트 권한이 필요하지 않습니다. 이 목적을 위해서만 사용하는 권한 없는(비특권) redis 사용자로 실행하는 것이 권장됩니다.

기타 보안 주제

이 섹션의 다른 페이지들에서 보안 주제를 더 자세히 다룹니다.