설명
보안 민감한 값(세션 ID, 토큰, 임시 비밀번호 등)을 생성할 때 java.util.Random, Math.random(), ThreadLocalRandom, SplittableRandom 같은 암호학적으로 안전하지 않은 PRNG를 사용하면 값이 예측될 수 있습니다. 약한 PRNG는 시드와 내부 상태가 비교적 쉽게 추정되어 다음 출력이 유추될 수 있습니다. 공격자는 관측 가능한 일부 값(쿠키, 링크의 토큰 등)을 기반으로 이후 값을 계산하거나 무차별 대입으로 빠르게 맞춰 세션 탈취, 계정 탈취 등을 시도할 수 있습니다.
잠재적 영향
- 세션 하이재킹: 예측 가능한 세션 ID/쿠키 값을 추측해 합법 사용자로 가장할 수 있습니다.
- 계정 탈취: 임시 비밀번호, 로그인 토큰, 이메일 인증 토큰 등을 맞춰 계정을 장악할 수 있습니다.
- 권한 남용/상승: 접근 토큰이나 API 키가 추정되면 권한 범위를 넘어선 행위를 수행할 수 있습니다.
- CSRF 우회: 예측 가능한 CSRF 토큰으로 위조 요청을 성공시킬 수 있습니다.
- 개인정보 유출: 인증·인가가 우회되면서 민감 데이터 접근이 가능해집니다.
해결 방법
- 보안 용도에는 반드시 java.security.SecureRandom 사용
- 예) SecureRandom sr = new SecureRandom(); byte[] b = new byte[32]; sr.nextBytes(b);
- 약한 PRNG(Random, Math.random(), ThreadLocalRandom, SplittableRandom)를 세션/토큰/코드 등 보안값 생성에 사용하지 않기
- 시드 고정 금지: new SecureRandom() 기본 엔트로피 사용, 직접 고정 시드 사용 금지
- 충분한 엔트로피 확보: 최소 128비트 이상(권장 192~256비트) 랜덤 바이트 생성 후 Base64(URL-safe) 인코딩
- 토큰 수명 제한 및 1회성 사용: 비밀번호 재설정/이메일 인증 토큰은 짧은 만료시간과 1회 사용 정책 적용
- 서버 측 검증 강화: 추측 시도를 탐지하기 위한 속도 제한(rate limiting) 및 실패 횟수 제한 적용
예시
변경 전
java
import javax.servlet.http.*;
import java.io.IOException;
import java.util.concurrent.ThreadLocalRandom;
public class WeakCookieServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
long id = ThreadLocalRandom.current().nextLong(); // 약한 PRNG
String value = Long.toHexString(id);
resp.addHeader("Set-Cookie", "SID=" + value + "; HttpOnly; Secure");
resp.getWriter().println("ok");
}
}
변경 후
java
import javax.servlet.http.*;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Base64;
public class StrongCookieServlet extends HttpServlet {
private static final SecureRandom SR = new SecureRandom();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
byte[] token = new byte[32]; // 256비트 엔트로피
SR.nextBytes(token);
String value = Base64.getUrlEncoder().withoutPadding().encodeToString(token);
resp.addHeader("Set-Cookie", "SID=" + value + "; HttpOnly; Secure; SameSite=Strict");
resp.getWriter().println("ok");
}
}
설명:
- 변경 전: ThreadLocalRandom은 성능 최적화용 PRNG로, 보안 목적에 필요한 예측 불가능성을 제공하지 않습니다. 이렇게 생성한 쿠키 값은 추정 또는 빠른 무차별 대입으로 맞춰질 가능성이 있어 세션 탈취로 이어질 수 있습니다.
- 변경 후: SecureRandom으로 256비트 난수를 생성해 URL-safe Base64로 인코딩합니다. 고정 시드를 사용하지 않고 충분한 엔트로피를 확보해 추측 위험을 줄입니다. 토큰의 만료와 서버 측 검증은 별도로 구현해야 합니다.