데이터를 저장하기 위한 방법들인 encoding, encryption, hashing의 차이점을 정리해보았다.
Encoding
인코딩은 데이터의 형태를 변환하는 방식으로, 암호화 기법은 포함하지 않는다.
암호화를 하지 않으므로 secret을 포함하지 않고 누구나 디코딩을 통해 원본 값을 알아낼 수 있어 데이터 보호를 위해 사용할 수는 없다.
인코딩 기법으로 ASCII, BASE64, UNICODE 등이 있다.
Encryption
Encryption은 기밀성을 보장하도록 데이터를 변환하는 방식이다.
기밀성을 보장하기 위해 encryption은 key라는 secret을 사용한다. Encrypt된 값을 decrypt하기 위해 key값이 필요하고, key가 안전하게 공유된다는 것이 보장된다면 encryption도 안전함을 보장할 수 있다.
Encryption은 암호 알고리즘과 key로 구성된다. 암호화를 위한 여러 암호 알고리즘이 존재하는데, 암호 알고리즘은 대칭, 비대칭 알고리즘으로 구분된다.

대칭 암호화에서는 데이터를 encrypt/decrypt할 때 동일한 key를 사용한다.
대칭 키를 사용하는 대칭 알고리즘으로 AES, DES가 있다.

비대칭 암호화에서는 암호화/복호화를 위한 두 개의 key를 사용한다.
암호화를 위한 public key, 복호화를 위한 private key를 사용하고 비대칭 알고리즘으로 RSA, Diffie-Hellman, ECC 등이 있다.
private key를 갖는 특정 사용자만 해당 암호문을 해독할 수 있으므로, 보안 연결이 필요한 시스템 같이 데이터 전송 시 이를 암호화 하는데 사용하기 유용하다.
Hashing
해쉬는 input값을 고유한 string으로 변환하는 방식이다.
해쉬 함수의 output을 digest, HASH라고 표현한다.
해쉬 함수는 어떤 길이의 input이라도 일정한 길이의 output으로 변환한다. SHA-256에서는 입력 값과 상관없이 항상 256bit의 digest로 변환된다.
해쉬의 특징 중 가장 중요한 것은 해쉬 함수가 비가역적이라는 것이다. 비가역적이라는 건 input을 해쉬 함수를 거쳐 output으로 출력할 수 있지만, output을 통해 input을 계산하는 건 불가능하다는 의미이다.
하지만 해쉬 자체로는 데이터를 안전하게 저장할 수 없는데, 해쉬에도 약점이 존재하기 때문이다.
첫 번째 약점은 동일한 입력에 대해 항상 동일한 다이제스트 값을 출력하므로, 만약 같은 password를 사용하는 유저가 있다고 할 때 rainwbow table attack과 같이 해커가 특정 hash값을 갖는 plain password를 알 수 있다면, 해당 password를 사용하는 다른 모든 유저들의 정보가 유출 될 수 있다.
두 번째 약점은 해쉬 함수가 매우 빠른 속도로 계산된다는 것이다. 빠른 속도로 진행된다는 것이 성능이 좋다고 말할 수도 있겠지만, 만약 공격자가 다이제스트로부터 plain password를 얻으려고 할 때 해쉬 함수의 빠른 속도를 이용해 brute force attack을 시도할 수 있을 것이다.
이러한 약점을 보완하기 위해 Hash는 아래와 같은 방법을 사용한다.
레인보우 테이블 공격을 막기 위해 salt라는 방식을 추가로 사용한다.
Salt는 각 사용자마다 생성되는 랜덤한 값으로, salt 값을 해쉬 결과에 포함하여 DB에 저장한다.
hash값 앞에 salt를 붙여 DB에 저장함으로써, 사용자들이 같은 비밀번호를 사용한다고 해도 salt로 인해 사용자 마다 DB에 저장되는 값이 다르게 되어 미리 해쉬값을 계산하여 이를 DB 테이블과 비교하는 레인보우 공격을 방지할 수 있다.
해싱 알고리즘은 공격자의 브루트 포스 공격을 방지하기 위해 도입한 것으로, 공격자가 해쉬 함수를 통해 다이제스트를 계산하는 과정을 보다 어렵게 하기 위해 사용한다.
해싱 알고리즘으로 PBKDF2, bcrypt, scrypt, Argon2 등을 사용하고 이 알고리즘은 해쉬 함수의 속도를 낮추고 CPU/memory 비용을 올려 브루트 포스를 방지하는 역할을 한다.
Spring의 Password Encoder에서도 random salt와 bcrypt와 같은 해싱 알고리즘을 사용하여 데이터 저장의 보안성을 높인다.
데이터를 저장할 때 어떤 방식을 사용해야 할까?
앞서 데이터를 변환하는 방식으로 Encoding, Encryption, Hashing을 살펴봤는데 데이터를 안전하게 저장해야 할 때 어떤 방식을 사용하는 것이 좋을까?
Encoding은 암호화 기법이 없으므로 사용할 수 없고, Encryption과 Hashing을 고려해볼만한데, Spring에서 사용자 정보를 DB에 저장할 때 bcrypt라는 Hashing 기법을 사용한다.
왜 Encryption 대신 Hashing을 사용하는지 궁금하여 해당 부분을 찾아보았다.
Encryption은 기본적으로 encrypt하는 대상과 decrypt하는 대상으로 나눠져 있고 encyprtion은 대상 간 key를 얼마나 안전하게 보관하는지 여부에 따라 보안성이 결정된다. 대칭키든 비대칭 키든 상관없이 만약 내가 key를 잘 저장하고 있다 하더라도, 상대방이 이 key를 안전하게 저장하고 있다는 보장이 없기에 데이터를 저장할 때 encryption을 사용하는 것은 위험한 선택일 수 있다.
정리하면 사용자 정보를 DB 서버에 저장할 때 데이터를 안전하게 저장하기 위해 서버가 key를 잘 관리한다 하더라도, 클라이언트 측이 해당 key를 얼마나 안전하게 보관하고 있을지를 모르기 때문에 데이터 저장 시 Encryption의 기밀성을 보장할 수가 없다고 할 수 있다.
따라서 단순히 데이터를 DB에 저장할 때는 Hashing을 사용하는 것이 합리적이고, Encyption은 데이터 전송 간 보안을 유지할 때 사용하는 편이다.