3.1 예외처리

개요

예외(Exception)는 소프트웨어 실행 중 발생하는 예기치 않은 오류 상황을 의미합니다. 예외 처리는 이러한 오류 발생 시 프로그램이 비정상적으로 종료되는 것을 막고, 오류로부터 회복하거나 사용자에게 적절한 안내를 제공하는 중요한 메커니즘입니다 (예: `try-catch` 구문).

하지만 예외 처리를 부적절하게 구현하면 보안 문제가 발생할 수 있습니다. 예를 들어, 발생한 예외 정보를(스택 트레이스, 내부 변수 값 등) 사용자에게 그대로 노출하면 공격자에게 시스템 내부 구조나 취약점 파악에 도움이 되는 민감한 정보를 제공하게 됩니다. 또한, 예외를 적절히 처리하지 못하고 프로그램이 비정상적인 상태에 빠지거나 종료되면 서비스 거부(DoS) 상태를 유발할 수 있습니다.

이 기준은 예외 발생 시 시스템 정보를 노출하지 않고, 프로그램의 실행 흐름을 안전하게 제어하며, 오류 상황에서도 시스템의 안정성을 유지하기 위한 예외 처리 보안 요구사항을 정의합니다.

보안 대책

  • - 민감 정보 노출 금지: 사용자에게 보여지는 오류 메시지에는 스택 트레이스, SQL 쿼리문, 내부 경로, 서버 정보, 변수 값 등 민감한 정보를 절대 포함하지 않습니다. 대신 일반적이고 모호한 오류 메시지(예: "요청 처리 중 오류가 발생했습니다. 관리자에게 문의하세요.")를 사용합니다.
  • - 상세 오류 정보 로깅: 실제 오류 원인 파악 및 디버깅에 필요한 상세 정보(스택 트레이스, 관련 변수 값 등)는 사용자 화면이 아닌 서버 측 로그 파일에만 기록합니다. 로그 파일 접근 권한은 최소화하고, 로그에 개인정보 등 민감 정보가 포함되지 않도록 주의합니다.
  • - 적절한 예외 처리 범위 설정: `try-catch` 블록으로 예외 발생 가능성이 있는 코드를 적절히 감싸고, 발생 가능한 구체적인 예외 타입(`Exception` 보다는 `IOException`, `SQLException` 등)을 명시하여 처리합니다.
  • - 자원 해제 보장: 예외 발생 여부와 관계없이 파일 핸들, 데이터베이스 커넥션, 네트워크 소켓 등 사용한 시스템 자원은 반드시 해제되도록 `finally` 블록이나 `try-with-resources`(Java 7 이상), `using`(C#), `with`(Python) 구문을 사용합니다. (참조: 관련 약점 - 부적절한 자원 해제)
  • - 예외 처리 누락 방지: 모든 예외 상황에 대해 적절한 처리 로직(오류 로깅, 사용자 알림, 안전한 상태로 복구 등)을 구현하고, 예외를 무시하거나 단순히 `catch` 블록을 비워두지 않습니다.
  • - 안전한 기본 상태 유지(Fail-Safe): 예외 발생으로 인해 프로그램이 불안정한 상태에 빠지지 않도록, 예외 처리 후에는 미리 정의된 안전한 상태로 복귀하거나 정상적인 종료 절차를 수행합니다.

코드 예시 (Java - 예외 처리)

취약한 코드 (스택 트레이스 노출):

import java.io.*;
import javax.servlet.http.*;

public class VulnerableServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws IOException {
        PrintWriter out = response.getWriter();
        try {
            // ... 파일 처리 등 예외 발생 가능성이 있는 로직 ...
            String filename = request.getParameter("filename");
            FileInputStream fis = new FileInputStream(filename); 
            // ...
        } catch (Exception e) {
            // 발생한 예외의 스택 트레이스를 그대로 사용자 브라우저에 출력 (매우 위험!)
            response.setContentType("text/html");
            out.println("<html><body><h1>An Error Occurred</h1><pre>");
            e.printStackTrace(out); // 민감 정보 노출
            out.println("</pre></body></html>");
        }
    }
}
                        
안전한 코드 (Generic 메시지 + 로깅 + 자원 해제):

import java.io.*;
import javax.servlet.http.*;
import java.util.logging.Level; // Java Util Logging 예시
import java.util.logging.Logger;

public class SafeServlet extends HttpServlet {
    private static final Logger LOGGER = Logger.getLogger(SafeServlet.class.getName());

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws IOException {
        
        String filename = request.getParameter("filename");
        // 파일명 검증 로직 추가 필요 (경로 조작 등 방지)
        
        // try-with-resources 구문으로 FileInputStream 자동 자원 해제 보장
        try (FileInputStream fis = new FileInputStream(filename)) { 
            // ... 정상적인 파일 처리 로직 ...
            response.getWriter().println("File processed successfully.");

        } catch (FileNotFoundException e) {
            // 예상 가능한 특정 예외 처리
            LOGGER.log(Level.WARNING, "File not found: " + filename, e); // 상세 로그 기록
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Requested file not found."); // 사용자에게 일반 오류 메시지 전달 (404)
        
        } catch (IOException e) {
            // 기타 I/O 예외 처리
            LOGGER.log(Level.SEVERE, "IO Error processing file: " + filename, e); // 상세 로그 기록
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An internal server error occurred."); // 사용자에게 일반 오류 메시지 전달 (500)
        
        } catch (Exception e) {
            // 예상치 못한 그 외 모든 예외 처리 (최상위 Exception)
            LOGGER.log(Level.SEVERE, "Unexpected error processing request.", e); // 상세 로그 기록
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An unexpected error occurred."); // 사용자에게 일반 오류 메시지 전달 (500)
        }
    }
}
                        

Python(try...except...finally, logging 모듈), JavaScript(try...catch...finally, console.error/로깅 라이브러리), C#(try-catch-finally, using 문, 로깅 프레임워크), C(오류 코드 반환 및 검사, 자원 해제) 등 다른 언어에서도 유사한 원칙(민감 정보 미노출, 상세 로깅, 자원 해제 보장, 적절한 오류 처리)을 적용하여 예외를 안전하게 처리해야 합니다.