Java에서 String과 char[] 배열은 모두 텍스트 데이터를 저장하는 데 사용되지만, 민감한 정보를 다룰 때는 어느 쪽을 선택하느냐가 매우 중요한 문제가 됩니다. 비밀번호, 주민등록번호(SSN) 같은 보안 정보를 저장할 때 char[] 배열이 선호되는 이유는 바로 String의 불변성(Immutability)에서 찾을 수 있습니다.
char[] 배열이 더 안전한 이유
- 실수로 인한 노출 위험이 적음: 일반 String을 사용하면 로그 파일이나 기타 안전하지 않은 곳에 비밀번호가 실수로 출력될 가능성이 훨씬 높습니다. 반면 char[] 배열은 이러한 우발적인 노출에 상대적으로 덜 취약합니다.
- 불변성으로 인해 내용 변경 불가: String은 불변(immutable) 객체이므로 한번 생성된 문자열의 내용을 변경하거나 덮어쓸 수 있는 메서드가 존재하지 않습니다. 이러한 특성 때문에 String 객체는 비밀번호 같은 보안 정보를 저장하기에 부적합합니다. 따라서 보안 정보는 항상 char[] 배열에 저장해야 합니다.
- 메모리에 오래 잔존하는 문제: String이 불변이기 때문에 비밀번호를 평문으로 저장하면 가비지 컬렉터(GC)가 정리할 때까지 메모리에 그대로 남아 있게 됩니다. 게다가 String은 문자열 재사용을 위해 문자열 상수 풀(String Constant Pool, SCP)을 사용하므로 메모리에 훨씬 오랫동안 유지될 가능성이 큽니다. 메모리 덤프에 접근할 수 있는 사람이라면 누구나 평문 비밀번호를 쉽게 찾아낼 수 있으며, 이것이 평문이 아닌 암호화된 비밀번호를 사용해야 하는 또 다른 이유입니다.
- Java 자체의 권장 방식: Java Swing 애플리케이션의 JPasswordField를 살펴보면, char[]를 반환하는
getPassword()메서드가 제공되고, 평문을 반환하던getText()메서드는 이미 deprecated(사용 중단 권고) 처리되었습니다. 즉, Java 표준 API 자체가 char[] 사용을 권장하고 있는 것입니다. - 사용 후 삭제(sanitization) 가능: char[] 배열은 사용이 끝난 후 값을 의미 없는 값(junk)이나 0으로 덮어써서 흔적을 지울 수 있습니다. 하지만 불변인 String은 한번 생성된 값을 지우거나 덮어쓰는 것이 불가능합니다.
예제 코드
public class SecureInfoData {
public static void main(String args[]) {
String pwd = "string_pass_word";
System.out.println("String Password is: " + pwd);
char charPwd[] = "char_pass_word".toCharArray();
System.out.println("Character Password is: " + charPwd);
}
}
실행 결과
String Password is: string_pass_word Character Password is: [C@6d06d69c
위 실행 결과에서 String 비밀번호는 평문 그대로 출력된 반면, char[] 배열을 문자열과 연결해 출력하면 [C@6d06d69c처럼 배열의 타입과 해시 코드만 표시됩니다. 이는 문자열 연결 연산 시 배열의 toString()이 호출되기 때문입니다. 참고로 System.out.println(charPwd)처럼 배열을 단독으로 출력하면 실제 문자가 그대로 출력되므로, 디버깅 시에도 주의가 필요합니다.
정리
요약하면, String은 불변성과 문자열 상수 풀 때문에 민감한 데이터가 메모리에 오랫동안 남고 명시적으로 삭제할 수 없는 반면, char[] 배열은 사용 후 값을 덮어써서 흔적을 제거할 수 있습니다. 따라서 비밀번호, 카드 번호, 주민등록번호 등 민감한 정보를 다룰 때는 String 대신 char[] 배열을 사용하고, 사용이 끝나면 즉시 0이나 의미 없는 값으로 채워 넣는 것이 안전한 개발 습관입니다.