2-4. 에러 처리 (Error Handling)

에러 발생 시 부적절한 처리로 인해 발생하는 취약점을 **Java**와 **Python** 코드를 통해 학습합니다.


1. 널 포인터 역참조 (Null Pointer Dereference)

객체가 null인지 확인하지 않고 접근하여 프로그램 비정상 종료나 서비스 거부(DoS)를 유발합니다.

Java - 취약한 코드NullPointerException
/* 취약한 코드 (Java) */
String user = getUserName(id); // user가 null을 반환할 수 있음
// ... (생략)

// user 변수가 null인지 검사하지 않음
if (user.length() > 0) { 
    // user가 null일 경우 NullPointerException 발생
    System.out.println("User exists.");
} 
Java - 안전한 코드Fix: Null 검사
/* 안전한 코드 (Java) */
String user = getUserName(id);

// 1. null인지 명시적으로 확인
if (user != null && user.length() > 0) {
    System.out.println("User exists.");
}

// 2. Optional 사용 (Java 8+)
Optional.ofNullable(user)
        .ifPresent(u -> System.out.println("User exists."));
Python - 취약한 코드AttributeError
# 취약한 코드 (Python)
def get_user_data(user_id):
    # user_data가 None을 반환할 수 있음
    return None 

user_data = get_user_data(101)

# None 객체에 .keys() 메서드 호출 시도
if user_data.keys(): 
    print("Data found.")
# AttributeError: 'NoneType' object has no attribute 'keys' 발생
Python - 안전한 코드Fix: None 검사
# 안전한 코드 (Python)
user_data = get_user_data(101)

# 1. is not None으로 명시적 확인
if user_data is not None and user_data:
    if 'name' in user_data.keys():
        print("Data is valid.")

# 2. Try-Except 문으로 예외 처리
try:
    if user_data.keys():
        print("Data found.")
except AttributeError:
    print("User data is missing or invalid.")

2. 부적절한 자원 해제 (Improper Resource Release)

열린 파일, DB 연결, 소켓 등의 자원을 명시적으로 닫지 않아 자원 누수 및 DoS를 유발합니다.

Java - 취약한 코드자원 누수
/* 취약한 코드 (Java) */
FileInputStream fis = null;
try {
    fis = new FileInputStream("data.txt");
    // ... 파일 처리 중 예외 발생 가능
    if (fis.read() == -1) { 
        return; // 여기서 함수가 종료되면서 close() 실행 안 됨
    }
} catch (IOException e) {
    e.printStackTrace();
} 
// finally 블록이 없거나, return 등으로 close()가 누락됨
// fis.close() 누락
Java - 안전한 코드Fix: Try-with-Resources
/* 안전한 코드 (Java) */
// Try-with-Resources 사용 (AutoCloseable 구현 객체)
try (FileInputStream fis = new FileInputStream("data.txt")) {
    int data = fis.read();
    // ... 파일 처리 로직
    System.out.println(data);
} catch (IOException e) {
    e.printStackTrace();
}
// try 블록을 벗어나면 fis.close()가 자동으로 호출됨
Python - 취약한 코드자원 누수
# 취약한 코드 (Python)
f = open('config.log', 'r')
data = f.read()

if 'error' in data:
    return data # 여기서 함수 종료. f.close() 누락

# f.close()를 명시적으로 호출하지 않음
# 예외 발생 시에도 close()가 실행되지 않음
Python - 안전한 코드Fix: With 문
# 안전한 코드 (Python)
try:
    # with 문 사용 (Context Manager)
    with open('config.log', 'r') as f:
        data = f.read()
        
        if 'error' in data:
            return data
            
    # with 블록을 벗어나는 순간 f.close()가 자동 호출됨
except FileNotFoundError:
    print("File not found.")

3. 오류 메시지를 통한 정보 노출

개발 환경의 상세한 오류 메시지(스택 트레이스 등)를 사용자에게 그대로 노출하여 시스템 정보를 유출합니다.

Java - 취약한 코드정보 노출
/* 취약한 코드 (Java) */
try {
    // DB 연결 실패 또는 SQL 구문 오류 발생
    connection.createStatement(); 
} catch (Exception e) {
    // 스택 트레이스를 응답에 포함하거나 클라이언트에 출력
    response.getWriter().write(e.toString()); // 상세 예외 정보 클라이언트 전달
    e.printStackTrace(); // 서버 로그에 기록하고 클라이언트에 500 에러 전달
}
// DB 스키마, 파일 경로, 서버 내부 구조 등 상세 정보 노출
Java - 안전한 코드Fix: 일반화된 오류
/* 안전한 코드 (Java) */
try {
    connection.createStatement();
} catch (SQLException e) {
    // 1. 상세 내용은 서버 로그에만 기록 (개발자용)
    log.error("Database error occurred: ", e);
    
    // 2. 클라이언트에게는 일반적인 메시지만 전달
    response.sendError(500, "서비스 처리 중 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.");
}
Python - 취약한 코드정보 노출
# 취약한 코드 (Python - Flask/Django 예시)
from flask import Flask, jsonify

