해싱, 암호화, 인코딩: 사람들이 혼동하는 세 가지

인코딩, 암호화, 해싱은 서로 다른 문제를 풉니다. 이것을 섞어 쓰는 것이 비밀번호가 새고 토큰이 위조되는 방식입니다. 그 정확한 구분을 정리합니다.

대화와 코드 리뷰에서 세 가지 작업이 서로 바꿔 쓰이고, 그 혼동은 학술적이지 않습니다. 평문이나 다름없는 비밀번호 저장소와 누구나 읽을 수 있는 토큰의 직접적 원인입니다. 인코딩, 암호화, 해싱은 모두 데이터를 변환하지만, 서로 다른 질문에 답하고 서로 다른 가역성을 가집니다. 잘못 고르면 실패는 조용 합니다. 코드는 돌고, 테스트는 통과하고, 가졌다고 생각한 보안 속성은 존재하지 않습니다.

가장 중요한 한 가지 구분

알고리즘을 걷어내면 질문은 둘입니다.

  • 되돌릴 수 있는가? 인코딩과 암호화는 되돌릴 수 있습니다. 해싱은 아닙니다.
  • 되돌리는 데 비밀이 필요한가? 암호화는 키가 필요합니다. 인코딩은 아무것도 필요 없습니다. 변환이 공개돼 있습니다.

그러면 세 묶음이 나옵니다.

되돌릴 수 있는가? 키가 필요한가? 목적 예시
인코딩 아니요 표현 / 전송 Base64, 16진수, URL 퍼센트 인코딩, UTF-8
암호화 기밀성 AES(대칭), RSA / ECC(비대칭)
해싱 아니요 아니요 무결성, 지문, 조회 SHA-256, SHA-3, BLAKE3

"키가 필요한가" 열이 사람들이 건너뛰는 열이고, 무언가가 보안 통제이긴 한지를 결정하는 열입니다. 인코딩은 키가 필요 없으므로 기밀성을 제공하지 않습니다. 끝입니다. 누구든 비밀 없이 역변환을 돌릴 수 있다면, 데이터는 노출된 것입니다.

인코딩: 표현을 바꾸기

인코딩은 바이트를 한 알파벳에서 다른 알파벳으로 매핑해, 원본을 운반하지 못하는 채널을 통과하게 합니다. Base64는 임의의 바이너리를 64개의 출력 가능한 ASCII 문자로 바꿔 JSON·이메일·URL에 맞게 합니다. 퍼센트 인코딩은 URL에서 의미를 갖는 문자를 이스케이프합니다. 16진수는 바이트를 0f 쌍으로 표시용으로 렌더링합니다. UTF-8은 코드 포인트를 바이트 시퀀스로 인코딩합니다.

이 중 어느 것도 비밀을 수반하지 않으며, 그것이 핵심입니다. 디코더는 모든 표준 라이브러리에 내장돼 있습니다. 브라우저의 atob(), 파이썬의 base64.b64decode, 명령줄의 base64 -d 모두 키 없이 즉시 Base64를 되돌립니다.

인코딩은 문제가 모양일 때 알맞은 도구입니다. 텍스트 전용 필드를 통과해야 하는 바이너리, URL에 안전해야 하는 파일명, 출력돼야 하는 해시 다이제스트 같은 것입니다. 문제가 비밀 유지인 순간에는 틀린 도구입니다. 그것을 전혀 제공하지 않기 때문입니다.

암호화: 키를 쓰는 기밀성

암호화는 평문을 키 없이는 되돌릴 수 없는 암호문으로 변환합니다. 셋 중 기밀성을 보호하는 유일한 것이고, 키가 그것을 참으로 만드는 것입니다.

두 갈래입니다.

  • 대칭(AES, ChaCha20) — 같은 키가 암호화하고 복호화합니다. 빠르고, 대량 데이터에 씁니다. 디스크 암호화, TLS 레코드 페이로드, 데이터베이스 컬럼 암호화입니다.
  • 비대칭(RSA, ECC) — 공개 키가 암호화하고, 개인 키가 복호화합니다(또는 서명의 경우 그 반대). 더 느리고, 사전 공유 비밀 없이 신뢰를 부트스트랩하고 대칭 키를 교환하는 데 씁니다.

암호화가 강요하는 트레이드오프는 키 관리입니다. 암호화된 데이터는 딱 키만큼 안전하고, 키는 어딘가에 있어야 합니다. KMS, HSM, 환경 변수, 설정 파일입니다. 새어 나간 키는 모든 암호문을 소급해 평문으로 되돌립니다. "우리는 암호화한다"는 키가 어디 있고 누가 닿을 수 있는지 답할 수 있을 때까지 미완의 문장입니다.

해싱: 단방향 지문

해시 함수는 임의 크기의 입력을 고정 크기 다이제스트로 매핑하며, 정의적 속성은 거꾸로 돌릴 수 없다는 것입니다. 키도 디코드 단계도 없습니다. 다이제스트가 주어졌을 때, 그것을 만들어 내는 입력을 찾는 유일한 방법은 하나가 맞을 때까지 입력을 시도하는 것입니다.

그 비가역성이 기능입니다. 해싱은 데이터 자체를 저장하거나 드러내지 않고 "이것이 같은 데이터인가?"와 "이것이 바뀌었는가?"에 답합니다. 무결성 검사(내려받은 파일의 다이제스트 비교), 중복 제거, 콘텐츠 주소 저장소(Git 커밋 ID), 해시 테이블 조회를 떠받칩니다.

