Hash란 무엇인가

데이터의 암호화 기법은 크게 두가지 방법으로 나눌 수 있는데 다음과 같은 차이가 있다.

Encryption (암호화)

Encryption을 통한 암호는 기본적으로 Decryption(해독)이 가능하다.

Hash (해쉬)

Hash는 일방 통행의 암호화이기 때문에 한번 암호화 된 것은 해독이 불가능하다.

이번 글에서는 이 해쉬가 무엇인지에 대해 알아보도록 한다.


1대1

이론적으로 해쉬는 하나의 인풋(input)에 대해 단 하나의 아웃풋(output)만 생성된다. 즉 아웃풋이 겹치는 경우가 없다는게 기본 전제이다. 같은 인풋에 대해선 항상 같은 아웃풋이 생성된다.

이론적으론 이렇듯 1대1 결과물이 나와야하지만, 어떤 알고리듬을 이용하느냐에 따라서 다르다. 예전에 주로 사용되던 SHA-1 알고리듬은 구글에서 겹치는 아웃풋을 찾아내는 것에 성공했다고 한다. 즉 뚫렸다는 뜻이 되고 더 이상 안전하지 않다는 것이 증명되었다.

아마 어떤 알고리듬이든 뚫는 것이 불가능한 건 아니겠지만, 엄청나게 어렵다는 것만 알아두면 된다. 그래서 이론상으로는 1대1 결과물이 나온다고 하는 것이다. 불가능하다는걸 완벽하게 증명할 수 없기 때문이다.

SHA-256

요즘에 보편적으로 쓰이는 해쉬 알고리듬은 SHA-256이다. 어떠한 데이터든 256비트의 고정된 문자열로 변환해주는 해쉬 알고리듬이다. 1바이트(byte)가 8비트(bit)이기 때문에, 바이트로 환산하면 32바이트. 1바이트당 두개의 문자가 들어가기 때문에 64개의 문자열을 생성해주게 된다.

예를 들면 "Hello World" 라는 문자를 SHA-256으로 해쉬를 하면 아래와 같이 변환된다.

a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e

인풋은 문자열이 아니어도 되고, 사진, 음성파일등 무엇이든 가능하다.

해쉬를 할 땐 인풋이 아주 미세하게 바뀌기만 해도 전혀 다른 결과물이 나타난다.

예를 들어 위의 "Hello World"를 맨 앞글자만 소문자로 바꾼 "hello World"로 해쉬하면 아래와 같다.

db4067cec62c58bf8b2f8982071e77c082da9e00924bf3631f3b024fa54e7d7e

위의 결과에서 알 수 있듯이 전혀 다른 값이 나온다. 그렇기 때문에 데이터를 더욱 안전하게 보관하고 싶다면 해쉬해서 나온 값을 다시 해쉬한다던가 하면 된다.

일방통행의 암호화라 결과값을 가지고 역으로 인풋이 뭐였나 알아낼 수 있는 방법은 단 하나다. 세상에 존재하는 모든 데이터를 일일이 해쉬해서 돌려보고 결과 값을 구해서 대조해볼 수 밖에 없다. 한마디로 불가능한 일에 가깝다.

SHA-256을 이용한 해쉬 값은 여러 웹사이트에서 만들어볼 수 있는데 그 중 한곳이 이곳이다.


#해쉬 기법은 어떨 때 쓰나?

가장 보편적으로 쓰이는 방법은 이용자의 비밀번호를 데이터베이스에 저장할 때이다. 이용자의 비밀번호가 "123456"이었을 경우, 이걸 그대로 데이터베이스에 저장하면, 데이터베이스가 해킹당해서 털리는 순간 이용자의 비밀번호도 그대로 노출되게 된다.

하지만 "123456"를 해쉬한 값을 데이터베이스에 저장한다면, 설령 데이터베이스가 해킹당해도 이용자의 비밀번호가 무엇인지는 해커도 알 수가 없다.

(참고로 "123456"의 해쉬값은 "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"이다)

물론 "123456"이나 "password"처럼 애초에 쉬운 비밀번호라면 해쉬해서 저장했다고 해도 별로 의미가 없다. "123456"이나 "password"에 대한 해쉬 값은 누구나 구할 수 있기 때문에, 대조해보면 금방 알 수 있기 때문이다. 그렇기에 이용자는 유추하기 어려운 조합의 비밀번호를 사용해야 안전하다.

디지털 서명과의 조합

또 다른 예로는 Public/Private Key로 만드는 디지털 서명과 함께 쓰는 방법이다.
(Public/Private Key에 대해서는 이전 포스트를 참조)

