1) 부적절한 자원 해제의 위험성
CWE-404는 애플리케이션이 파일 핸들, 네트워크 소켓, 데이터베이스 연결(Connection), 메모리 등의 시스템 자원을 사용한 후, 이를 적절히 닫거나 해제하지 않을 때 발생합니다.
시스템이 가질 수 있는 자원의 수는 한정되어 있습니다. 해제되지 않은 자원이 계속 쌓이면 '자원 누수(Resource Leak)'가 발생하고, 결국 새로운 요청을 처리할 자원이 부족해져 시스템이 느려지거나 갑자기 중단되는 서비스 거부(DoS) 상태에 빠지게 됩니다.
2) 흔히 발생하는 취약한 패턴
주로 예외 상황(Exception)이 발생했을 때 해제 로직을 건너뛰면서 문제가 생깁니다.
에러 발생 시 Close 누락: 데이터를 읽거나 쓰는 도중 에러가 발생하여 catch 블록으로 넘어갈 때, 하단에 있는 close() 코드가 실행되지 않는 경우.
조건문 내 조기 반환(Return): 함수 중간에 특정 조건에 따라 return으로 빠져나가면서, 앞서 열어둔 파일이나 소켓을 닫지 않는 경우.
복잡한 자원 할당: 여러 개의 자원을 연달아 열다가 중간에 실패했을 때, 이미 열린 자원들을 개별적으로 해제하지 않는 경우.
3) 실무적 대응: 자동 해제 매커니즘 활용
자원을 여는 코드와 닫는 코드는 항상 한 쌍으로 움직여야 합니다.
Try-with-resources 사용 (권장): Java 7 이상에서 제공하는 이 구문을 사용하면, try 블록이 끝날 때 자원이 자동으로 해제됩니다. 코드가 간결해지고 실수할 확률이 거의 없습니다.
Finally 블록에서 해제: 구버전 환경이라면 반드시 finally 블록에서 null 체크 후 자원을 해제하여, 에러 발생 여부와 상관없이 닫히도록 보장합니다.
커넥션 풀(Connection Pool) 관리: DB 연결의 경우 직접 닫기보다는 검증된 커넥션 풀 라이브러리를 사용하고 적절한 타임아웃 설정을 적용합니다.
4) CWE-404 대응 및 안전한 자원 관리 자바 코드 예시
[Java]
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.sql.*;
public class ResourceManager {
private static final Logger logger = LoggerFactory.getLogger(ResourceManager.class);
// [CWE-404 조치] Try-with-resources를 사용하여 자동 해제 보장
public void processFileSafe(String filePath) {
// AutoCloseable을 구현한 객체는 블록 종료 시 자동으로 close() 호출
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String content = br.readLine();
logger.debug("Read content: {}", content);
} catch (IOException e) {
logger.error("File processing error: {}", e.getMessage());
}
}
// [CWE-404 조치] Finally 블록을 이용한 명시적 해제 (구버전 방식)
public void executeQuery(String sql) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost/db", "user", "pass");
pstmt = conn.prepareStatement(sql);
pstmt.execute();
} catch (SQLException e) {
logger.error("Database error: {}", e.getMessage());
} finally {
// 역순으로 안전하게 닫기
try { if (pstmt != null) pstmt.close(); } catch (SQLException e) { /* ignore */ }
try { if (conn != null) conn.close(); } catch (SQLException e) { /* ignore */ }
logger.debug("Database resources closed in finally block.");
}
}
}
코멘트: 자원 관리는 소프트웨어의 '뒷정리'와 같습니다. "프로그램이 종료되면 OS가 알아서 회수하겠지"라는 생각은 장시간 가동되는 서버 환경에서는 매우 위험합니다. 특히 반복문 안에서 자원을 생성할 때는 누수가 치명적이므로 반드시 Try-with-resources를 생활화하십시오. logger.debug()를 통해 자원 할당/해제 흐름을 추적하고, 자원 고갈로 인한 서비스 장애를 사전에 방지하는 것이 안정적인 시스템 운영의 핵심입니다.
댓글 달기