분명히 말해 둘 미묘한 점 하나입니다. MD5와 SHA-1은 충돌 저항성 면에서 깨졌습니다. 공격자가 같은 다이제스트를 갖는 서로 다른 두 입력을 구성할 수 있고, 이는 서명과 인증서에 치명적입니다. 그래도 비보안 체크섬으로는 여전히 완벽히 괜찮습니다. 우발적 손상 탐지만 신경 쓰고 충돌을 조작하는 적이 없는 경우입니다. 공격자가 영향을 줄 수 있는 것에는 SHA-256이나 BLAKE3을 쓰세요. MD5는 ETag나 캐시 키로는 받아들일 만합니다.

혼동, 그리고 그것이 낳는 버그

"비밀번호를 저장 전에 Base64로 한다"

이것은 보안으로 오인된 인코딩입니다. Base64는 키가 없습니다. 저장된 값은 분장한 비밀번호입니다. 테이블 읽기 권한을 가진 누구든 디코드 한 번으로 모든 자격 증명을 갖습니다. 이것은 마땅한 것보다 더 자주 실제 침해에 나타납니다. Base64는 전송용이지 비밀의 저장용이 결코 아닙니다.

"비밀번호를 SHA-256 한다"

더 가깝지만 여전히 틀렸고, 그 이유가 미묘해서 신중한 엔지니어도 배포합니다. SHA-256은 비가역적이고, 이는 비밀번호 저장이 원하는 바로 그것처럼 느껴집니다. 문제는 그것이 빠르다는 것입니다. 현대 GPU는 초당 수십억 개의 SHA-256 해시를 계산합니다. 해시 테이블을 훔친 공격자는 사전 공격이나 무차별 대입을 오프라인에서 엄청난 속도로 돌리고, 약한 비밀번호는 몇 초 만에 무너집니다.

비밀번호 저장에는 느리고 의도적으로 비싼 키 유도 함수가 필요합니다. bcrypt, scrypt, 또는 Argon2입니다. 이들은 조정 가능합니다. 비용 인자를 설정해 해시 하나가 예컨대 100ms 걸리게 하면, 로그인에서는 눈에 띄지 않지만 오프라인 크래킹은 수천 배 비싸집니다.

그리고 모든 비밀번호에는 사용자별 솔트가 필요합니다. 해시와 함께 저장돼 그 안에 섞이는 무작위 값입니다. 솔트가 없으면 같은 비밀번호를 쓴 두 사용자가 같은 다이제스트를 얻고, 공격자는 흔한 비밀번호 해시 테이블(레인보우 테이블)을 한 번 미리 계산해 새어 나온 모든 데이터베이스에 영원히 대조합니다. 사용자마다 고유한 솔트는 사전 계산을 완전히 무력화합니다. 공격자는 각 항목을 따로 깨야 합니다. 현대 KDF는 솔트를 생성해 함께 담아 줍니다. bcrypt는 출력 문자열에 그것을 저장합니다.

페퍼는 관련되지만 구별되는 개념입니다. 모든 비밀번호에 해싱 전 더해지는 비밀 값으로, 해시 안이 아니라 데이터베이스와 분리해(앱 설정이나 KMS에) 저장됩니다. 솔트는 데이터베이스가 새어 나갈 때 사전 계산을 방어하고, 페퍼는 데이터베이스만의 유출로는 드러나지 않는 두 번째 요소를 더합니다. 이것은 심층 방어 수단이지, 제대로 된 KDF와 솔트의 대체물이 아닙니다.

"API 토큰을 암호화한다"

때로는 옳고, 자주 틀린 도구입니다. 나중에 토큰의 평문을 복구해야 한다면, 사용자를 대신해 상위 API로 전달하기 위해서라면, 암호화가 옳고 키 관리가 그 비용입니다. 하지만 들어오는 토큰을 저장된 것과 대조하기만 한다면, 무엇도 복구할 필요가 없고, 암호화는 지켜야 할 키를 더하는 과잉입니다. 토큰의 해시를 저장하고 해시를 비교하세요. 그리고 질문이 "이 메시지가 주장하는 사람에게서, 변조 없이 왔는가?"라면, 답은 보통 HMAC 입니다. 웹훅 페이로드 같은 메시지를 인증하도록 만들어진 키 있는 해시이지, 암호화가 전혀 아닙니다.

웹 앱에서 각자가 쓰이는 곳

  • 인코딩 — JSON이나 URL용 바이너리 직렬화, data URI 구성, 다이제스트를 표시용 16진수로 렌더링. Base64는 암호화가 아니다 라는 구분은 익혀둘 가치가 있습니다. 토큰과 페이로드 어디에나 있고, 아무것도 지키지 않습니다.
  • 암호화 — 다시 읽어야 하는 저장 데이터(PII 컬럼, 암호화된 백업), 전송 중 데이터(TLS), 평문으로 복구해야 하는 토큰. 항상 키 관리 이야기와 짝을 이룹니다.
  • 해싱 — 비밀번호 저장(원시 빠른 해시가 아니라 느린 KDF + 솔트로), 무결성 검사, 중복 제거, 조회 키, 그리고 메시지 인증용 HMAC.

머릿속 지름길입니다. 키가 없으면 인코딩이거나 해싱이고, 아무것의 기밀성도 보호하지 않습니다. 원본이 되돌아와야 하면 인코딩이거나 암호화이지 결코 해싱이 아닙니다. 그리고 비밀번호를 저장한다면 범용 도구는 어느 것도 맞지 않습니다. 목적에 맞게 만들어진 KDF로 손을 뻗으세요.

디버깅하며 다이제스트를 계산하거나 비교해야 할 때, 우리 해시 생성기는 SHA-256과 그 밖의 다이제스트를 브라우저에서 만들어, 셸로 무언가를 파이프하지 않고도 체크섬을 검증하거나 두 값이 맞는지 점검할 수 있게 합니다.