암호화 알고리즘에 대해서 학습하다가 해싱과 암호화에 대해서 잘못 이해하고 있었던 개념들이 있었다는 것을 깨달았습니다. 먼저 해싱에 대해서 설명하겠습니다.
해싱(단방향 암호화)?
해싱은 어떤 데이터를 고정된 크기의 고유한 값, 즉 '해시값'으로 변환하는 과정입니다. 이때 변환 과정은 단방향입니다. 즉, 해시값에서 원래의 데이터로 되돌릴 수 없기 때문에 단방향 암호화라고도 합니다. 고정된 크기의 고유 값으로 만들기 위해서 해시 함수를 사용하는데, 해시 함수는 어떤 길이의 데이터든 간에 일정한 길이의 해시값으로 출력합니다. 여기서 나머지 연산의 개념이 적용됩니다. 해시 함수는 각 데이터를 정해진 수로 나눈 나머지 값으로 계산합니다. 그 결과로 생성된 나머지 값이 곧 해당 데이터의 해시값이 됩니다. 나머지 연산을 적용하면 a mod 3 = 1이 될 수 있는 a의 값은 무수히 많습니다. 이러한 방식으로 알고리즘을 알고있더라도 원래 값을 알 수 없게 만드는 단방향 암호화 방식이 해싱입니다.
대표적으로, SHA-256은 'Secure Hash Algorithm 256'의 줄임말로, 해싱 알고리즘 중 하나입니다. 이 알고리즘은 입력된 데이터를 256비트 길이의 해시값으로 변환합니다. 이때 변환된 해시값은 원래 데이터의 아주 작은 변화에도 완전히 다른 값이 되어, 원래 데이터에 대한 정보를 전혀 가지지 않습니다.
암호화(양방향 암호화)?
암호화는 해싱과 마찬가지로 데이터를 안전하게 보호하기 위해 이해할 수 없는 형태로 변환하는 과정입니다. 그러나 해싱은 복호화가 불가능한 반면, 암호화는 데이터를 복호화(해독)하여 원래의 형태로 돌릴 수 있어야 합니다. 이것은 암호화의 핵심적인 차이점입니다. 암호화의 방식에는 대칭키 방식과 비대칭키 방식이 있습니다.
간단하게 설명하면 대칭키 방식은 공통의 키로 암호화와 복호화가 가능한 방식입니다. 데이터 -> 암호 -> 데이터의 암호화 복호화 과정이 하나의 키로 수행가능한 암호 알고리즘입니다. 비대칭키 방식(비밀키 방식)은 공용키, 비밀키로 암호화, 복호화를 각각 하는 방식입니다. 어떤 키를 비밀키로 할 지는 목적에 따라 다릅니다.
CA와 같이 암호화하는 기관이 신뢰할 수 있는 기관임을 증명하기 위해서 비밀키로 암호화, 공용키로 복호화 합니다.
그리고 데이터 보호의 측면에서는 비밀키로 복호화, 공용키로 암호화하는 것이 안전하겠죠.
결론적으로 양방향 암호화는 키를 알면 데이터를 복호화하거나 암호화할 수 있기 때문에 키와 패킷이 탈취되면 제 3자가 데이터를 탈취하여 개인정보 등을 얻을 수 있습니다.
대표적으로 사용되는 양방향 암호화 알고리즘인 RSA 알고리즘은 'Rivest, Shamir, Adleman'의 이니셜로, 공개키 암호화 방식 중 하나입니다. RSA는 두 개의 큰 소수를 이용하여 공개키와 비밀키를 생성합니다. 큰 소수의 인수분해 방식을 key로 사용하기 때문에, key를 모르는 상태에서 복호화하기 위해서는 큰 소수의 조합을 brute force로 찾아야하고, 큰 bit 수를 암호화에 사용할 경우 슈퍼 컴퓨터로도 만년 단위의 시간이 걸려 보안이 강력한 알고리즘으로 알려져있습니다.
해싱과 암호화 비교
이제 해싱과 암호화 알고리즘의 차이점을 명확히 이해했을 것입니다. 해싱은 데이터의 무결성을 확인하는데 적합하며, 원본 데이터를 추론할 수 없는 고유한 해시값을 제공합니다. 반면 암호화는 원본 데이터를 복호화할 수 있어야 하는 상황, 예를 들어 안전한 정보 전송이 필요한 경우에 적합합니다. 키가 유출되면 아무리 강력한 암호화 알고리즘이더라도 무용지물이지만, 제대로된 해싱 알고리즘은 key 유출과 무관하게 원복이 거의 불가능합니다.
그렇기 때문에 대부분의 서비스에서 사용자의 패스워드같은 민감한 정보가 DB에서 유출되더라도 해커가 원본으로 원복할 수 없도록 해싱으로 저장하고 서비스 담당자 또한 원본의 패스워드는 알 수 없는 것입니다. 사용자 비밀번호를 잊은 경우 원래 비밀번호를 알려주는 것이 아닌 새 비밀번호로만 변경이 가능한 이유가 이 것입니다. 해싱은 암/복호화에 비해 가볍고 보안성이 강력하므로 위조만 판단하려는 경우 해싱으로 sign할 수 있습니다.
하지만 해싱이라고 해서 절대 해킹이 불가능하지는 않습니다. 키 값의 bit수가 적은 경우 bruteforce로 해킹을 할 수 있으므로, 그리고 긴 key를 쓴다고 하더라도 언젠가는 해킹이 가능합니다. 그러므로, 특정 길이 이상의 bit수를 써주고 키를 주기적으로 교체해주는 것이 좋습니다. 6개월 주기로 비밀번호 교체를 권장하는 이유가 이러한 연유에서입니다.
비밀번호 암호화 방식
그렇다면 일반적인 서비스에서 어떻게 DB에 정보를 저장하는 것이 안전할지 생각해보겠습니다.
1. 클라이언트에서 해싱한 후 서버에서 받아서 DB에 그대로 저장한다.
이 경우는 비밀번호가 평문이 아니지만, 제 3자가 패킷을 탈취해서 그대로 인증에 사용가능하기 때문에 평문 패스워드를 얻은 것과 크게 다를바가 없습니다. 따라서 클라이언트에서 해싱하는 것은 크게 의미가 없다는 것을 알게됩니다. 즉, 제 3자가 패킷을 탈취했을 때 이를 악용하지 못하도록 하려면 암호화/복호화 과정이 필요하다는 것을 알 수 있습니다.
2. 클라이언트에서 암호화한 후 서버에서 복호화하여 평문으로 만들고, DB에 저장한다.
이 경우에는 제 3자가 패킷을 탈취해도 비교적 안전합니다. 패킷을 복호화하여 평문으로 DB에 저장하기 때문에, 원본 패스워드를 알고있는 것이 아닌 암호화된 패스워드로는 인증을 할 수 없습니다. 하지만, 대상 서버에서 복호화한 후 평문으로 DB에 저장하기 때문에, DB의 패킷을 탈취하면 사용자의 패스워드를 알 수 있습니다.
3. 클라이언트에서 암호화한 후 서버에서 복호화하여 평문으로 만들고, 이를 해싱하여 DB에 저장한다.
이 경우는 조금 더 안전합니다. DB 패킷을 탈취하더라도 사용자의 패스워드는 알 수 없고, 해싱된 값을 클라이언트에서 입력하더라도 서버에서 한번 더 해싱하기 때문에 DB에 저장된 값과는 일치하지 않습니다. 즉, h(password) != h(h(password))인 경우입니다. 그러나 한 가지 문제가, 서버의 메모리 상에 평문 패스워드가 일시적으로 존재합니다. 해커가 메모리를 dump할 수 있는 경우 평문의 패스워드를 그대로 확인할 수 있습니다.
4. 클라이언트에서 해싱, 암호화한 후 서버에서 복호화하여 평문으로 만들고, 이를 해싱하여 DB에 저장한다.
가능한 해결 방법은 바로 클라이언트에서 해싱을 한 번 더 해주는 것입니다. 이렇게 하면 메모리에서 dump하여 해싱된 패스워드를 얻더라도 이중 해싱되기 때문에 인증에 성공할 수 없게 됩니다. 클라이언트에서 패킷을 전송한 이후로 평문으로 패스워드가 원복되는 경우가 없기 때문에 보안적으로 더욱 안전한 방식입니다.
위에서 학습했던 알고리즘을 이용하면 전체 과정은 아래와 같습니다.
1. SHA256으로 클라이언트에서 해싱합니다.
2. HTTPS로 패킷을 전송합니다. 이 과정에서 SSL을 이용하기 때문에 RSA 암호화 방식으로 패킷이 암호화됩니다.
3. 서버에서 패킷을 복호화하여 해싱된 비밀번호를 얻습니다.
4. DB에 저장하기 전에 값을 한번 더 해싱합니다.