@app.route('/data')
def get_data():
    try:
        result = 10 / 0 # ZeroDivisionError 발생
    except Exception as e:
        # e.args를 통해 상세 에러 메시지 직접 반환
        return jsonify({'error': str(e)}), 500
# "division by zero"와 같은 내부 예외 메시지 노출
Python - 안전한 코드Fix: 일반화된 오류
# 안전한 코드 (Python - Flask/Django 예시)
import logging
log = logging.getLogger(__name__)

@app.route('/data')
def get_data():
    try:
        result = 10 / 0 
    except Exception as e:
        # 1. 상세 내용은 서버 로그에 기록
        log.error("데이터 처리 중 오류 발생", exc_info=True)
        
        # 2. 사용자에게는 일반 메시지 반환
        return jsonify({'error': 'Internal Server Error'}), 500

4. 예외 처리 미흡 (Inadequate Exception Handling)

광범위한 예외(`Exception` 또는 `BaseException`)를 잡고 로그를 남기지 않거나, 적절한 후속 조치 없이 에러를 무시합니다(Swallowing Error).

Java - 취약한 코드Swallowing Error
/* 취약한 코드 (Java) */
try {
    // 중요한 로직 (예: 파일 삭제)
    fileManager.deleteFile(userFile);
} catch (Exception e) { 
    // 모든 예외를 잡고 아무런 조치도 취하지 않음 (Log도 없음)
    // 공격자가 에러 발생을 유도해도 아무도 모름
}
// 오류가 발생했지만 프로그램은 정상적으로 계속 실행
Java - 안전한 코드Fix: 명시적 Catch 및 Log
/* 안전한 코드 (Java) */
try {
    fileManager.deleteFile(userFile);
} catch (SecurityException e) { 
    // 1. 필요한 특정 예외만 잡음
    log.warn("파일 삭제 권한 없음: ", e);
    // 2. 적절한 예외 처리 로직 (재시도, 사용자에게 오류 메시지 전달 등)
} catch (IOException e) {
    log.error("파일 I/O 오류 발생: ", e);
    throw new RuntimeException("파일 삭제 실패", e); // 3. 오류 전파
}
Python - 취약한 코드Swallowing Error
# 취약한 코드 (Python)
try:
    # 중요한 계산 로직
    result = calculate_critical_value(data)
except: 
    # 예외의 종류와 상관없이 모두 무시하고 통과
    pass 
    
return default_value # 계산 오류에도 불구하고 기본값 반환
Python - 안전한 코드Fix: 명시적 Catch 및 Log
# 안전한 코드 (Python)
import logging
log = logging.getLogger(__name__)

try:
    result = calculate_critical_value(data)
except ZeroDivisionError as e: 
    # 1. 특정 예외만 잡고 로그 기록
    log.warning(f"계산 중 0 나누기 오류 발생: {e}")
    result = 0 # 2. 안전한 기본값 설정
except Exception as e:
    # 3. 예상치 못한 예외는 상세 로그 기록 후 재전파
    log.error("알 수 없는 오류 발생", exc_info=True)
    raise 

5. 오류 처리 누락 (Missing Error Handling)

잠재적으로 예외가 발생할 수 있는 함수나 시스템 호출에 대해 예외 처리(`try-catch`/`try-except`)를 전혀 하지 않아 서비스가 중단됩니다.

Java - 취약한 코드서비스 중단
/* 취약한 코드 (Java) */
// 파일 읽기/쓰기는 IOException 발생 가능
public String readConfig(String path) {
    // new FileReader는 FileNotFoundException을 throw함에도 처리하지 않음
    FileReader fr = new FileReader(path); 
    BufferedReader br = new BufferedReader(fr);
    return br.readLine(); 
}
// main 함수에서 호출 시, 예외 처리 없으면 컴파일 오류 또는 런타임 종료
Java - 안전한 코드Fix: 예외 전파/처리
/* 안전한 코드 (Java) */
// 1. 함수 레벨에서 예외를 호출자로 전파
public String readConfigSecure(String path) throws IOException {
    try (FileReader fr = new FileReader(path);
         BufferedReader br = new BufferedReader(fr)) {
        return br.readLine();
    }
}
// 2. 호출자는 try-catch로 예외를 반드시 처리해야 함
Python - 취약한 코드서비스 중단
# 취약한 코드 (Python)
def load_settings(filename):
    # 파일이 없을 경우 FileNotFoundError 발생 가능
    with open(filename, 'r') as f: 
        return json.load(f)

# 파일이 없는 상태에서 함수 호출 시
# 프로그램이 FileNotFoundError로 비정상 종료됨
Python - 안전한 코드Fix: 기본값 제공
# 안전한 코드 (Python)
import json

def load_settings_secure(filename):
    try:
        with open(filename, 'r') as f: 
            return json.load(f)
    except FileNotFoundError:
        # 파일이 없을 경우 안전한 기본 설정 반환 (오류 무시가 아님)
        print(f"설정 파일 {filename}을 찾을 수 없습니다. 기본 설정을 사용합니다.")
        return {"default_key": "default_value"}
    except json.JSONDecodeError:
        # 파일 손상 시 적절한 처리
        print("설정 파일 형식이 잘못되었습니다.")
        return {"default_key": "default_value"}