소프트웨어 개발보안 가이드 분석(2021) : 하드코드된 중요정보

프로그램 코드 내부에 하드코드된 패스워드 또는 암호화키를 포함하여 내부 인증에 사용하거나

암호화를 수행하면 중요정보(관리자 정보, 암호화된 정보 등)가 유출될 수 있는 보안취약점이다.

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

 

하드코드된 중요정보란?

  • 하드코드(하드코딩)란 소프트웨어 개발에서 특정한 값이나 설정을 소스 코드에 직접 포함시키는 것을 의미합니다. 하드코딩은 특정한 상황에서는 편리할 수 있지만, 보안상의 문제와 유지 보수의 어려움을 발생시킬 수 있습니다.
  • 즉, 하드코드된 중요정보란 소프트웨어에서 사용되는 중요한 데이터나 인증 정보 등이 소스 코드나 실행 파일에 직접 하드코딩되어 있는 상태를 의미합니다. 이로 인해 해커가 쉽게 해당 정보에 접근하여 시스템을 침투하거나 중요한 데이터를 유출할 수 있습니다.

 

공격 메커니즘

하드코드된 비밀번호(왼쪽) / 하드코드된 암호화키(오른쪽)
소프트웨어 개발 보안 가이드(행정자치부 / 한국인터넷진흥원)
  1. 소스 코드 누출 : 하드코드된 중요정보가 소스 코드에 직접 포함되어 있으면, 공격자가 소스 코드에 접근하여 중요정보를 얻을 수 있습니다. 이러한 정보는 API 키, 암호화된 비밀번호, 데이터베이스 연결 정보 등이 될 수 있습니다.
  2. 리버스 엔지니어링 : 실행 파일이나 애플리케이션에서 하드코딩된 중요정보를 추출하기 위해 리버스 엔지니어링 기술이 사용될 수 있습니다. 이를 통해 공격자는 중요한 정보를 분석하고 악용할 수 있습니다.
  3. 인증 우회 : 하드코딩된 인증 정보를 악용하여 시스템의 인증 기능을 우회하는 공격입니다. 공격자는 하드코딩된 사용자 이름과 비밀번호를 이용하여 시스템에 로그인하거나 인증을 우회하여 권한을 얻습니다.
  4. 악의적인 행위 : 인증을 우회하여 권한을 얻은 공격자는 시스템에 접근하여 악성 코드 삽입, 정보 탈취 등 악의적인 행위를 수행합니다.

 

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

  • 하드코드된 비밀번호
   public class HardcodedDBPasswordExample {
       // 하드코드된 데이터베이스 연결 정보
 1     private static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase";
 1     private static final String DB_USER = "myuser";
 2     private static final String DB_PASSWORD = "myHardcodedPassword123";

       public static void main(String[] args) {
           Connection connection = null;        
 3             connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
    }
}
  • 1 : 데이터베이스 연결을 위한 정보
  • 2 : 데이터베이스 연결에 필요한 패스워드가 하드코딩 됩니다.(myHardcodedPassword123)
  • 3 : 데이터베이스 연결 정보(DB의 URL, USER, 하드코드된 PASSWORD)를 이용하여 데이터베이스 연결
  • 해당 코드는 상수로 데이터베이스 연결 정보를 하드코딩했습니다. 다만, 이렇게 하드코딩된 비밀번호를 사용하는 것은 보안상 위험하므로 외부 설정 파일이나 환경 변수를 사용하여 비밀번호를 안전하게 관리하는 것이 좋습니다.

  • 하드코드된 암호화 키
    public class AESEncryptionExample {
  1     private static final String ENCRYPTION_KEY = "myHardcodedEncryptionKey";
        public static void main(String[] args) {
  2         String dataToEncrypt = "SensitiveData";
  3         Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
  4         SecretKeySpec secretKeySpec = new SecretKeySpec(ENCRYPTION_KEY.getBytes(), "AES");
  5         cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
  6         String encryptedData = Base64.getEncoder().encodeToString(cipher.doFinal(dataToEncrypt.getBytes()));
        }
    }
  • 1 : AES 암호화에 사용할 키를 하드코딩 합니다.
  • 2 : 예시를 위해 해당 문자열을 암호화합니다.
  • 3 : AES 알고리즘을 사용하여 암호화를 수행할 객체를 생성합니다.
  • 4 : 암호화 키를 바이트 배열로 변환합니다.
  • 5 : 암호화 모드로 설정하고 사용할 키를 적용합니다.
  • 6 : 2번의 문자열을 암호화하고, 결과를 Based64로 인코딩합니다.
  • 결과적으로 암호화에 사용되는 암호화 키가 하드코딩되어 보안상의 위험이 있습니다. 따라서 안전한 방법으로 암호화 키를 관리해야 합니다.

 

시큐어코딩 적용 방법

  • 하드코드 되지 않은 비밀번호
   public class SecureDBConnectionExample {
       public static void main(String[] args) {
           Connection connection = null;

   1       Properties properties = new Properties();
   2       InputStream input = new FileInputStream("db.properties");
   3       properties.load(input);
            
   4       connection = DriverManager.getConnection(
                   properties.getProperty("db.url"),
                   properties.getProperty("db.user"),
                   properties.getProperty("db.password")
           );
       }
   }
  • 1 : 외부 설정 파일에서 읽어올 데이터베이스 연결 정보를 저장할 객체를 생성합니다.
  • 2 : 외부 설정 파일(db.properties)을 읽어옵니다.
  • 3 : input으로 읽어온 외부 설정 파일 데이터를 로드합니다.
  • 4 : 외부 설정 파일에서 읽어온 정보를 이용해 데이터베이스에 연결합니다.
  • 다음과 같이 소스코드 하드코딩 대신에 환경 변수를 사용하거나 외부 설정 파일에서 비밀번호를 불러 들여오는 것이 보안상 안전합니다.
  • ※ db.properties 파일에는 하드코드된 비밀번호 코드에서 1~2의 코드가 작성되어 있습니다.

  • 하드코드 되지 않은 암호화 키
    public class SecureAESEncryptionExample {
        public static void main(String[] args) {
            String dataToEncrypt = "SensitiveData";
        
    1       String encryptionKey = readEncryptionKeyFromFile("encryption.properties");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            Key secretKeySpec = new SecretKeySpec(encryptionKey.getBytes(), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            String encryptedData = Base64.getEncoder().encodeToString(cipher.doFinal(dataToEncrypt.getBytes()));
        }

        // 외부 설정 파일에서 암호화 키를 읽어오는 메서드
   2    private static String readEncryptionKeyFromFile(String filename) throws IOException {
            Properties properties = new Properties();
            try (InputStream input = new FileInputStream(filename)) {
                properties.load(input);
                return properties.getProperty("encryption.key");
            }
        }
    }
  • 1 : readEncryptionKeyFromFile메서드를 통해 외부 설정 파일(encryption.key)에서 암호화 키를 읽어옵니다.
  • 2 : encryption.key 파일에 설정되어 있는 암호화 키를 읽어와 리턴합니다.
  • 결과적으로 소스코드에는 암호화 키를 작성하지 않고 외부의 파일로부터 불러 들여오기 때문에 코드의 안전성이 향상됩니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : 암호화되지 않은 중요정보
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : 충분하지 않은 키 길이 사용