RETROSPECTIVE · 회고
Base64 인코딩 결과에 줄바꿈이 끼어 본인인증이 실패한 이유
Java
본인인증 모듈을 연동하면서 요청 데이터를 암호화한 뒤 Base64로 인코딩해 넘기는 부분이 있었다. 로컬에서는 잘 되는데 특정 서버에서만 "데이터 형식 오류"가 났다. 인코딩 결과를 로그로 찍어보니 문자열 중간중간에 \r\n이 끼어 있었다.
원인: 같은 메서드, 다른 동작
Apache Commons Codec의 Base64.encodeBase64String()은 1.4 버전에서는 76자마다 CRLF를 넣는 MIME 형식(chunked) 으로 결과를 만들고, 1.5부터 줄바꿈 없는 결과로 바뀌었다. 코드에서는 최신 버전을 쓴다고 생각했지만, 서버 클래스패스에는 다른 라이브러리가 끌고 온 구버전 codec이 먼저 올라가 있었다.
- 레거시 코드에서 자주 보이는
sun.misc.BASE64Encoder도 76자마다 줄바꿈을 넣는다. - 받는 쪽이 줄바꿈을 허용하지 않는 디코더면 그대로 실패한다.
실제로 로딩된 jar 확인하기
버전이 의심될 때는 추측하지 말고 런타임에 어떤 jar에서 클래스가 올라왔는지 찍어보는 게 빠르다.
System.out.println(
org.apache.commons.codec.binary.Base64.class
.getProtectionDomain().getCodeSource().getLocation());
// 빌드 도구에서 의존성 경로 확인
// mvn dependency:tree -Dincludes=commons-codec
해결
- Java 8 이상이면 표준
java.util.Base64.getEncoder()를 쓴다. 줄바꿈이 들어가지 않고, MIME 형식이 필요할 때만getMimeEncoder()를 명시적으로 고른다. - codec을 계속 써야 한다면
Base64.encodeBase64(bytes, false)처럼 chunked 여부를 인자로 명시한다. - URL 파라미터로 넘기는 값이면
+,/,=때문에 URL-safe 인코딩이나 URL 인코딩도 함께 확인한다.
String encoded = java.util.Base64.getEncoder().encodeToString(cipherBytes);
배운 점
라이브러리 메서드 이름이 같아도 버전에 따라 계약이 다를 수 있다. 외부 시스템에 넘기는 값은 "길이, 허용 문자" 정도라도 로그에서 한 번 검증해두면 이런 문제를 훨씬 빨리 찾는다.