NullPointerException은 Java 개발에서 가장 흔하고도 위험한 오류 중 하나입니다. 단순한 예외로 끝나지 않고, 보안 취약점으로 이어질 수 있다는 점에서 실무 개발자와 보안 담당자 모두가 반드시 주의해야 할 항목입니다.
이번 포스팅에서는 널포인터 역참조 취약점의 원인과 발생 구조를 분석하고, 실제 Spring Boot 프로젝트에서 안전하게 대응하는 시큐어코딩 기법을 소개합니다. 취약한 코드 예제부터 안전한 코드 구성, 실무 체크리스트까지 실전 중심으로 정리해드립니다.
1. 널포인터 역참조란 무엇인가?
널포인터 역참조(Null Pointer Dereference)는 null인 객체에 접근하거나 메서드를 호출할 때 발생하는 오류입니다. 이 문제는 단순한 런타임 오류로 보이지만, 사용자 데이터 누락, 인증 우회, 예외 누락에 따른 보안 허점으로 이어질 수 있습니다.
특히 Spring, JPA, JSP 등에서는 의도치 않은 null 객체 전달로 인해 컨트롤러에서 NullPointerException이 발생하고, 서버 오류 페이지가 노출되거나 정상 로직이 동작하지 않아 서비스 장애로 연결될 수 있습니다.
2. 실무에서 자주 발생하는 취약한 코드
아래 예제는 null 여부 검사를 하지 않고 객체의 메서드에 접근하는 전형적인 코드 패턴입니다. 서비스 로직 중 중간에 null이 들어오면 바로 예외가 발생하며, 예외처리 없는 경우 전체 요청이 실패합니다.
// 사용자 이름을 반환하는 간단한 메서드
public class UserService {
public String getUserName(User user) {
return user.getName(); // user가 null이면 예외 발생
}
public void printUserName(User user) {
System.out.println("User name: " + getUserName(user));
}
public void process() {
User user = null; // 외부에서 null 전달된 상황 가정
printUserName(user); // NPE 발생
}
public static void main(String[] args) {
new UserService().process();
}
}
이 코드는 user
객체가 null일 경우, 메서드 호출 자체가 불가능해져 서버 로그에 NullPointerException 예외가 발생합니다. 보안 로그에 민감 정보가 출력될 수도 있으며, 서비스 흐름이 비정상 종료될 수 있습니다.
3. 안전한 코딩 패턴 (Spring 기반 예제)
Java에서는 명확한 null 체크와 Optional 활용을 통해 NullPointerException을 예방할 수 있습니다. 아래 코드는 Optional
을 사용해 null 객체의 접근을 안전하게 처리하며, null일 경우 사용자에게 적절한 기본 메시지를 반환합니다.
// 안전한 null 처리 구조
public class UserService {
public static class User {
private String name;
public User(String name) { this.name = name; }
public String getName() { return name; }
}
public Optional<String> getUserName(User user) {
return Optional.ofNullable(user)
.map(User::getName)
.filter(name -> !name.isBlank());
}
public void printUserName(User user) {
String displayName = getUserName(user)
.orElse("이름 정보 없음");
System.out.println("User name: " + displayName);
}
public void process(User user) {
printUserName(user);
}
public static void main(String[] args) {
User user1 = new User("Alice");
User user2 = null;
User user3 = new User("");
UserService service = new UserService();
service.process(user1);
service.process(user2);
service.process(user3);
}
}
위 코드는 user
가 null인 경우에도 예외를 발생시키지 않고, 기본 메시지 "이름 정보 없음"
을 반환하여 흐름을 유지합니다. 보안상 민감한 예외 메시지를 숨기고, 사용자 경험을 보호하는 시큐어코딩의 대표 사례입니다.
실무에서는 다음의 추가 전략도 고려할 수 있습니다:
- Controller에서 DTO 매핑 시 null-safe 처리
- JPA Entity 조회 시
Optional user = repository.findById(id)
- 서비스 로직에서 assertNotNull 대신 Optional 사용
4. 실무 Q&A
Optional은 null 체크를 더 명확하게 하기 위한 수단입니다. 다만 DTO나 내부 필드에서는 과도하게 사용하면 가독성이 떨어질 수 있으므로, Service나 Controller 레이어의 입력값 처리에 국한해 사용하는 것이 좋습니다.
Spring에서는 @ControllerAdvice
와 ExceptionHandler
를 활용해 NullPointerException이 발생했을 때 통합된 에러 응답을 제공할 수 있습니다. 하지만 근본적으로는 사전에 방지하는 코드 패턴이 가장 중요합니다.
findById()
는 Optional을 반환하기 때문에, orElseThrow()
또는 orElse(null)
로 명시적 분기 처리가 필요합니다. 이 과정을 생략하면 NullPointerException이 언제든지 발생할 수 있습니다.
5. 마무리 요약
NullPointerException은 단순한 버그처럼 보일 수 있지만, 보안상 민감한 정보 유출, 예외 흐름 누락, 서비스 장애로 이어질 수 있는 중대한 이슈입니다. 이 포스팅에서 소개한 Optional 활용, null-safe 분기 처리는 실무 보안 코드 리뷰에서도 반드시 체크되어야 할 항목입니다.
Optional.ofNullable()
활용하여 null 안전성 확보- 필드 초기화 시 기본값 제공
- Service, Controller에서 null 체크 분기 로직 명확화
- JPA 조회 시 Optional 기반 예외 분기 처리
- ExceptionHandler로 전역 예외 응답 처리 구성
'시큐어코딩 > JAVA' 카테고리의 다른 글
[시큐어코딩]Java에서 전자서명 검증 실패가 초래하는 실무 위협과 대응 전략 완벽 가이드 (0) | 2025.04.10 |
---|---|
[시큐어코딩]Java에서 부적절한 세션 종료 보안 이슈와 안전한 처리 방법4 (0) | 2025.04.10 |
[시큐어코딩]Java에서 CSRF(크로스사이트요청위조) 취약점 완벽 대응 가이드 (0) | 2025.04.09 |
[시큐어코딩]Java에서 서버사이드 요청 위조(SSRF) 완벽 대응 가이드 (0) | 2025.04.08 |
[시큐어코딩]Java에서 파일업로드 취약점, 완벽 대응 가이드!! (1) | 2025.04.07 |