Secure Coding)해시함수와 문제점

시큐어 코딩

Featured image

@

단방향 해시란?

단방향 해시 함수는 수학적인 연산을 통해 원본 메시지를 변환하여 암호화된 메시지인 다이제스트(digest)를 생성한다. 원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없어야 하며 이를 ‘단방향성’이라고 한다. 대표적으로 MD5, SHA 등이 있다. ‘password’를 MD5로 변환하면 ‘5f4dcc3b5aa765d61d8327deb882cf99’ 다이제스트가 생성이 된다. ‘Password’를 똑같이 변환하면 ‘dc647eb65e6711e155375218212b3964’가 되어 전혀 다른 문자가 되는 것을 확인할 수 있다. 이러한 특징을 avalanche라 하고 사용자의 원본 패스워드를 추론하기 어렵게 만드는 중요한 요소이다.

단방향 해시함수의 문제점

같은 메시지가 같은 다이제스트를 갖는다면 다이제스트를 많이 갖고 있다면 그 둘을 비교하면 원본데이터를 찾을 수 있다. 이러한 행위를 레인보우 공격(rainbow attack)이라 한다. 또한 해시 함수는 처음부터 짧은 시간에 데이터를 검색하기 위해 설계된 것이다. 그러다보니 1초당 56억 개의 다이제스트를 대입할 수 있을 만큼 빠른 속도를 가지고 있다. 따라서 위와같은 공격은 치명적일 수 밖에 없다.

해결방안

  1. 다이제스트 생성에 딜레이를 준다.
  2. 솔팅(salting)
  3. 키 스트레칭(key stretching)

해결방안1 - 다이제스트 생성에 딜레이를 준다.

사용자는 패스워드를 인증하는 데 걸리는 시간에 그리 민감하지 않다. 그래서 다이제스트를 생성하는 시간을 0.1초에서 1초로 변경하더라도 사용자는 신경쓰지 않는다.

import CryptoSwift

extension String {
  func delayMD5(timeInterval:TimeInterval, completion:@escaping (_ md5:String)->()) {
        Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false) { (timer) in
            completion(self.md5())
            timer.invalidate()
        }
    }
}

해결방안2 - 솔팅(salting)

솔트(salt)는 단방향 해시 함수에서 다이제스트를 생성할 때 추가되는 바이트 단위의 임의의 문자열이다. 그리고 이 원본 메시지에 문자열을 추가하여 다이제스를 생성하는 것을 솔팅(salting)이라 한다.

import CryptoSwift

extension String {
  func saltingMD5(salt:String) -> String {
        return (self+salt.md5()).md5()
  }
}

해결방안3 - 키 스트레칭(key stretching)

입력한 패스워드의 다이제스트를 생성하고, 생성된 다이제스트를 입력 값으로 하여 다이제스트를 생성하고, 또 이를 반복하는 방법으로 다이제스트를 생성할 수도 있다. 이렇게 하면 입력한 패스워드를 동일한 횟수만큼 해시해야만 입력한 패스워드의 일치 여부를 확인할 수 있다. 이것이 기본적인 키 스트레칭 과정이다. 최근에는 일반적인 장비로 1초에 50억 개 이상의 다이제스트를 비교할 수 있지만, 키 스트레칭을 적용하여 동일한 장비에서 1초에 5번 정도만 비교할 수 있게 한다. GPU(Graphics Processing Unit)를 사용하더라도 수백에서 수천 번 정도만 비교할 수 있다. 50억 번과는 비교할 수도 없을 정도로 적은 횟수다. 앞으로 컴퓨터 성능이 더 향상되면 몇 번의 반복을 추가하여 보완할 수 있다

import CryptoSwift

extension String {
  func keyStretchingMD5(round:Int) -> String {
        var ret = self
        for _ in 0..<round {
            ret = ret.md5()
        }
        return ret
    }
}

마침

CryptoSwift라는 오픈소스를 활용해서 해결방안들을 만들어보았다. 이 외에도 Adaptive Key Derivation Functions, PBKDF2, bcrypt 등 많은 알고리즘이 존재하고 그것을 활용하는 방법은 다음 포스팅에 쓰도록 하겠다.

출처