본문 바로가기
우아한 코딩

외부 프로세스 실행 시 보안 취약점 및 대응방안

by 피크인사이트 2024. 2. 20.
반응형

외부 프로세스 실행 시 보안 취약점은 시스템의 안전성을 위협할 수 있는 중요한 문제입니다.

외부 프로세스는 사용자의 입력을 받거나 시스템 자원에 접근하여 작업을 수행하므로, 이를 악용하면 보안 문제가 발생할 수 있습니다.

이에 대응하기 위해서는 적절한 보안 대책을 마련해야 합니다.

이 글에서는 외부 프로세스 실행 시 발생할 수 있는 주요 보안 취약점과 이를 해결하기 위한 대응책에 대해 알아보겠습니다.

외부 프로세스 실행 시 보안 취약점 및 대응방안


1. 명령어 삽입 공격

[개요]

명령 주입(Command Injection)은 악의적인 사용자가 원래의 프로그램 로직을 우회하여 임의의 시스템 명령을 실행하는 공격 방법입니다.

사용자 입력을 검증하지 않고 그대로 시스템 명령의 일부로 사용할 때 발생하는데,

예를 들면 아래와 같은 코드는 명령 주입 공격에 취약합니다.

String userSuppliedFileName = ...; // 사용자로부터 제공된 파일 이름 
Runtime.getRuntime().exec("/usr/bin/ls " + userSuppliedFileName);

위 코드에서 사용자가 파일 이름 대신 "; rm -rf /"와 같은 문자열을 입력하면,

실제로 실행되는 명령은 "/usr/bin/ls ; rm -rf /"가 됩니다.

이렇게 되면 사용자는 임의의 시스템 명령을 실행할 수 있게 되며, 아울러 공격자가 악성 코드를 주입할 수 있습니다.

이로 인해 공격자는 파일 시스템 접근, 권한 상승, 백도어 설치, 데이터 손상 등을 일으킬 수 있고, 궁극적으로 서버를 완전히 장악할 수 있습니다.

 

[대응방안]

코드 인젝션 공격을 방지하기 위한 주요한 대책은 다음과 같습니다.

 

첫째, 모든 사용자 입력에 대한 유효성 검증 및 필터링을 실시합니다.

특수문자, 스크립트 코드 등이 포함되어 있는지 검사합니다.

 

둘째, 사용자 입력을 직접 셀이나 OS 명령어로 보내지 않도록 합니다.

사용자 입력은 애플리케이션 레벨에서 처리하고, 필요시 whitelist를 사용하여 유효한 명령어인지 검증합니다.

 

셋째, 외부 프로세스 실행 시 최소 권한의 사용자 계정을 통해 실행합니다.

 

넷째, 외부 프로세스 실행 전후에 파일 체크섬 검증 등을 통해 무결성을 확인하는 절차를 추가합니다.

 

다섯째, 취약점 분석, 코드 리뷰, 테스트를 지속적으로 하여 취약한 부분을 보완합니다.


2. 경로 조작 공격(Path Traversal)

[개요]

경로 조작 공격(Path Traversal)은 악의적인 사용자가 파일 시스템 경로를 조작하여 시스템의 루트 디렉터리 이외의 파일에 접근하는 공격입니다.

일반적으로 외부 프로세스 실행 시 사용자로부터 입력된 파일 경로를 사용하는 경우에 발생할 수 있습니다.

악의적인 사용자가 입력된 파일 경로에 상대 경로나 특수 문자를 포함하여 파일 시스템 경로를 조작하여 민감한 파일에 접근하거나 수정할 수 있습니다.

예를 들어 아래 코드에서는 사용자로부터 입력받은 fileName을 그대로 directory와 합쳐 파일을 읽어오고 있습니다.

이때, 사용자가 상대 경로나 특수 문자를 입력하여 파일 경로를 조작할 경우, 의도하지 않은 파일에 접근할 수 있습니다.

예를 들어, 사용자가 ../secret.txt와 같은 입력을 제공하면 실제로는 읽어오면 안 되는 파일에 접근할 수 있습니다.

