소프트웨어 개발보안 가이드 분석(2021) : 취약한 API 사용

취약한 API는 보안상 금지된 함수이거나, 부주의하게 사용될 가능성이 많은 API를 의미한다.

이들 범주의 API에 대해 확인하지 않고 사용할 때 보안 문제를 발생시킬 수 있다.

금지된 API의 대표적인 예로는 스트링 자료와 관련된 gets(), strcat(),strcpy(),strncat(),strncpy(),sprintf() 등이 있다.

또한 보안상 문제가 없다 하더라도 잘못된 방식으로 함수를 사용할 때도 역시 보안 문제를 발생시킬 수 있다.

소프트웨어 개발보안가이드(2021), 한국인터넷진흥원

https://owasp.org/www-project-api-security/ (출처 : OWASP)

※ API(Application Programming Interface) : 소프트웨어 응용 프로그램이 서로 상호 작용할 수 있도록 허용하는 인터페이스입니다. 이는 라이브러리, 프레임워크, 운영체제와 같은 시스템의 부부를 제어할 수 있는 메서드, 함수, 클래스 및 구조가 될 수 있습니다.

API는 일반적으로 "기능 제공, 추상화, 상호 운용성, 보안 및 권한 관리" 등의 기능을 수행할 수 있습니다. 하지만 이러한 편의성과 반대로 API 공격 피해가 잇달아 발생하자 OWASP는 2019년을 시작으로 OWASP API Security Top 10을 발표하며 개발자들이 API를 개발하고 구현할 때 고려해야 할 보안 위험을 강조하고 있습니다.

 

취약한 API 사용이란?

의미 그대로, 애플리케이션에서 사용되는 API가 보안 취약점을 가지고 있거나, 안전하지 않은 방식으로 사용될 때 공격자는 이러한 틈을 이용해 시스템에 접근하거나 데이터를 탈취하는 보안 문제를 초래합니다.

 

공격 메커니즘

취약한 API 사용으로 인한 공격 메커니즘은 주로 다음과 같습니다.

  1. 인증 우회 : API가 적절한 인증 없이도 접근할 수 있는 경우 발생할 수 있습니다. 공격자는 인증 절차를 우회하고 시스템에 접근하여 민감한 데이터를 탈취하거나 조작할 수 있습니다.
  2. 인가 우회 : API가 사용자의 권한을 충분히 검증하지 않고도 특정 기능 또는 리소스에 접근할 수 있는 경우 발생합니다. 이를 통해 공격자는 권한이 없는 기능에 접근하여 시스템을 악용할 수 있습니다.
  3. 데이터 노출 : API가 민감한 데이터를 적절하게 보호하지 않은 경우 발생합니다. 공격자는 민감한 정보를 노출시키거나 탈취하여 악용할 수 있습니다.
  4. DoS 공격 : 공격자가 API에 과도한 요청을 보내거나 시스템 리소스를 과부하 시키는 경우 발생합니다. 이를 통해 서비스의 가용성을 저하시키거나 시스템을 다운시킬 수 있습니다.

) 2016년 미국의 한 기업에서는 5천만 명 정도의 개인정보 데이터 유출 사건이 있었습니다. 사건이 일어난 원인으로 취약한 API 사용으로 인해 발생한 것으로 알려졌습니다. 공격자는 기업의 개발자용 GitHub 저장소에 접근하여 인증 정보를 획득하고, 이를 통해 기업의 AWS 계정에 접근하여 데이터를 탈취할 수 있었습니다.

 

