1. Private 배열에 Public 데이터 할당의 위험성
CWE-496은 외부에서 전달된 배열이나 가변 객체(Mutable Object)를 클래스 내부의 private 배열 필드에 직접 할당할 때 발생합니다. 자바의 배열은 참조 타입이기 때문에, 외부 사용자가 생성한 배열을 그대로 필드에 저장하면 클래스 외부와 내부가 동일한 메모리 주소를 공유하게 됩니다.
결과적으로 외부 호출자가 클래스 밖에서 해당 배열의 값을 수정하면, 클래스 내부의 private 필드 값도 함께 변하게 됩니다. 이는 객체의 독립성을 훼손하며, 공격자가 시스템 설정이나 권한 목록을 실시간으로 조작할 수 있는 통로를 열어주는 격이 됩니다.
2. 흔히 발생하는 취약한 패턴: "잘못된 생성자와 Setter"
주로 생성자나 설정(Setter) 메서드를 통해 데이터를 주입받을 때 실수하기 쉽습니다.
-
생성자 직접 할당:
public User(String[] roles) { this.roles = roles; }와 같이 전달받은 배열의 주소값을 그대로 저장하는 경우. -
Setter 직접 할당: 외부에서 받은 설정 배열을 검증이나 복사 없이 필드에 대입하는 경우.
-
가변 객체 공유: 배열뿐만 아니라
Date,List,Map등 수정 가능한 객체를 직접 할당하는 행위 전반.
3. 실무적 대응: 입력값의 방어적 복사(Defensive Copy)
외부 데이터를 내부로 받아들일 때는 원본과의 연결 고리를 완전히 끊어야 합니다.
-
방어적 복사 수행: 전달받은 배열을 내부 필드에 저장하기 전,
clone()메서드나System.arraycopy()를 사용하여 새로운 배열 객체를 생성하고 데이터 내용만 복사합니다. -
유효성 검사 후 복사: 복사본을 만든 후에 해당 데이터가 시스템 정책에 맞는지 검증합니다. (복사 전 검증하면 검증과 할당 사이에 데이터가 변할 수 있는 TOCTOU 위험이 있습니다.)
-
불변 컬렉션 활용: 데이터를 저장할 때 수정이 불가능한 형태로 변환하여 저장하는 것을 고려합니다.
4. CWE-496 대응 및 안전한 데이터 할당 자바 코드 예시
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
public class SecurityPolicyManager {
private static final Logger logger = LoggerFactory.getLogger(SecurityPolicyManager.class);
// 내부에서 보호되어야 할 보안 정책 배열
private String[] allowedIPs;
/* [위험한 코드] 외부 배열의 주소를 그대로 할당함 (CWE-496)
public void setAllowedIPs(String[] ips) {
this.allowedIPs = ips;
}
*/
// [CWE-496 조치] 입력받은 배열을 방어적으로 복사하여 할당
public void setAllowedIPs(String[] ips) {
if (ips == null) {
this.allowedIPs = new String[0];
return;
}
// 1. 방어적 복사 수행: 외부 원본 배열과 내부 필드의 연결을 끊음
this.allowedIPs = ips.clone();
logger.debug("Allowed IPs updated securely with defensive copy.");
}
public void printConfig() {
logger.debug("Current Allowed IPs: {}", Arrays.toString(allowedIPs));
}
}
코멘트: 클래스의 private 키워드는 외부에서 '직접' 접근하는 것만 막아줄 뿐, '참조'를 통한 간접 수정을 막아주지는 못합니다. 외부에서 온 데이터는 절대 그대로 믿고 내부에 들여놓아서는 안 됩니다. logger.debug()를 통해 설정 변경 이력을 기록하되, "들어올 때도 복사, 나갈 때도 복사"라는 방어적 복사의 원칙을 지키는 것이 가장 완벽한 캡슐화의 완성입니다.
댓글 달기