소프트웨어 개발보안 가이드 분석(2021) : 부적절한 예외 처리

프로그램 수행 중에 함수의 결과값에 대한 적절한 처리 또는 예외 상황에 대한 조건을 적절하게 검사하지 않을 경우,

예기치 않은 문제를 야기할 수 있다.

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

부적절한 예외처리를 쉬운 예시를 하나 들을 수 있습니다. A의 계좌에 1,000,000원이 있고 이를 초과하는 1,500,000원을 B에게 송금하려고 합니다. 안전한 예외처리가 발생한다면 오류 메시지와 함께 송금이 불가능하겠지만, 부적절한 예외처리를 한다면 오류 메시지 대신에 송금이 수행되어 B에게 1,500,000원을 송금하고 A의 계좌에는 잔액이 마이너스로 표시될 수 있습니다.

이러한 상황은 의도하지 않은 금액을 송금하게 만들어 시스템의 안정성과 신뢰성을 저해시킬 수 있습니다. 따라서 이번 포스팅에서는 부적절한 예외처리에 대해 알아보고 취약한 코드 예제와 이를 방지하기 위한 안전한 코드 작성 방법을 살펴보려고 합니다.

 

부적절한 예외처리란?

부적절한 예외처리는 소프트웨어에서 예외적인 상황에 대한 적절한 처리가 이루어지지 않을 때 발생합니다. 잘못된 입력이나 예상치 못한 상황에 대한 예외 처리가 부족하거나, 예외 상황이 발생했을 때 적절한 로깅이나 오류 메시지가 제공되지 않는 경우일 수 있습니다. 이는 곧 공격자에게 시스템을 탐색하고 공격하는 데 빌미를 제공하는 상황이 됩니다.

 

공격 메커니즘

부적절한 예외처리 취약점으로 인한 공격 메커니즘은 다음과 같습니다.

  1. 예외 인젝션 : 예외 처리 과정에서 사용자 입력값을 충분히 검증하지 않을 경우, 악의적인 사용자는 예외를 발생시키는 입력을 통해 시스템을 불안정하게 만들거나 의도치 않은 동작을 유도할 수 있습니다.
  2. 예외 추적 : 프로그램이나 시스템에서 발생한 예외 정보를 이용하여 시스템에 대한 정보를 수집하거나 민감한 데이터를 노출시키는 공격입니다. 일반적으로 이러한 정보는 디버그 메시지, 스택 추적(traceback), 혹은 로깅 정보 등을 통해 수집됩니다.
  3. 서비스 거부 공격(DoS) : 예외 처리가 부적절하게 구현되면 공격자는 시스템을 과부하 시키거나 의도적으로 예외를 유발하여 서비스 거부 공격을 수행할 수 있습니다.

 

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

    public class BankingSystem {
  1     private double won = 1000;

  2     public void transfer(double amount) {
            won -= amount;
            System.out.println(amount + "원 송금했습니다.");
            System.out.println("현재 잔액: " + won + "원");
        }

        public static void main(String[] args) {
            BankingSystem bankingSystem = new BankingSystem();
  3         double transferAmount = 1500;
  4         bankingSystem.transfer(transferAmount);
        }
    }
  • 1 : 초기 계좌에 1000원이 있습니다.
  • 2 : 1500원을 송금하고 남은 계좌 잔액은 -500원이 됩니다.
  • 3 : 송금할 금액은 1500원입니다.
  • 4 : 송금할 금액이 계좌 금액보다 크지만 아무런 오류 없이 송금이 가능합니다.
  • 이러한 상황이 실제로 은행에서 발생한다면 매우 치명적인 오류입니다. 이러한 오류가 발생하지 않도록 오류를 적절히 처리하면서 각 오류마다 세분화하는 것이 중요합니다.

 

시큐어코딩 적용 방법

   public class SafeBankingSystem {
       private double won = 1000;

       public void transfer(double amount) {
           try {
  1            if (amount <= 0) {
                   throw new IllegalArgumentException("송금액은 0보다 커야 합니다.");
               }
  2            if (won >= amount) {
                   won -= amount;
                   System.out.println(amount + "원을 송금했습니다.");
                   System.out.println("현재 잔액: " + won + "원");
               } else {
                   throw new RuntimeException("잔액이 부족하여 송금할 수 없습니다.");
               }
  3        } catch (IllegalArgumentException e) {
               System.out.println(e.getMessage());
  3        } catch (RuntimeException e) {
               System.out.println(e.getMessage());
  3        } catch (Exception e) {
               System.out.println("예상치 못한 오류가 발생했습니다.");
           }
       }

       public static void main(String[] args) {
           SafeBankingSystem bankingSystem = new SafeBankingSystem();
           double transferAmount = 1500;
           bankingSystem.transfer(transferAmount);
       }
   }
  • 1 : 송금액이 0보다 작거나 같다면, IllegalArgumentException 메서드를 실행하며 오류를 알립니다.
  • 2 : 송금액보다 잔액이 같거나 큰 경우 송금을 진행시키고, 잔액이 없을 경우 RuntimeException 메서드를 실행하며 오류를 알립니다.
  • 3 : 여러 상황에 대비한 예외 처리를 세분화해야 합니다.
  • 결과적으로 예외 처리를 수행하여 잔액이 부족한 상태에서는 송금이 이루어지지 않습니다.
  • 모든 예외 상황에 대해 적절한 예외 처리를 제공하고, 사용자에게 명확하고 유용한 오류 메시지를 제공하는 것이 중요합니다. 또한 예외 상황이 발생할 때 시스템이 안정적으로 동작하도록 보장하는 것이 필요합니다.

부적절한 예외 처리 취약점을 방지하기 위한 보안 대책은 다음과 같은 방법들이 있습니다.

  1. 적절한 예외 처리 : 각 예외 상황에 맞는 적절한 예외 처리를 구현합니다.
  2. 예외의 세분화 : 여러 종류의 예외를 구분하여 세분화합니다. 각 예외 상황에 맞게 다른 예외 클래스를 사용하여 예외를 발생시키고, 이를 적절하게 처리하는 것입니다.
  3. 예외 메시지 제공 : 발생한 예외에 대한 명확하고 유용한 메시지를 제공합니다. 사용자가 발생한 예외를 이해하고 적절하게 대응할 수 있도록 예외 메시지를 구체적으로 작성하는 것입니다.
  4. 예외 로깅 : 발생한 예외에 대한 정보를 로그에 기록하여 추적 가능하도록 합니다. 이를 통해 예외 발생 시점의 상황을 파악하고, 원인을 분석하여 개선할 수 있습니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : 오류상황 대응 부재
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : Null Pointer 역참조