import java.io.*;
public class PathTraversalExample {
    public static void main(String[] args) {
        // 사용자 입력으로부터 파일명을 받음
        String fileName = args[0];
        String directory = "/path/to/files/"; // 실제 파일이 저장된 디렉토리
        // 사용자 입력을 파일 경로로 사용하여 파일을 읽어옴
        try {
            BufferedReader reader = new BufferedReader(new FileReader(directory + fileName));
            String lines;
            while ((lines = reader.readLine()) != null) {
                System.out.println(lines); // 결과
            } 
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

[대책]

첫 번째, 입력된 파일 경로를 절대 경로로 변환하여 사용합니다.

절대 경로는 루트 디렉터리부터 시작하여 파일을 찾기 때문에 상대 경로를 이용한 경로 조작을 방지할 수 있습니다.

 

두 번째, 사용자로부터 입력된 파일 경로를 검증하여 유효한 파일 경로인지 확인합니다.

입력 값이 예상되는 형식에 맞지 않는 경우 거부하거나 적절한 오류 메시지를 반환합니다.

 

세 번째, 파일 시스템에서 접근이 허용되는 디렉터리를 제한하고, 실행 중인 프로세스의 권한을 최소한으로 제한하여 파일 시스템에 대한 접근을 제한합니다.

 

네 번째, 파일 시스템 접근을 제어하기 위해서 액세스제어목록(ACL)이나 파일 시스템 권한을 설정하여 민감한 파일에 대한 접근을 제한합니다. 마지막으로 사용되는 소프트웨어나 라이브러리의 보안업데이트를 자주 수행하여 최신 보안 패치를 적용하여 공격자의 경로조작 공격을 예방할 수 있습니다.


3. 권한 상승 공격(Privilege Escalation)

[개요]

권한 상승 공격은 공격자가 외부 프로세스 실행 기능을 악용하여 시스템 권한을 상승시키는 공격입니다.

공격자는 취약한 외부 프로세스 실행 코드를 통해 시스템 권한이 높은 사용자 혹은 시스템 자체의 권한으로 임의의 명령을 실행합니다.

<공격 시나리오>

공격 과정은 먼저 공격자가 악의적인 명령을 포함하는 입력값을 전달하여 시스템 권한이 높은 사용자 혹은 시스템 자체의 권한으로 실행합니다.

그리고 시스템 파일을 조작하여 시스템 설정을 변경하거나 백도어를 설치하여 민감 정보를 유출시키게 됩니다.

 

다음은 공격 예시 코드입니다.

// 안전하지 않은 코드
String command = request.getParameter("command");
Runtime.getRuntime().exec(command);

위 코드는 사용자 입력값 command을 직접 명령어로 실행합니다.

공격자가 command에 rm -rf /와 같은 값을 입력하면 시스템 내의 모든 파일을 삭제할 수 있게됩니다.

[대책]

첫 번째, 사용자 입력값을 철저하게 검증하여 허용되지 않은 명령어를 제거합니다.

두 번째, 최소 권한 부여: 외부 프로세스 실행에 필요한 최소한의 권한만 부여하여 공격자가 시스템을 악용할 수 있는 범위를 제한합니다

세 번째, 실행 가능한 명령어 목록을 정의하고, 해당 목록에 포함되지 않은 명령어는 실행하지 않도록 합니다.

네 번째, 외부 프로세서를 샌드박스 환경에서 실행하여 시스템에 영향을 줄 수 있는 공격을 방지합니다.

마지막으로 사용하는 라이브러리를 최신 버전으로 유지하여 알려진 취약점을 개선할 수 있습니다.


4. 부적절한 리소스 해제

[개요]

부적절한 자원 해제(Improper Resource Release)는 프로그램에서 사용한 자원을 올바르게 해제하지 않아 발생하는 보안 취약점 중 하나입니다.

이 취약점은 자원을 더 이상 사용하지 않는 프로그램이 해당 자원을 해제하지 않고 남겨둠으로써 발생할 수 있습니다.

주로 파일, 네트워크 연결, 데이터베이스 연결, 메모리 등의 자원을 관리할 때 발생할 수 있습니다.

// 안전하지 않은 코드
Connection connection = DriverManager.getConnection(...);
// ...
// connection 객체 해제 누락

위 코드는 connection 객체를 사용한 후 해제하지 않습니다.

이 경우 공격자가 connection 객체를 악용하여 데이터베이스에 접근하거나 데이터를 조작할 수 있습니다.

따라서 다음과 같이 connection 객체를 명확하게 해제하는 것이 중요합니다.

try (Connection connection = DriverManager.getConnection(...)) {
  // ...
} catch (SQLException e) {
  e.printStackTrace(); //
}

 

위 코드는 try-with-resources 블록을 사용하여 connection 객체를 자동으로 해제합니다.

이렇게 하면 코드 블록을 벗어날 때 자동으로 connection 객체가 해제되어 메모리 누수 및 공격 위험을 방지할 수 있습니다.

[대책]

첫 번째, Try-with-Resources를 사용합니다

Java에서는 AutoCloseable 인터페이스를 구현한 클래스를 try-with-resources 구문으로 자원을 안전하게 해제할 수 있습니다. 이를 통해 예외 발생 여부에 관계없이 자원이 안전하게 해제됩니다.

두 번째, 자원을 안전하게 해제하기 위해 finally 블록을 사용하여 자원 해제 코드를 작성할 수 있습니다.

따라서 try-catch-finally 구문 사용으로 예외 발생 여부에 관계없이 자원을 해제할 수 있습니다.

세 번째, 사용한 자원은 적절한 시점에 해제해야 합니다.

자원 사용이 끝난 후 즉시 자원을 해제하도록 프로그래밍하는 것이 중요합니다.

네 번째, 정적분석도구를 이용하여 코드를 검사하고 부적절한 자원 해제와 관련된 취약점을 식별할 수 있습니다.


5. 입력 값 검증 취약

[개요]

입력 값 검증 취약점은 프로그램이 사용자 입력값을 검증하지 않아 발생하는 취약점입니다.

공격자는 이 취약점을 악용하여 프로그램에 악의적인 코드를 삽입하거나 예상치 못한 동작을 유발하여 공격할 수 있습니다.

공격자는 이 취약점을 이용하여 SQL 삽입공격, 스크립트 삽입공격, 크로스사이트스크립팅(XSS) 공격 및 명령어 삽입 공격 등을 할 수 있습니다.

 

// 안전하지 않은 코드
String nm = request.getParameter("name");
// name 변수를 직접 SQL 쿼리에 사용
String SQLquery = "SELECT * FROM users WHERE name = '" + nm + "'";

위 코드는 사용자 입력 값 nm을 직접 SQL 쿼리에 사용합니다.

공격자가 nm 변수에 '; DROP TABLE users;와 같은 값을 입력하면 데이터베이스의 users 테이블을 삭제할 수 있습니다. 다음과 같이 nm 변수를 검증하고 SQL 쿼리에 사용해야 합니다.

String nm = request.getParameter("name");
// nm 변수 검증
if (nm.matches("[a-zA-Z0-9]+")) {
  // nm 변수가 유효한 경우
String query = "SELECT * FROM users WHERE name = '" + nm + "'";
// ...
} else {
  // nm 변수가 유효하지 않은 경우
  // 적절한 에러 처리 구문 …
}

 

[대책]

첫 번째, 사용자 입력 값을 철저하게 검증하여 허용되지 않은 문자나 코드를 제거합니다.

두 번째, 입력 값 인코딩합니다.

사용자 입력 값을 출력하기 전에 HTML 엔터티 인코딩과 같은 방식으로 인코딩하여 악의적인 코드가 실행되지 않도록 합니다.

세 번째, 사용자 입력 값을 사용하지 않는 코드 작성

가능한 경우 사용자 입력 값을 사용하지 않고 프로그램을 작성합니다.

네 번째 프로그램에 필요한 최소한의 권한만을 부여하여 공격자가 시스템을 악용할 수 있는 범위를 제한합니다.


 

지금까지 외부 프로세스 실행 시 보안 취약점과 대응방안에 대해 살펴보았습니다.

간략히 정리하면 보안 취약점은 프로그램이나 시스템에서 발생할 수 있는 보안 위협으로, 부적절한 자원 해제, 경로 조작 공격, 권한 상승 공격 등 다양한 형태로 나타납니다.

이를 예방하기 위해선 적절한 자원 관리, 입력 검증, 보안 업데이트 등의 대책이 필요합니다.

정적 분석 도구를 활용하고 최신 보안 패치를 적용하여 프로그램을 안전하게 유지하는 것이 중요합니다.

 

프로그래머는 보안 취약점에 대해 항상 인식하고, 적절한 보안 대책으로 안전한 시스템을 구축하여, 사용자 정보와 시스템 자산을 안전하게 보호해야 할 것입니다.

반응형