인코딩, 해싱, 암호화는 서로 다른 세 가지입니다
이 페이지에서 생기는 오해는 거의 모두 이 셋을 같은 것처럼 다루는 데서 나오므로, 제대로 구분해 둘 가치가 있습니다.
인코딩은 되돌릴 수 있으며 비밀이 없습니다. Base64는 이메일 첨부 파일, 데이터 URI, JSON 문자열처럼 텍스트만 받는 통로로 바이너리 데이터를 전달하려고 존재하며, 3바이트를 4글자로 표현합니다. 그래서 결과는 항상 약 3분의 1 커집니다. 키는 없습니다. 문자열을 본 사람은 누구나 이 사이트를 포함해 한 번에 디코딩할 수 있습니다. Base64는 보안이 아니고, 난독화라고 부를 수준도 아니며, 들여다보는 사람에게서 무언가를 숨기는 방법도 아닙니다. 퍼센트 인코딩은 URL을 위한 같은 종류의 작업이며, 또 하나의 흔한 버그가 여기에 있습니다. 쿼리 문자열에 들어갈 값 하나를 인코딩하는 것과 이미 조립된 URL 전체를 정리하는 것은 다른 작업입니다. URL의 구조를 이루는 &, =, ?, /를 이스케이프하는 것은 앞의 작업뿐이기 때문입니다. 이를 잘못하면 “Smith & Sons”가 하나의 값이 아니라 두 개의 매개변수로 서버에 도착합니다.
해싱은 단방향이며 역시 키가 없습니다. 해시는 어떤 입력이든 받아 고정 길이의 지문을 만들고, 되돌릴 방법이 없습니다. 암호화되어서가 아니라 정보 자체가 사라졌기 때문입니다. 할 수 있는 것은 입력을 추측해서 비교하는 것뿐인데, 바로 이 점 때문에 MD5, SHA-1, SHA-256은 비밀번호 저장에 맞지 않습니다. 이들은 빠르고, 그래픽 카드 하나가 초당 수십억 개를 계산하므로, 유출된 해시 테이블은 그 속도로 뚫립니다. 비밀번호에는 일부러 느리게 만든 솔트 적용 함수인 bcrypt, scrypt, Argon2가 필요합니다. 이와 별개로 MD5와 SHA-1은 다른 이유로 깨졌습니다. 해시가 같은 서로 다른 두 파일을 이제 의도적으로 만들 수 있어서, 둘 다 파일이 기대한 그 파일임을 증명하지 못합니다. 다만 다운로드 중 우연히 손상된 파일을 찾아내는 데는 여전히 괜찮습니다.
암호화는 키가 있는 유일한 방식이며, 이 도구들은 암호화를 하지 않습니다. 빠뜨린 것이 아닙니다. 암호화를 제대로 하려면 키 관리가 필요하고, 웹 페이지는 키를 둘 곳이 아닙니다.
JWT는 암호화가 아니라 서명된 것이며, 사람들이 끊임없이 여기서 실수합니다. 토큰의 헤더와 페이로드는 Base64URL이어서, 토큰을 가진 사람은 키 없이 누구나 읽을 수 있습니다. 서명은 토큰을 바꾸는 것을 막을 뿐 읽는 것은 전혀 막지 못하므로, 기밀 정보는 페이로드에 넣으면 안 됩니다. 따라서 토큰을 디코딩해도 그 토큰에 대해 증명되는 것은 전혀 없습니다. 누구든 원하는 클레임을 적어 Base64로 만들 수 있기 때문입니다. 검증이란 발급자의 키로 서명을 다시 계산하는 것이고, 이는 서버가 할 일이며 웹사이트는 절대 그 키를 요구해서는 안 됩니다. 그래서 저희 디코더는 검증된 것처럼 보이게 하지 않으며, 문제를 뜻하는 형태를 표시합니다. 만료된 토큰, 만료 시각이 아예 없는 토큰, 그리고 “none” 알고리즘입니다. “none”은 신기한 현상이 아니라 문서화된 인증 우회 기법입니다.
JSON에는 대부분의 포맷터가 모르는 사이에 걸려 넘어지는 숫자 정밀도 한계가 있습니다. JSON 자체는 숫자 크기에 제한을 두지 않지만, JavaScript의 숫자는 64비트 부동소수점이라 정수는 9,007,199,254,740,991까지만 정확합니다. 트위터 게시물 ID, Discord 스노우플레이크, 64비트 데이터베이스 키, 은행 계좌 번호는 이보다 크며, 흔한 한 줄짜리 포맷터(파싱 후 다시 문자열로 변환)를 거치면 끝자리가 바뀝니다. 결과가 여전히 그럴듯한 숫자로 보인다는 점이 위험합니다. 7205759403792793600이 조용히 7205759403792793000이 됩니다. 저희 JSON 도구는 붙여 넣은 숫자를 자릿수 그대로 다시 출력하고, 보호한 숫자가 몇 개인지 알려 드립니다.
UUID 버전은 무엇을 보장하는지가 아니라 어떻게 만들어졌는지를 나타냅니다. UUID를 등록하거나 고유성을 강제하는 곳은 없습니다. 고유성은 확률적이며, 버전은 비트가 어디서 왔는지를 알려 줄 뿐입니다. 버전 4는 브라우저의 암호학적 난수 생성기에서 얻은 122비트 무작위 값이라 추측할 수 없고, 그래서 비밀번호 재설정 링크, 초대 코드 등 비밀이 필요한 곳에 맞습니다. 반면 데이터베이스 키로는 맞지 않습니다. 무작위 키는 정렬된 인덱스 곳곳에 흩어져 인덱스를 조각내기 때문입니다. 버전 7은 48비트 밀리초 타임스탬프를 앞에 두어, 전역적으로 고유하면서도 자동 증가 정수처럼 인덱스 끝에 차례로 추가됩니다. 대신 v7 UUID는 그것을 가진 누구에게나 생성 시각을 밀리초 단위까지 그대로 드러냅니다.
브라우저가 정말 맞지 않는 경우
이 13가지 도구는 어디로도 아무것도 보내지 않습니다. 요청을 보내지 않고, 아무것도 기록하지 않으며, 네트워크를 끊어도 페이지는 계속 동작합니다. 그래서 고객 기록이 가득한 API 응답이나 이메일로도 보내지 않을 토큰에도 쓸 수 있습니다. 이것이 이 도구들을 써야 하는 솔직한 이유입니다. 이제 쓰지 말아야 할 솔직한 이유를 말씀드립니다.
운영 중인 실제 비밀 값을 웹 페이지에 붙여 넣는 습관은 들이지 않는 것이 좋습니다. 이 페이지도 예외가 아닙니다. 저희의 개인정보 보호 주장은 사실이지만, 여러분을 지키는 것은 주장이 아니라 코드의 실제 동작입니다. 어떤 웹 페이지든 믿기보다 확인하는 것이 유일하게 합리적인 태도입니다. 확인하는 데는 비용이 들지 않습니다. 페이지가 로드된 뒤 인터넷 연결을 끊고 도구가 계속 동작하는지 보세요. 서버가 필요한 도구라면 멈출 것입니다. 그리고 아직 유효한 인증 정보를 이미 검증할 수 없는 곳에 붙여 넣었다면, 따져 볼 것이 아니라 교체하는 것이 올바른 대응입니다. 지금 살아 있는 토큰이라면 사용 중인 언어의 Base64 함수나 셸 명령 두 줄로 로컬에서 디코딩하는 것이 저희를 포함한 어떤 웹사이트보다 좋은 방법입니다.
스키마 검증. 저희 JSON 및 XML 도구는 문서가 올바른 형식인지 확인하며, 이는 스키마에 맞는지와는 다른 문제입니다. JSON Schema나 XSD로 검증하려면 스키마 파일을 가져와 적용해야 하는데, 이는 이 페이지들이 일부러 절대 보내지 않는 네트워크 요청입니다. JSON Schema에는 ajv를, XSD와 DTD에는 xmllint를 쓰세요. 둘 다 무료이고 웹 페이지보다 이 작업을 잘합니다.
정말 큰 데이터나 스트리밍. 문서는 통째로 메모리에 올라가고, 정렬하는 동안에는 두 번 올라가기도 하므로 한계는 탭입니다. 몇 MB는 즉시 처리되지만 몇백 MB는 그렇지 않습니다. jq는 어떤 브라우저도 열지 못하는 수 GB짜리 JSON 파일을 스트리밍으로 처리하고, ripgrep은 파일 하나를 붙여 넣는 것보다 빠르게 저장소 전체를 검색합니다. 해싱에는 같은 한계가 더 뚜렷하게 나타납니다. 브라우저의 암호화 기능에는 점진적 다이제스트가 없어서 파일 전체가 한꺼번에 메모리에 들어가야 합니다. DVD 이미지는 실패하지만, sha256sum, shasum, certutil은 아무렇지 않게 스트리밍으로 처리합니다.
자동화와 CI. 이 도구들은 사람이 클릭할 때 동작합니다. 저장소의 모든 파일 정렬, 테스트 데이터용 UUID 1만 개 생성, 파이프라인에서의 JSON 검사는 스크립트가 할 일입니다. jq, xmllint, uuidgen, openssl, base64 명령은 대부분의 컴퓨터에 이미 설치되어 있습니다.
서명된 것의 검증. 서명 검증, 인증서 체인, 개인 키가 필요한 모든 작업은 유지 관리되는 라이브러리를 써서 서버에서 해야 합니다. 이 페이지는 토큰이 무엇을 주장하는지 알려 드리지만, 토큰이 진짜라고는 절대 말하지 않습니다. 키 없이 그렇게 할 수 있다고 말하는 사이트는 의심하셔야 합니다.
비슷해 보이는 도구 중에서 고르기
JSON 포맷터 vs JSON 유효성 검사. 파서는 같고 질문이 다릅니다. 포맷터는 이미 파싱되는 문서를 읽고 모양을 바꾸는 도구로, 들여쓰기, 압축, 큰 숫자 보존을 해 줍니다. 유효성 검사는 파싱되지 않는 문서를 위한 도구로, 줄, 열, 캐럿으로 표시한 문제 문자, 그리고 흔한 네 가지 원인 중 무엇인지를 알려 줍니다. “Unexpected token”을 보고 있다면 유효성 검사가 필요합니다. XML 포맷터와 XML 유효성 검사도 같은 방식으로 나뉩니다.
Base64 디코딩 vs JWT 디코더. JWT는 Base64URL 세 부분으로 되어 있어서 일반 디코더로도 각 부분을 볼 수 있습니다. JWT 페이지는 이를 나눠 주고, JSON을 정렬하고, 만료(exp)·발급(iat)·유효 시작(nbf) 클레임을 Unix 타임스탬프에서 사용자의 시간대 기준 실제 날짜로 바꾸고, 등록된 클레임에 이름표를 붙이며, 만료와 “none” 알고리즘을 경고합니다. Base64 덩어리에는 일반 디코더를, 토큰에는 JWT 페이지를 쓰세요.
Base64 인코딩 vs URL 인코딩. 둘 다 “안전한” 텍스트를 만들지만 하는 일이 다릅니다. Base64는 임의의 바이트를 33% 크기 증가를 감수하고 텍스트로 바꾸며, 표준 Base64는 URL에서 전혀 안전하지 않습니다. +, /, =가 모두 URL에서 의미를 갖기 때문이고, 그래서 Base64URL이 있습니다. 퍼센트 인코딩은 읽을 수 있는 텍스트는 그대로 두고 URL을 깨뜨릴 부분만 이스케이프합니다. 검색어, 이메일 주소, 리디렉션 대상에 필요한 것이 이것입니다. 파일 전체를 쿼리 문자열용으로 인코딩하고 있다면, 대개는 요청 본문으로 보냈어야 한다는 신호입니다.
해시 생성기 vs UUID 생성기. 해시는 입력에서 만들어지므로 같은 입력은 항상 같은 값을 냅니다. 그래서 지문 역할을 하며, 다운로드 검증이나 동일한 내용의 중복 제거에 좋습니다. UUID는 무엇과도 관계없는 새로운 무작위 값이라 반복되지 않습니다. 같은 내용에 같은 식별자를 원하면 해시를, 새 식별자를 원하면 UUID를 만드세요.
QR 코드 만들기 vs 바코드 만들기. QR은 URL, Wi-Fi 접속 정보, 전화번호 같은 임의의 텍스트를 담는 2차원 격자이며, 휴대폰 카메라로 읽습니다. 바코드 페이지는 유통과 물류에 쓰는 1차원 코드인 EAN-13, EAN-8, UPC-A, ITF-14, Code 128, Code 39를 만듭니다. 유통용 체크 숫자를 계산하고, 틀리거나 짧은 번호는 다른 상품의 유효한 바코드로 몰래 채워 넣지 않고 거부합니다.