취약한 웹 애플리케이션의 예

  1     String userInput = "exampleUser'; DROP TABLE users; --";
        
  2     URL url = new URL("http://example.com/api/userinfo?username=" + userInput);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
       
  3     BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        String inputLine;
  4     StringBuffer response = new StringBuffer();
        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
}
  • 1 : 사용자 입력을 변수에 할당합니다.(예시를 위해 사용자 입력 대신 문자열로 지정하였습니다.)
  • 2 : 사용자 입력을 이용해 외부 API에 요청을 보내고 HTTP GET메서드를 사용해 연결합니다.
  • 3 : 외부 API의 응답을 읽기 위해 BufferedReader를 생성합니다.
  • 4 : 외부 API의 응답을 저장합니다.
  • 이 코드는 사용자의 입력을 적절하게 검증하지 않고 외부 API를 호출하는 상황입니다. 사용자의 입력을 URL에 연결하여 외부 API로 전달하기 때문에 입력을 적절하게 검증하지 않으면 인젝션 공격과 같은 보안 취약점이 발생할 수 있습니다. 이러한 상황에서는 안전하게 API를 호출하기 위해 사용자의 입력을 적절히 검증하고, 안전하게 API 호출을 수행해야 합니다.

 

시큐어코딩 적용 방법

    public static void main(String[] args) {
   1    String userInput = "exampleUser";
        
        try {
   2        URL url = new URL("https://example.com/api/userinfo");
   3        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            
   4        String postData = "username=" + URLEncoder.encode(userInput, "UTF-8");
   5        try (OutputStream os = connection.getOutputStream()) {
                byte[] input = postData.getBytes("UTF-8");
                os.write(input, 0, input.length);
            }
    6       try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                String inputLine;
                StringBuffer response = new StringBuffer();
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                System.out.println("API 응답: " + response.toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 1 : 사용자 입력을 변수에 할당합니다.(예시를 위해 문자열로 저장하였습니다.)
  • 2 : 외부 API에 연결하기 위한 URL 객체를 생성합니다.
  • 3 : URL 객체를 이용하여 HTTP POST 메서드를 사용해 설정합니다.
  • 4 : 사용자 입력을 URL 인코딩하여 POST 데이터를 생성합니다.
  • 5 : POST 데이터를 바이트 배열로 변환하고 출력 스트림을 이용해 POST 데이터를 외부 API에 전송합니다.
  • 6 : 입력 스트림을 이용하여 외부 API의 응답을 읽어들입니다.
  • 결과적으로 HTTPS와 POST 요청을 통해 중간에 데이터 변조, 노출을 방지하며 안전하게 외부 API를 호출하였습니다.

위험한 API 함수

  1. 인증 및 권한 부여 관련 함수
    • authenticateUser(username, password): 사용자 인증을 위해 평문으로 사용자 이름과 암호를 전송하며, 암호화되지 않은 상태로 저장되어 있을 수 있습니다.
    • grantAdminPrivileges(user): 적절한 권한 검사 없이 사용자에게 관리자 권한을 부여하는 함수입니다.
  2. 입력 검증 관련 함수:
    • executeSQLQuery(query): 사용자 입력을 그대로 SQL 쿼리에 삽입하여 실행하는 함수로, SQL 삽입 공격에 취약합니다.
    • loadFile(filePath): 사용자로부터 입력받은 파일 경로를 그대로 사용하여 파일을 로드하며, 경로 조작 공격에 취약합니다.
  3. 파일 시스템 관련 함수:
    • writeToFile(data, filePath): 사용자 입력을 그대로 파일에 쓰는 함수로, 경로 조작 공격이 가능합니다.
    • deleteFile(filePath): 적절한 권한 검사 없이 파일을 삭제하는 함수로, 보안 문제를 초래할 수 있습니다.
  4. 네트워크 통신 함수:
    • sendDataToServer(data, serverURL): 암호화되지 않은 데이터를 서버로 전송하는 함수로, 중간자 공격에 취약합니다.
    • downloadFileFromURL(fileURL): 적절한 검증 없이 원격 파일을 다운로드하는 함수로, 악성코드 실행에 이용될 수 있습니다.
  5. 데이터베이스 접근 함수:
    • queryDatabase(query): 사용자 입력을 그대로 SQL 쿼리에 삽입하여 실행하는 함수로, SQL 삽입 공격에 취약합니다.
    • executeStoredProcedure(procedureName, parameters): 적절한 검증 없이 저장 프로시저(Stored Procedure)를 실행하는 함수로, 보안 문제를 초래할 수 있습니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : DNS lookup에 의존한 보안결정