1. Null Pointer 역참조의 위험성
CWE-476은 애플리케이션이 null 값을 가진 객체 참조를 사용하여 속성에 접근하거나 메서드를 호출하려고 할 때 발생합니다. 자바에서는 그 유명한 NullPointerException(NPE)이 발생하며 프로그램이 예외 처리가 되어 있지 않을 경우 즉시 중단됩니다. 공격자는 의도적으로 null을 유발하는 입력값을 주입하여 시스템을 서비스 거부(DoS) 상태로 만들거나, 예외 발생 시 출력되는 스택 트레이스(Stack Trace)를 통해 시스템 내부 구조 정보를 수집할 수 있습니다.
2. 흔히 발생하는 취약한 패턴
주로 외부 시스템과의 연동 결과나 사용자 입력값이 "반드시 존재할 것"이라고 가정할 때 나타납니다.
-
메서드 반환값 미검증: DB 조회 결과나 API 호출 결과가
null일 수 있음에도 즉시.toString()이나.equals()를 호출하는 경우. -
조건문 순서 오류:
if (data.equals("A") && data != null)와 같이null체크보다 사용을 먼저 하는 경우. -
복잡한 객체 그래프 탐색:
user.getAddress().getCity()처럼 중간 단계의 객체가null일 가능성을 배제하고 연쇄적으로 호출하는 경우.
3. 실무적 대응: 방어적 코딩과 옵셔널 활용
가장 확실한 방어법은 "객체는 언제든 null일 수 있다"고 가정하고 코드를 작성하는 것입니다.
-
선 검증 후 사용: 객체를 참조하기 직전에 반드시
null여부를 확인합니다. -
Optional 클래스 도입: Java 8 이상이라면
Optional<T>를 사용하여 값이 없을 수 있음을 명시적으로 표현하고 안전하게 처리합니다. -
Null-Safe 라이브러리 활용: Apache Commons Lang의
StringUtils.equals(a, b)와 같이null입력에도 안전한 유틸리티 메서드를 사용합니다. -
정적 분석 도구 사용: IDE의 경고 설정이나 SonarQube 등을 통해 잠재적인
null참조 지점을 배포 전 찾아냅니다.
4. CWE-476 대응 및 안전한 객체 참조 자바 코드 예시
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
public class NullSafetyManager {
private static final Logger logger = LoggerFactory.getLogger(NullSafetyManager.class);
// [CWE-476 조치] 전통적인 방어적 코딩 방식 (Null Check)
public void processOrder(String orderId) {
// 외부 입력을 받는 orderId가 null인지 먼저 확인
if (orderId == null || orderId.trim().isEmpty()) {
logger.debug("Security Event: Received null or empty order ID. Aborting process.");
return;
}
// 이후 로직 수행
logger.debug("Processing order: {}", orderId.toUpperCase());
}
// [CWE-476 조치] Optional을 활용한 안전한 처리 (Java 8+)
public void updateUserName(User user) {
// user 객체가 null일 가능성에 대비
Optional<User> userOpt = Optional.ofNullable(user);
// 값이 존재할 때만 실행되도록 보장
userOpt.ifPresent(u -> {
String name = u.getName();
if (name != null) {
logger.debug("Updating user name to: {}", name);
// 업데이트 로직...
}
});
if (userOpt.isEmpty()) {
logger.debug("Security Event: Attempted to update a null user object.");
}
}
}
코멘트: "이 값은 절대 null이 아닐 거야"라는 확신이 시큐어 코딩에서는 가장 위험합니다. 특히 클라이언트에서 넘어오는 데이터나 외부 API의 응답은 언제든 비어있을 수 있음을 명시하십시오. logger.debug()를 통해 null 발생 상황을 기록하되, 예외가 발생하여 사용자에게 시스템 내부 정보가 노출되거나 서비스가 멈추지 않도록 꼼꼼한 그물망(검증 로직)을 치는 것이 기본입니다.
댓글 달기