예를 들어 철수가 영희에게 "내일 5시에 만나" 라는 메세지를 보낸다고 가정한다.

  1. "내일 5시에 만나"의 해쉬값을 구한다.
  2. 해쉬값을 철수의 Private Key를 이용해서 암호화 한다. (디지털 서명)
  3. "내일 5시에 만나" 라는 메세지와 디지털 서명을 함께 영희에게 보낸다.
  4. 영희는 철수의 디지털 서명을 철수의 Public Key를 이용해서 해독한다.
  5. 해독해서 나온 값을 "내일 5시에 만나" 라는 메세지의 해쉬값과 비교해본다.
  6. 두가지 값이 일치하면 철수가 보낸 메세지라는 것을 확신할 수 있다.

대충 위와 같은 단계를 거치게 된다. 그렇다면 여기서 한가지 의문이 들 수 있다. 애초에 그냥 메세지를 Private Key로 암호화 해서 보내면 되지 왜 굳이 해쉬 값을 암호화해서 메세지와 같이 보내는가?

이유는 효율성과 데이터의 위/변조 방지에 있다.

효율성

우선 효율에 대해 얘기를 하자면, 메세지가 짧을 경우는 디지털 서명의 크기도 작겠지만, 만약 메세지의 크기가 크다면 그에 비례해서 디지털 서명의 크기도 거치게 된다. 반면 해쉬 값은 애초에 256바이트 라고 정해져있으니, 디지털 서명의 크기도 메세지의 길이와 상관 없이 고정할 수 있게 된다.

데이터 위/변조 방지

디지털 서명의 크기에는 한계가 있다. 어떤 RSA 타입인지에 따라 한계치도 달라지긴 하지만 일단 한계치가 있다는 것만 알아두자. 그럼 만약 암호화 하려는 데이터가 한계치를 넘었을 경우 어떻게 되는가?

데이터를 조각화하여 각각의 조각을 암호화하게 된다. 예를 들어 한계치가 한번에 두글자까지라고 가정하면 "내일 5시에 만나"라는 메세지는 다음과 같이 암호화하게 된다.

  • "내일" 에 대한 서명을 "S1"
  • "5시" 에 대한 서명을 "S2"
  • "에" 에 대한 서명을 "S3"
  • "만나" 에 대한 서명을 "S4"

이렇게 조각낸 암호들을 순서대로 "S1 S2 S3 S4" 라고 보내면 되겠지만, 문제는 중간에 누군가가 이 순서를 바꿈으로써 데이터의 변조를 시도할 수 있다는 것이다. 가령 누군가가 순서를 "S2 S4 S3 S1"로 바꿔버리면, 해독한 메세지는 "5시만나에 내일"이 되어버린다. 변조가 된 것이다. 서명 자체에는 문제가 없기 때문에 변조된 내용에 따라서는 아무런 의심을 받지 않을 수도 있다.

이러한 일을 방지 하기 위해서 데이터를 해쉬 한 다음 서명하는 것이다.

더욱 안전한 디지털 서명은?

위의 예로 왜 해쉬한 값에 디지털 서명을 하는지에 대해서는 알 수 있었다. 하지만 또 다시 하나의 의문이 생길 수 있다.

디지털 서명 자체를 복사해서 사용하면 얼마든지 사기를 칠 수 있지 않나?

해쉬는 하나의 인풋에 대해서 항상 같은 아웃풋을 생성한다. 그 아웃풋을 철수가 그의 Private Key를 이용하여 만든 디지털 서명은 항상 같은 서명 값이 된다.

그럼 이 서명 자체를 훈이가 이용해서, 마치 철수가 보낸 것 처럼 영희에게 메세지를 보낼 수 있게 된다. 디지털 서명을 도용할 수 있다는 것이다.

이를 방지하기 위해서는 해쉬를 만들 때, 그 메세지를 만든 시간과 유효기간을 설정하면 된다.

"내일 5시에 만나" + "영희에게" + "2018/07/10 18:32:28" + "24시간"

이런 식으로 추가 적인 인풋을 함께 해쉬하면 그 값은 그 시각에만 생성할 수 있는 고유의 해쉬값이 된다. 해쉬 값은 인풋이 조금이라도 바뀌면 완전히 다른 값이 되기 때문이다.

이렇듯 서명 자체의 유효기간을 설정해두면, 설사 나중에 훈이가 이 디지털 서명을 도용했다고 해도, 이미 유효하지 않은 서명이라는 것을 알 수 있기 때문에 도용된 것이라는 걸 알 수 있다.


Public/Private Key 그리고 해쉬는 데이터 암호화에 있어 가장 보편적으로 사용되는 방법이다. 이 두가지를 이해하면 https나 블록체인과 같은 것들이 어떤 원리로 작동하는지 이해하기 더 쉬워지고, 다른 컨셉들로 지식의 영역을 넓혀갈 수 있다.