소프트웨어 개발보안 가이드 분석(2021) : 정수형 오버플로우

정수형 오버플로우는 정수형 크기는 고정되어 있는데 저장할 수 있는 범위를 넘어서, 크기보다 큰 값을 저장하려 할 때 실제 저장되는 값이 의도치 않게 아주 작은 수이거나 음수가 되어 프로그램이 예기치 않게 동작될 수 있다. 특히 반복문 제어, 메모리 할당, 메모리 복사 등을 위한 조건으로 사용자가 제공하는 입력값을 사용하고 그 과정에서 정수형 오버플로우가 발생하는 경우 보안상 문제를 유발 할 수 있다.

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

정수형 오버플로우의 공격 메커니즘

정수형 오버플로우는 주로 연산 과정에서 발생합니다. 정수형 데이터 타입의 최댓값을 초과하면 최솟값으로 다시 돌아가게 되는데, 이러한 동작을 공격자가 의도적으로 악용하여 보안 취약점을 유발합니다. 예를 들어, 데이터 값에 대한 범위 검증을 하지 않는 상황에서 양의 정수형 데이터 타입의 최댓값에 1을 더하면 최솟값이 되는데, 이를 악용하여 제어를 획득하거나 임의의 코드를 실행시킬 수 있습니다.

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

다음은 정수형 오버플로우 취약점을 가진 취약한 웹 애플리케이션의 예시입니다.

   protected void doGet(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
  1     int itemId = Integer.parseInt(request.getParameter("item_id"));
  2     int price = getItemPrice(itemId);

  3     int totalPrice = price * Integer.parseInt(request.getParameter("quantity"));
        response.getWriter().write("Total price: " + totalPrice);
   }
  • 1행 : item_id를 매개변수로 읽어와 정수형으로 변환하여 itemID 변수에 저장합니다.
  • 2행 : getItemPrice 메서드를 통해 itemId에 해당하는 가격을 가져와 price에 저장합니다.
  • 3행 : 사용자 입력을 통해 전달받은 quantity와 price를 이용해 totalPrice를 계산합니다.
    • 만일 price에 매우 큰 값을 갖고 있고, 사용자가 임의로 큰 수량(quantity)을 입력한다면, 매우 큰 값을 생성할 수 있습니다.
    • 이렇게 생성된 값이 int 자료형의 표현 범위(-2,147,483,648 ~ 2,147,483,647)를 초과할 경우, 정수형 오버플로우가 발생하여 예기치 않은 값이 totalPrice로 지정될 수 있습니다.
  • 위 코드에서는 사용자가 전달한 아이템의 가격을 계산하여 총 가격을 반환하는 예시입니다. 그러나 이 코드는 사용자가 조작 가능한 수량(quantity)과 가격(price)을 곱한 총 가격을 계산하고 있습니다. 만약 공격자가 음수로 아이템의 가격을 설정하면 정수형 오버플로우가 발생하여 예상치 못한 큰 값이 총 가격으로 계산될 수 있습니다.

시큐어코딩 적용 방법

  • 적절한 자료형 사용
public class SafeIntegerCalculationServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        int itemId = Integer.parseInt(request.getParameter("item_id"));
   1    long quantity = Integer.parseInt(request.getParameter("quantity"));

   2    long price = getItemPrice(itemId);

   3    long totalPrice = price * quantity;

        response.getWriter().write("Total price: " + totalPrice);
    }
}
  • 1~3행 : 기존 int형이 아닌 long을 사용하여 선언합니다.
  • int형의 범위를 초과할 경우 정수형 오버플로우가 발생하기 때문에 int보다 훨씬 큰 범위의 값을 표현할 수 있는 long을 사용함으로써 정수형 오버플로우를 방지할 수 있습니다.

  • 입력값 검증
public class SafeIntegerCalculationServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 요청에서 파라미터 가져오기
        String itemIdStr = request.getParameter("item_id");
        String quantityStr = request.getParameter("quantity");

        // 파라미터가 null인지 확인
  1     if (itemIdStr == null || quantityStr == null) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing parameter(s)");
            return;
        }

        // 파라미터를 정수로 변환
  2     try {
            int itemId = Integer.parseInt(itemIdStr);
            long quantity = Long.parseLong(quantityStr);
        } catch (NumberFormatException e) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid parameter(s)");
            return;
        }

        // 파라미터 범위 유효성 검사
  3     if (itemId <= 0 || quantity <= 0) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid parameter value(s)");
            return;
        }

        // 아이템 가격 조회
        long price = getItemPrice(itemId);
  4     if (price == -1) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Item not found");
            return;
        }

        // 총 가격 계산 (정수형 오버플로우 대응)
        long totalPrice = price * quantity;

        // 결과 반환
        response.getWriter().write("Total price: " + totalPrice);
    }
}
  • 1 : item_id와 quantity 파라미터 중 하나라도 null이 있다면, "Missing parameter(s)"를 출력하여 오류를 클라이언트에게 반환합니다.
  • 2 : 문자열로 변환한 itemIdStr와 quantityStr을 정수형으로 변환하며 만일 숫자가 아닌 문자열이 포함되면 예외 처리를 발생시키고 "Invalid parameter(s)"를 클라이언트에게 반환하여 오류를 알립니다.
  • 3 : 정수값으로 변환된 itemId와 quantity의 값이 0 이하라면, 유효하지 않은 값을 나타내는 "Invalid parameter value(s)"를 클라이언트에게 반환하여 오류를 알립니다.
  • 4 : getItemPrice 메서드에서 itemId에 해당하는 가격을 조회합니다. 만일 getItemPrice에서 -1을 반환하면 이는 아이템을 찾지 못한 것으로 구현되어 price가 -1이라면 "Item not found"를 클라이언트에게 반환하여 오류를 알립니다.
  • 처음 입력값인 item_id와 quantity 파라미터가 null 인지 확인하여 누락을 검사하고, 그 후에 각 파라미터를 정수로 변환하는 과정에서 예외 처리를 통해 유효하지 않은 입력값을 거부하였습니다. 또한, 파라미터 값이 음수인지 확인하여 유효하지 않은 값이 경우에도 오류를 반환하였습니다. 이러한 검증 절차를 통해 사용자의 잘못된 입력값으로부터 안전하게 보호받을 수 있습니다.

정수형 오버플로우를 방지하기 위한 시큐어코딩 적용 방법으로는 다음과 같은 방법들도 있습니다.

  1. 적절한 자료형 사용: 데이터 타입의 범위를 고려하여 적절한 자료형을 사용합니다. 예를 들어, 큰 값의 연산이 예상되는 경우에는 long 또는 BigInteger와 같은 적절한 자료형을 사용합니다.
  2. 입력값 검증: 사용자 입력값을 적절히 검증하여 오버플로우를 일으킬 수 있는 값을 필터링하거나 제한합니다.
  3. 산술 연산 전 검사: 연산을 수행하기 전에 결과가 유효한 범위 내에 있는지 검사하여 오버플로우를 방지합니다.
  4. 오버플로우 대응 로직: 오버플로우가 발생할 경우 적절한 대응 로직을 수행하여 시스템의 무결성을 유지합니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : HTTP 응답 분할
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : 보안기능 결정에 사용되는 부적절한 입력값