모바일 게임 서버의 로그인 토큰을 설계하면서 서명 알고리즘을 RSA로 할지 ECDSA로 할지 정해야 했다.
최종 선택은 ES256이었고, 결정의 근거는 보안 강도가 아니라 토큰 크기였다.
같은 보안 수준에서 ECDSA 키가 훨씬 작고, 모바일 환경에서는 그 차이가 패킷 사이즈로 그대로 이어진다.
1. 비대칭키의 기본 개념
대칭키는 열쇠 하나로 잠그고 여는 방식이다.
그래서 키가 유출되면 그 키로 잠근 모든 것이 위험해진다.
비대칭키는 자물쇠 역할의 공개키와 열쇠 역할의 비밀키가 따로 있다.
- 공개키: 외부에 공개해도 된다.
"이걸로 잠가서 나한테 보내라"는 용도다. - 비밀키: 나만 가지고 있어야 한다.
"이걸로 내가 열어본다"는 용도다.
이 관계는 반대 방향으로도 성립한다.
비밀키로 잠그고(서명) 공개키로 여는(검증) 방식, 이것이 전자서명이다.
2. ECDSA는 서명 전용 알고리즘이다
여기서 흔한 오해가 있다.
ECDSA가 RSA보다 최신 기술이므로 암호화 성능도 더 좋을 것이라고 생각하기 쉽다.
ECDSA는 암호화 알고리즘이 아니다.
이름을 풀어 보면 Elliptic Curve Digital Signature Algorithm, 즉 서명(Signature) 알고리즘이다.
데이터 암호화가 필요하다면 ECIES(Elliptic Curve Integrated Encryption Scheme) 등을 사용해야 하며, ECDSA 키로 암호화를 시도하면 정상적으로 동작하지 않는다.
- 키 교환이 필요하다면 → ECDH
- 서명(로그인 토큰)이 필요하다면 → ECDSA
- 데이터 암호화가 필요하다면 → AES (키 교환은 RSA나 ECDH로)
3. RSA와 ECDSA 비교
결론부터 말하면, 새로운 프로젝트라면 ECDSA가 낫다.
| 항목 | RSA | ECDSA |
|---|---|---|
| 키 길이 | 2048비트 이상 | 256비트 |
| 속도 | 상대적으로 느림 | 빠름 |
| 용도 | 암호화·서명 모두 가능 | 서명 전용 |
RSA는 키가 커서 JWT에 넣으면 토큰 길이가 매우 길어진다.
모바일 게임 환경에서는 패킷 사이즈 최적화가 중요하다.
보안 강도는 유지하면서 키 크기가 훨씬 작은 ECDSA가 효율적이다.
4. JWT는 서명된 데이터다
JWT는 자체적으로 암호화 기능을 제공하지 않는다.
"서명된 증명서"에 가깝다.
Base64로 인코딩된 문자열이라 디코딩만 하면 내용이 그대로 보인다.
# 디코딩하면 페이로드가 그대로 보인다
echo "eyJ1c2VySWQiOjEyM30" | base64 -d
# 결과: {"userId":123}따라서 JWT 페이로드에는 비밀번호나 주민등록번호 같은 민감한 개인정보를 포함해서는 안 된다.
"아무도 못 보겠지"라고 생각하는 순간, 심각한 개인정보 유출 사고로 이어질 수 있다.
5. JWT의 보안 범위
보안 범위를 명확히 이해해야 취약점을 예방할 수 있다.
JWT가 보장하는 것 (무결성)
- 내용물이 중간에 변조되지 않았다는 것
- 우리 서버가 발급한 토큰이 맞다는 것 (출처)
JWT가 보장하지 않는 것 (기밀성)
- 내용을 남이 보는 것 (전부 보인다)
- 토큰 자체를 훔쳐가는 것 (탈취)
토큰이 탈취되면 서버가 이를 막을 방법은 마땅치 않다.
해커가 해당 토큰으로 정상적인 유저 행세를 할 경우, 서버는 이를 신뢰할 수밖에 없기 때문이다.
그래서 HTTPS가 필수적이며, 만료 시간(Expire Time)을 짧게 설정하여 피해를 최소화해야 한다.
게임 서버라면 여기에 한 가지가 더 붙는다.
서명이 유효하다는 것은 토큰이 위조되지 않았다는 뜻일 뿐, 클라이언트가 보내는 값이 정직하다는 뜻이 아니다.
클라이언트가 "1000 데미지를 줬다"는 값을 보내고 서버가 그대로 승인하는 구조는 취약하고, 클라이언트는 "공격 버튼을 눌렀다"는 입력만 보내고 데미지 계산은 서버가 수행해 적용하는 구조가 안전하다.
패킷 암호화와 JWT 서명만으로는 치트를 막을 수 없다.
서버가 모든 권한을 쥐고, 클라이언트가 보내는 값을 검증 없이 신뢰하지 않아야 한다.
6. 자주 나오는 취약 설정
alg: none 공격
헤더에 {"alg": "none"}을 보내면 서명 검증을 건너뛰는 취약한 설정이 있을 수 있다.
서버 코드에서 ES256 알고리즘만 허용하도록 명시적으로 설정하는 것이 좋다.
취약한 비밀키
HS256(대칭키) 사용 시 비밀키를 secret123 같이 단순하게 설정하면 무차별 대입 공격에 취약해진다.
최소 32바이트 이상의 랜덤 문자열을 생성하여 사용하는 것이 안전하다.
만료 시간 미설정
exp 클레임을 넣지 않으면 그 토큰은 영구적으로 유효한 토큰이 된다.
해커가 취득하게 되면 서비스 종료 시까지 악용될 위험이 있어, 만료 시간을 설정하는 것이 중요하다.
신규 프로젝트에는 ES256(ECDSA), 레거시 호환이 필요하면 RS256(RSA), 내부 서버 간 통신에는 HS256(HMAC)을 기준으로 삼는다.
어느 쪽을 선택하든 JWT에는 민감한 정보를 담지 않고, HTTPS로 통신 구간을 보호하는 것이 최소한의 안전장치다.