
Redis는 메모리 기반의 key-value 저장소입니다. 일반적인 데이터베이스처럼 디스크에 데이터를 영구 저장하는 용도보다는, 빠르게 읽고 써야 하는 임시 데이터를 저장할 때 많이 사용됩니다.
대표적인 사용 예시는 다음과 같습니다.
- SMS 인증번호
- 로그인 세션
- refresh token
- 캐시 데이터
- 요청 횟수 제한 정보
- 임시 인증 상태
특히 문자 인증 로직에서는 Redis가 자주 사용됩니다. 인증번호는 오래 보관할 필요가 없고, 일정 시간이 지나면 자동으로 만료되어야 하기 때문입니다.
예를 들어 사용자가 휴대폰 인증을 요청하면 서버는 인증번호를 생성하고 Redis에 저장합니다.
key: sms:otp:01012345678
value: 123456
TTL: 180초
TTL은 Time To Live의 약자로, 데이터가 자동으로 삭제되기까지의 시간을 의미합니다. 위 예시에서는 180초, 즉 3분이 지나면 인증번호가 자동으로 삭제됩니다.
하지만 Redis는 인증번호, 세션, 토큰 같은 민감한 데이터를 다루는 경우가 많기 때문에 잘못 설정하면 심각한 보안 문제가 발생할 수 있습니다.
Redis의 기본 동작 방식
Redis는 데이터를 key-value 형태로 저장합니다.
key → value
예를 들면 다음과 같습니다.
name → kim
session:abc123 → userId=10
sms:otp:01012345678 → 123456
SMS 인증에서는 보통 다음과 같은 흐름으로 동작합니다.
1. 사용자가 휴대폰 번호를 입력한다.
2. 서버가 6자리 인증번호를 생성한다.
3. Redis에 인증번호를 저장한다.
4. SMS로 인증번호를 발송한다.
5. 사용자가 인증번호를 입력한다.
6. 서버가 Redis에서 인증번호를 조회한다.
7. 사용자가 입력한 값과 Redis 값을 비교한다.
8. 일치하면 인증 성공 처리한다.
9. 인증번호는 즉시 삭제한다.
Redis 명령어로 보면 다음과 같습니다.
SET sms:otp:01012345678 123456 EX 180
이 명령어는 다음 의미를 가집니다.
01012345678 번호의 인증번호를 123456으로 저장하고,
180초 뒤 자동으로 삭제한다.
검증할 때는 다음 명령어로 값을 조회합니다.
GET sms:otp:01012345678
인증에 성공하면 다음처럼 인증번호를 삭제합니다.
DEL sms:otp:01012345678
Redis 주요 취약점
Redis 취약점은 크게 세 가지로 나눌 수 있습니다.
1. 설정 취약점
2. 애플리케이션 설계 취약점
3. Redis 자체 버전 취약점
실무에서 가장 많이 발생하는 문제는 Redis 자체의 취약점보다는 설정 실수와 잘못된 사용 방식입니다.
1. Redis 외부 노출
가장 위험한 취약점은 Redis가 외부 인터넷에 노출되는 경우입니다.
Redis는 원칙적으로 애플리케이션 서버 내부에서만 접근해야 합니다.
정상적인 구조는 다음과 같습니다.
사용자
↓
백엔드 서버
↓
Redis
사용자는 Redis에 직접 접근할 수 없어야 합니다.
하지만 잘못 설정하면 다음과 같은 구조가 됩니다.
사용자 또는 공격자
↓
Redis 직접 접속
Redis 기본 포트는 보통 6379입니다.
Redis 기본 포트: 6379
만약 이 포트가 외부에 열려 있고, 인증도 설정되어 있지 않다면 공격자가 Redis에 직접 접속할 수 있습니다.
위험한 설정 예시는 다음과 같습니다.
bind 0.0.0.0
protected-mode no
requirepass 없음
이 설정은 다음과 같은 의미입니다.
모든 IP에서 Redis 접근 가능
보호 모드 비활성화
비밀번호 없음
이 경우 Redis 안에 저장된 인증번호, 세션, 토큰 등이 그대로 노출될 수 있습니다.
예를 들어 Redis에 다음 데이터가 저장되어 있다면:
sms:otp:01012345678 → 123456
공격자는 인증번호를 직접 확인할 수 있습니다.
따라서 Redis는 절대 외부 인터넷에 직접 노출하면 안 됩니다.
2. 인증 없음 또는 약한 비밀번호
Redis에 비밀번호가 없거나 너무 단순한 비밀번호를 사용하는 것도 위험합니다.
예를 들어 다음과 같은 비밀번호는 부적절합니다.
1234
password
redis
admin
Redis에 접근할 수 있는 사람이 있다면, 비밀번호가 없거나 약한 경우 데이터를 쉽게 읽거나 조작할 수 있습니다.
Redis 6 이후에는 ACL 기능을 사용할 수 있습니다. ACL은 사용자별로 사용할 수 있는 명령어와 접근 가능한 key 범위를 제한하는 기능입니다.
예를 들어 SMS 인증 서비스 계정은 다음 명령어 정도만 필요합니다.
GET
SET
DEL
EXPIRE
INCR
TTL
반대로 일반 애플리케이션 계정에 다음 명령어를 허용하는 것은 위험합니다.
CONFIG
FLUSHALL
FLUSHDB
KEYS
EVAL
SCRIPT
SAVE
BGSAVE
각 명령어가 위험한 이유는 다음과 같습니다.
CONFIG → Redis 설정 변경 가능
FLUSHALL → 전체 데이터 삭제 가능
FLUSHDB → 현재 DB 데이터 삭제 가능
KEYS → 전체 key 조회로 성능 저하 가능
EVAL → Lua 스크립트 실행 가능
SAVE → 디스크 저장 작업 유발
애플리케이션 서버는 필요한 명령어만 사용할 수 있도록 최소 권한 원칙을 적용해야 합니다.
3. 위험 명령어 허용
Redis에는 운영 환경에서 매우 조심해야 하는 명령어가 있습니다.
대표적인 명령어는 다음과 같습니다.
FLUSHALL / FLUSHDB
FLUSHALL
이 명령어는 Redis의 전체 데이터를 삭제합니다.
Redis에 로그인 세션, 인증번호, 캐시 데이터가 저장되어 있다면 전부 사라질 수 있습니다.
결과적으로 다음 문제가 발생할 수 있습니다.
- 모든 사용자의 로그인 세션 만료
- 인증번호 검증 실패
- 캐시 장애
- 서비스 장애
CONFIG
CONFIG GET *
CONFIG SET ...
CONFIG 명령어는 Redis 설정을 조회하거나 변경할 수 있습니다.
일반 애플리케이션 서버가 Redis 설정을 바꿀 필요는 거의 없습니다. 따라서 운영 환경에서는 CONFIG 권한을 제한하는 것이 좋습니다.
KEYS
KEYS *
KEYS 명령어는 Redis에 저장된 모든 key를 조회합니다.
개발 환경에서는 편리할 수 있지만, 운영 환경에서 key가 많을 경우 Redis가 느려질 수 있습니다. 운영 환경에서는 KEYS 대신 SCAN을 사용하는 것이 안전합니다.
EVAL / EVALSHA
EVAL "..." 0
EVAL은 Redis에서 Lua 스크립트를 실행하는 명령어입니다.
여러 명령어를 원자적으로 처리할 때 유용하지만, 권한이 과도하게 열려 있으면 위험할 수 있습니다. 특히 Redis 자체의 Lua 관련 취약점이 발생할 경우 피해가 커질 수 있으므로, 필요하지 않다면 EVAL 계열 명령어는 제한하는 것이 좋습니다.
4. 인증번호 평문 저장
문자 인증 로직에서 자주 발생하는 문제입니다.
나쁜 예시는 다음과 같습니다.
sms:otp:01012345678 → 123456
이렇게 저장하면 Redis가 노출되었을 때 인증번호가 그대로 유출됩니다.
더 안전한 방식은 인증번호를 해시해서 저장하는 것입니다.
sms:otp:<전화번호 해시> → HMAC_SHA256(인증번호 + 서버 비밀키)
예를 들면 다음과 같습니다.
sms:otp:a82f91... → 9d14c2...
이렇게 하면 Redis 데이터가 노출되더라도 원본 인증번호를 바로 알기 어렵습니다.
검증할 때는 사용자가 입력한 인증번호를 같은 방식으로 해시한 뒤 Redis에 저장된 값과 비교합니다.
사용자 입력: 123456
→ HMAC_SHA256(123456 + 서버 비밀키)
→ Redis 저장값과 비교
즉, Redis에는 원본 인증번호를 저장하지 않는 것이 좋습니다.
5. TTL 누락
Redis를 인증 로직에 사용하는 가장 큰 이유 중 하나는 TTL입니다.
TTL을 설정하면 일정 시간이 지난 뒤 데이터가 자동으로 삭제됩니다.
좋은 예시는 다음과 같습니다.
SET sms:otp:<phoneHash> <hashedOtp> EX 180
이 명령어는 인증번호를 180초 동안만 저장합니다.
반대로 다음처럼 저장하면 문제가 됩니다.
SET sms:otp:<phoneHash> <hashedOtp>
이 경우 만료 시간이 설정되지 않습니다.
TTL이 없으면 다음 문제가 발생할 수 있습니다.
- 오래된 인증번호가 계속 남음
- Redis 메모리 사용량 증가
- 불필요한 개인정보성 데이터 장기 보관
- 인증번호 재사용 위험 증가
따라서 SMS 인증번호, 임시 토큰, 세션 데이터에는 반드시 TTL을 설정해야 합니다.
인증에 성공한 경우에는 TTL이 남아 있더라도 즉시 삭제해야 합니다.
DEL sms:otp:<phoneHash>
6. 인증번호 무차별 대입 방지 부족
6자리 인증번호는 000000부터 999999까지 총 100만 가지입니다.
000000 ~ 999999
실패 제한이 없다면 공격자는 계속 인증번호를 입력하며 맞춰볼 수 있습니다.
이를 방지하려면 실패 횟수를 Redis에 저장해야 합니다.
sms:fail:<phoneHash> → 1
sms:fail:<phoneHash> → 2
sms:fail:<phoneHash> → 3
보통 다음과 같은 정책을 둡니다.
5회 실패 시 5분 차단
Redis 명령어로는 다음과 같이 처리할 수 있습니다.
INCR sms:fail:<phoneHash>
EXPIRE sms:fail:<phoneHash> 300
다만 INCR와 EXPIRE를 따로 실행하면 중간에 장애가 발생했을 때 TTL이 설정되지 않을 수 있습니다. 더 안정적으로 처리하려면 트랜잭션이나 Lua 스크립트를 고려할 수 있습니다.
단, Lua 스크립트를 사용할 경우 EVAL 권한 관리와 Redis 버전 패치를 신경 써야 합니다.
7. SMS 발송 제한 없음
SMS 인증 로직에서는 인증번호 검증 제한뿐 아니라 발송 제한도 중요합니다.
발송 제한이 없으면 공격자가 계속 SMS 발송 요청을 보낼 수 있습니다.
그 결과 다음 문제가 발생할 수 있습니다.
- SMS 비용 증가
- 특정 사용자에게 반복 문자 발송
- 서버 부하 증가
- SMS 발송 업체에서 서비스 차단
이를 방지하기 위해 Redis에 발송 제한 정보를 저장합니다.
예시는 다음과 같습니다.
sms:cooldown:<phoneHash> → 1, TTL 60초
sms:daily:<phoneHash> → 발송 횟수, TTL 하루
ip:sms:send:<ipHash> → 요청 횟수, TTL 1분
일반적인 정책은 다음과 같습니다.
같은 번호는 60초에 1회만 발송
같은 번호는 하루 5~10회까지만 발송
같은 IP는 분당 요청 횟수 제한
이런 제한이 없으면 보안 문제뿐 아니라 비용 문제도 발생합니다.
8. key에 개인정보 노출
Redis key에 전화번호, 이메일 같은 개인정보를 그대로 넣는 것도 좋지 않습니다.
나쁜 예시는 다음과 같습니다.
sms:otp:01012345678
user:email:test@example.com
이렇게 저장하면 Redis key 목록, 로그, 모니터링 도구에서 개인정보가 그대로 노출될 수 있습니다.
더 나은 방식은 개인정보를 해시해서 key에 사용하는 것입니다.
sms:otp:hash(01012345678)
예를 들면 다음과 같습니다.
sms:otp:a83f92cb19...
이렇게 하면 Redis key가 노출되더라도 실제 전화번호를 바로 알기 어렵습니다.
9. 세션 및 토큰 탈취
Redis는 로그인 세션 저장소로도 자주 사용됩니다.
예를 들어 다음과 같은 데이터가 저장될 수 있습니다.
session:abcd1234 → userId=15
refreshToken:xyz → userId=15
Redis가 노출되면 공격자가 세션이나 refresh token을 탈취할 수 있습니다.
그 결과 다음 문제가 발생할 수 있습니다.
- 사용자 계정 탈취
- 관리자 세션 탈취
- refresh token 재사용
- 로그인 상태 조작
따라서 세션이나 refresh token을 Redis에 저장할 때도 주의해야 합니다.
권장 방식은 다음과 같습니다.
- refresh token 원문 저장 지양
- token은 해시해서 저장
- TTL 설정
- 재사용 탐지 로직 적용
- 관리자 세션은 더 짧은 만료 시간 적용
10. 캐시 오염
Redis는 캐시 저장소로도 많이 사용됩니다.
예를 들어 다음과 같은 데이터가 저장될 수 있습니다.
user:15:role → USER
product:100:price → 10000
문제는 애플리케이션이 Redis 값을 지나치게 신뢰할 때 발생합니다.
만약 Redis 값이 조작되어 다음처럼 바뀐다면:
user:15:role → ADMIN
애플리케이션이 이 값을 그대로 권한 판단에 사용하면 심각한 권한 상승 문제가 발생할 수 있습니다.
따라서 중요한 판단은 Redis 캐시에만 의존하면 안 됩니다.
권한 판단
결제 금액 판단
소유권 판단
관리자 여부 판단
이런 정보는 DB나 신뢰 가능한 원본 데이터를 기준으로 검증해야 합니다.
Redis는 빠른 조회를 위한 보조 저장소이지, 항상 신뢰 가능한 보안 판단 기준이 되어서는 안 됩니다.
11. 메모리 고갈 DoS
Redis는 메모리 기반 저장소입니다.
따라서 key가 과도하게 쌓이거나 value 크기가 너무 크면 메모리 문제가 발생할 수 있습니다.
문제 상황은 다음과 같습니다.
- TTL 없는 key가 계속 생성됨
- 인증 요청이 무제한 발생함
- 캐시 key가 무제한 증가함
- 너무 큰 value가 저장됨
그 결과 다음 문제가 생길 수 있습니다.
- Redis 응답 지연
- key eviction 발생
- 세션 유실
- 인증번호 유실
- 서비스 장애
이를 방지하려면 다음 설정과 정책이 필요합니다.
- maxmemory 설정
- maxmemory-policy 설정
- 임시 key에는 TTL 필수 적용
- value 크기 제한
- 요청 횟수 제한
- Redis 메트릭 모니터링
12. TLS 미사용
Redis는 애플리케이션 서버와 네트워크로 통신합니다.
애플리케이션 서버 → Redis
이 구간이 평문이면 내부 네트워크에서 트래픽이 노출될 가능성이 있습니다.
특히 다음과 같은 환경에서는 TLS 적용을 고려해야 합니다.
- 클라우드 환경
- Kubernetes 환경
- 여러 서버 간 통신
- 관리형 Redis 사용
- VPC 피어링 또는 사설망 연결
더 안전한 구조는 다음과 같습니다.
애플리케이션 서버 → TLS → Redis
내부망이라고 해서 항상 안전하다고 볼 수는 없습니다. 민감한 인증정보나 토큰을 다룬다면 전송 구간 암호화도 고려해야 합니다.
13. Redis 자체 버전 취약점
Redis 자체에도 버전별 보안 취약점이 발생할 수 있습니다.
특히 Lua 스크립트 실행 기능과 관련된 취약점은 위험할 수 있습니다.
Redis는 EVAL 명령어를 통해 Lua 스크립트를 실행할 수 있습니다.
EVAL "..." 0
이 기능은 여러 Redis 명령어를 원자적으로 묶을 때 유용합니다. 하지만 특정 버전에 보안 취약점이 있다면 원격 코드 실행 같은 심각한 문제로 이어질 수 있습니다.
따라서 운영 환경에서는 다음을 지켜야 합니다.
- Redis 최신 안정 버전 사용
- 보안 패치 주기적 적용
- 불필요한 EVAL/EVALSHA 권한 제거
- ACL로 명령어 제한
- Redis 외부 노출 금지
버전 취약점은 시간이 지나면서 계속 새로 발견될 수 있으므로, Redis를 설치한 뒤 방치하면 안 됩니다.
SMS 인증 로직에서 안전한 Redis 설계 예시
문자 인증 기능을 Redis로 구현한다면 다음과 같은 key 구조를 사용할 수 있습니다.
sms:otp:<phoneHash> → 인증번호 해시값, TTL 3분
sms:fail:<phoneHash> → 실패 횟수, TTL 5분
sms:cooldown:<phoneHash> → 재전송 제한, TTL 60초
sms:daily:<phoneHash> → 하루 발송 횟수, TTL 하루
여기서 phoneHash는 전화번호를 그대로 쓰지 않고 해시한 값입니다.
인증번호 발송 흐름은 다음과 같습니다.
1. 사용자가 전화번호를 입력한다.
2. 전화번호 형식을 검증한다.
3. 전화번호를 정규화한다.
4. 전화번호를 해시한다.
5. 최근 60초 이내 발송 여부를 확인한다.
6. 하루 발송 횟수를 확인한다.
7. 6자리 인증번호를 생성한다.
8. 인증번호를 HMAC으로 해시한다.
9. Redis에 TTL 3분으로 저장한다.
10. SMS를 발송한다.
11. 발송 제한 key를 갱신한다.
인증번호 검증 흐름은 다음과 같습니다.
1. 사용자가 전화번호와 인증번호를 입력한다.
2. 전화번호를 정규화하고 해시한다.
3. 실패 횟수를 확인한다.
4. Redis에서 저장된 인증번호 해시값을 조회한다.
5. 값이 없으면 만료된 인증번호로 처리한다.
6. 사용자가 입력한 인증번호를 HMAC으로 해시한다.
7. Redis에 저장된 값과 비교한다.
8. 일치하지 않으면 실패 횟수를 증가시킨다.
9. 일치하면 인증 성공 처리한다.
10. 인증번호와 실패 횟수 key를 삭제한다.
11. 짧은 수명의 인증 완료 토큰을 발급한다.
안전한 Redis 운영 체크리스트
운영 환경에서 Redis를 사용할 때는 최소한 다음 항목을 확인해야 합니다.
[네트워크]
- Redis 6379 포트를 외부에 공개하지 않는다.
- 사설망 또는 VPC 내부에서만 접근하게 한다.
- 방화벽 또는 보안그룹으로 접근 IP를 제한한다.
[인증 및 권한]
- Redis ACL을 사용한다.
- 강한 비밀번호를 설정한다.
- default 사용자는 비활성화하거나 제한한다.
- 서비스별 Redis 계정을 분리한다.
- 위험 명령어를 차단한다.
[데이터 저장]
- 인증번호는 평문으로 저장하지 않는다.
- refresh token도 원문 저장을 피한다.
- 전화번호, 이메일 등을 key에 직접 넣지 않는다.
- 임시 데이터에는 반드시 TTL을 설정한다.
- 인증 성공 후 인증번호는 즉시 삭제한다.
[애플리케이션 로직]
- 인증번호 실패 횟수를 제한한다.
- SMS 발송 횟수를 제한한다.
- 같은 IP의 반복 요청을 제한한다.
- 권한 판단을 Redis 캐시에만 의존하지 않는다.
[운영 관리]
- Redis를 최신 안정 버전으로 유지한다.
- 보안 패치를 적용한다.
- maxmemory와 eviction policy를 설정한다.
- Redis 메모리 사용량을 모니터링한다.
- slowlog와 접속 로그를 확인한다.
- 필요한 경우 TLS를 적용한다.
마무리
Redis는 매우 빠르고 편리한 저장소입니다. SMS 인증번호, 로그인 세션, 캐시, 요청 제한 정보처럼 짧은 시간 동안 필요한 데이터를 처리하기에 적합합니다.
하지만 Redis에 인증번호, 세션, 토큰 같은 민감한 데이터가 저장된다면 단순한 캐시 서버가 아니라 보안적으로 중요한 저장소로 봐야 합니다.
가장 위험한 조합은 다음과 같습니다.
Redis 외부 노출
+ 비밀번호 없음
+ 인증번호와 세션 평문 저장
+ TTL 없음
+ 요청 제한 없음
+ 위험 명령어 허용
Redis 보안의 핵심은 어렵지 않습니다.
외부에 열지 않고,
인증과 권한을 설정하고,
민감정보는 평문으로 저장하지 않고,
TTL과 요청 제한을 반드시 적용하는 것
특히 SMS 인증 로직에서는 Redis 자체보다 “Redis에 어떤 데이터를 어떻게 저장하고, 누가 접근할 수 있게 할 것인가”가 더 중요합니다. Redis는 빠른 임시 저장소지만, 인증 시스템에 사용되는 순간 보안 저장소처럼 다뤄야 합니다.
'Security > 웹' 카테고리의 다른 글
| 모의해킹 방법론 OWASP WSTG (0) | 2026.04.21 |
|---|---|
| 취약점 점검 후기 (0) | 2026.04.02 |
| [WEB] wizmall로 Bug Bounty 실습해보기 (0) | 2026.03.18 |
| [web] NoSQL 인젝션(Injection) (0) | 2026.03.01 |
| [web] SSTI 알아보기 (0) | 2026.02.15 |