한국어

인코딩, 이스케이프 및 해싱

Base64는 암호화가 아니고, btoa는 UTF-8가 아니고, encodeURI는 encodeURIComponent가 아니고, SHA-256는 비밀번호 해시가 아닙니다. 다음은 이들 각각이 실제로 수행하는 작업과 다르게 가정할 때 발생하는 구체적인 실수입니다.

인코딩은 암호화도 아니고 압축도 아닙니다

인코딩은 데이터가 기록되는 방식을 변경합니다. 암호화를 읽을 수 있는 사람이 변경됩니다. 압축은 차지하는 공간의 양을 변경합니다. 이것들은 세 가지 다른 작업이며 base64는 첫 번째 작업만 수행합니다. 다른 두 가지 중 하나를 원했다면 안타깝습니다.

Base64는 한 번에 3바이트를 가져와 이를 64 기호 알파벳에서 가져온 4개의 문자로 다시 씁니다. 3바이트를 전달하는 4개의 문자는 출력이 항상 입력보다 약 33% 더 크고 패딩이 더 크다는 것을 의미합니다. 이는 이메일 헤더, HTTP 헤더, JSON 문자열 값, URL, XML 속성 등 많은 인프라가 텍스트용으로 설계되어 임의 바이트를 변조하거나 거부하기 때문에 존재합니다. Base64는 텍스트 모양의 파이프를 통해 바이트를 푸시할 수 있는 어댑터입니다.

열쇠가 없기 때문에 누구나 열쇠 없이 즉시 되돌릴 수 있습니다. base64로 비밀번호를 입력했다면 약간 불편한 형식으로 비밀번호가 게시된 것입니다. 이는 Base64 출력이 인간의 눈에 뒤죽박죽으로 보이기 때문에 중요합니다. 이는 사람들이 할 수 없는 일에 대해 신뢰하게 만드는 특성입니다.

btoa()가 중단되는 이유와 중단되는 두 가지 방법

브라우저는 btoa() 및 atob()을 제공하며 이는 최신 텍스트 API보다 오래되었습니다. btoa는 "이진 문자열"에 대해 정의됩니다. 즉, 모든 코드 단위가 0에서 255까지 단일 바이트인 문자열입니다. 문자는 그렇지 않습니다.

첫 번째 실패는 시끄럽습니다. btoa("세계")를 호출하면 U+4E16이 바이트에 맞지 않기 때문에 InvalidCharacterError가 발생합니다. 시끄러운 실패는 좋은 종류입니다. 즉각적으로 이를 알아차리고 해결책을 찾으러 갑니다.

두 번째 실패는 침묵하며 생산에 도달하는 것입니다. 문자 é는 U+00E9이며 이는 바이트에 맞습니다. 따라서 btoa("café")는 é를 단일 바이트 0xE9로 인코딩하여 행복하게 반환합니다. 그러나 UTF-8의 é는 2바이트, 0xC3 0xA9입니다. 방금 생성한 base64는 지구상의 다른 모든 시스템에서 텍스트가 아닌 것으로 디코딩됩니다. 데이터베이스의 이름이 대체 문자로 바뀌면 몇 주 후에 알게 될 것입니다.

해결 방법은 텍스트를 바이트로 처리하는 것을 중단하고 명시적으로 변환하는 것입니다. TextEncoder는 UTF-8 bytes를 생성합니다. 그것을 인코딩합니다. TextDecoder는 바이트를 다시 텍스트로 바꾸고 { fatal: true }로 구성하면 U+FFFD를 조용히 대체하는 대신 유효하지 않은 시퀀스를 발생시키므로 그럴듯해 보이는 넌센스를 반환하는 대신 정확할 수 없는 디코드가 실패합니다. 이것이 이 툴킷이 사용하는 파이프라인이므로 이모티콘, 표시 결합 및 오른쪽에서 왼쪽으로 쓰는 스크립트가 모두 정확하게 왕복되는 이유입니다.

  1. TextEncoder를 사용하여 텍스트를 바이트로 변환합니다. 절대로 문자열에 색인을 추가하지 마세요.
  2. 바이트를 base64로 인코딩합니다.
  3. 역방향: base64를 바이트로 디코딩한 다음 해당 바이트를 fatal: true를 사용하여 UTF-8로 디코딩합니다.
  4. UTF-8 단계가 실패하면 페이로드는 텍스트가 아닌 바이너리입니다. 척하기보다는 16진수로 보여주세요.

base64 대 base64url 및 패딩 질문

표준 base64는 마지막 두 기호로 + 및 /를 사용합니다. 둘 다 URL에서 의미가 있습니다. +는 쿼리 문자열에서 인코딩된 공백으로 읽을 수 있고 /는 경로 구분 기호입니다. 따라서 RFC 4648는 -와 _를 대신하는 두 번째 알파벳인 base64url을 정의합니다. JWT는 대부분의 토큰 형식 및 많은 API와 마찬가지로 이를 사용합니다.

패딩은 또 다른 변수입니다. 표준 base64 패드에는 =가 있으므로 출력 길이는 항상 4의 배수입니다. base64url은 일반적으로 패딩을 삭제합니다. 왜냐하면 = 자체가 URL에서 어색한 문자이고 길이가 산술적으로 복구될 수 있기 때문입니다. 패딩을 요구하는 디코더는 완벽하게 유효한 JWT 세그먼트를 거부합니다.

실용적인 조언: 디코더는 두 알파벳을 모두 수용하고 패딩 누락을 허용해야 합니다. 왜냐하면 전달받은 내용을 거의 제어할 수 없기 때문입니다. 수신자가 관심을 가질 수 있으므로 인코더는 무엇을 내보내는지 명시해야 합니다. 여기서 base64 유틸리티는 바로 그 일을 합니다. 즉, 합리적인 모든 것을 받아들이고 그것이 생성하는 것을 정확하게 선택할 수 있게 해줍니다.

encodeURI 및 encodeURIComponent: 한 문장의 차이점

둘 다 UTF-8를 사용하여 퍼센트 인코딩됩니다. 그것들은 어떤 문자를 남겨두는지만 다르며 그 차이점이 전체 이야기입니다. encodeURIComponent는 예약된 구분 기호를 이스케이프하지만 encodeURI는 그렇지 않습니다.

예약된 구분 기호는 URL에 구조를 제공하는 문자입니다: : / ? # [ ] @ ! $ & ' ( ) * + , ; =. encodeURI는 이미 올바르게 구조화된 URL을 전달했다고 가정하고 그 상태를 유지해야 합니다. 즉, https://를 https%3A%2F%2F로 바꾸지 않습니다. encodeURIComponent는 슬롯에 떨어질 조각 하나를 건네줬다고 가정하고 조각이 슬롯에서 빠져나오지 못하도록 이스케이프 처리합니다.

이로 인해 발생하는 버그는 완전히 기계적입니다. a&b=c의 검색 값을 사용합니다. encodeURI로 인코딩하고 ?q=a&b=c로 추가하면 두 개의 매개변수가 자동으로 생성됩니다. q는 이제 "a"이고 길 잃은 b=c가 나타났습니다. encodeURIComponent로 인코딩하면 ?q=a%26b%3Dc, 하나의 매개변수, 올바른 값을 얻게 됩니다. 동일한 종류의 버그로 인해 조작된 값이 코드가 작성하는 URL에 매개변수를 삽입할 수 있습니다. 이것이 바로 "값에 구성요소 양식 사용"이 단순한 정확성이 아니라 보안 규칙인 이유입니다.

양식 인코딩은 두 번째 규칙과 비슷한 세 번째 규칙입니다. application/x-www-form-urlencoded는 %20 대신 +로 공백을 씁니다. 일반 decodeURIComponent를 사용하여 양식 본문을 디코딩하는 경우 데이터의 모든 더하기 기호는 공백이 됩니다. "C++"를 "C"로 변환한 모든 검색 상자는 이 버그입니다.

HTML 엔터티와 innerHTML로 디코딩하는 것이 나쁜 습관인 이유

HTML에 대한 이스케이프는 범위가 좁고 잘 이해됩니다. &는 &amp;이 되고, <는 &lt;이 되고, >는 &gt;이 되고, 내부 속성 값 " 및 '도 이스케이프가 필요합니다. 5자입니다. 그 이상 이스케이프하는 것(모든 악센트 문자를 명명된 엔터티로 바꾸는 것)은 불확실한 문자 인코딩 시대에 대한 해결 방법이었으며 이제는 안전보다는 선택적인 스타일입니다.

디코딩은 나쁜 습관이 사는 곳입니다. 모든 답변에 나타나는 한 줄 요령은 분리된 요소의 innerHTML에 문자열을 할당하고 해당 textContent를 다시 읽는 것입니다. 효과가 있지만 좋지 않은 생각입니다. 실제 DOM 노드를 구축하는 HTML 파서에 신뢰할 수 없는 입력을 전달했습니다. 해당 문자열의 <img src=x onerror=...>는 실제 오류 처리기가 연결된 실제 이미지 요소가 됩니다. 해당 하위 트리가 문서에 삽입되면 실행됩니다. 또한 데이터를 자동으로 파괴합니다. 파서가 태그를 텍스트가 아닌 마크업으로 해석하기 때문에 입력의 태그는 왕복 대신 사라집니다.

엔터티를 제대로 디코딩하려면 파서가 전혀 필요하지 않습니다. 즉, 참조를 일치시키거나, 테이블에서 이름을 찾거나, 숫자 참조에 대한 산술을 수행합니다. 그것은 수십 줄이고 아무것도 실행할 수 없으며 충실하게 왕복합니다. 이 툴킷은 그런 방식으로 수행하므로 스크립트 태그를 엔터티 디코더에 붙여넣으면 스크립트 태그가 표시됩니다.

해시 선택과 이를 결정하는 세 가지 질문

암호화 해시는 모든 입력을 고정 길이 다이제스트로 변환하므로 다이제스트가 동일한 두 입력을 찾는 것이 불가능합니다. 이 속성은 서명, 무결성 검사 또는 콘텐츠 주소에서 다이제스트가 데이터를 대신할 수 있도록 하는 것입니다.

첫 번째 질문: 사고로부터 보호하고 있습니까, 아니면 적으로부터 보호하고 있습니까? 손상된 다운로드를 방지하는 체크섬은 무작위 반전만 포착하면 됩니다. CRC32는 괜찮습니다. 공격자가 충돌을 통해 이익을 얻을 수 있는 다이제스트에는 여전히 남아 있는 해시가 필요합니다. 이러한 구별은 SHA-1가 단순히 "old"가 아닌 이유입니다.

SHA-1이 손상되었습니다. 2017에서 SHAttered 작업은 동일한 SHA-1 다이제스트를 사용하여 두 개의 서로 다른 PDF 파일을 생성했습니다. 2020에서 "SHA-1 is a Shambles"는 선택된 접두사 충돌을 시연했습니다. 이는 공격자가 신중하게 구성된 두 개의 blob이 아닌 두 개의 의미 있는 서로 다른 문서를 충돌시킬 수 있기 때문에 더 강력하고 훨씬 위험한 변종입니다. 시스템의 보안이 SHA-1 충돌 저항에 의존하는 경우 해당 보안은 사라집니다. SHA-1은 git 객체 ID와 레거시 API 서명의 롱테일에서 계속 사용하고 해당 값을 재현할 수 있어야 하기 때문에 이 툴킷에 남아 있습니다. 가치를 재현하는 것은 그것에 의존하는 것과는 다릅니다.

두 번째 질문: 입력한 내용이 비밀번호입니까? 그렇다면 이 중 어느 것도 답이 아닙니다. SHA-256는 빠르도록 설계되었으며, 빠른 것은 비밀번호에 있어서 정확히 잘못된 것입니다. 이는 데이터베이스를 공격하는 공격자가 초당 수십억 번의 추측을 시도할 수 있음을 의미합니다. 비밀번호에는 Argon2id, scrypt 또는 bcrypt와 같은 사용자별 솔트를 사용하여 의도적으로 느리고 메모리 하드 기능이 필요합니다. 이것은 뉘앙스가 아닙니다. 비밀번호로 SHA-256를 사용하는 것은 가장 흔한 심각한 해싱 실수입니다.

세 번째 질문: 키가 있는 다이제스트가 필요합니까? 지문을 채취하는 대신 메시지를 인증하는 경우 베어 해시가 아닌 HMAC가 필요합니다. 비밀을 연결하고 해싱하는 것은 길이 확장 공격에 대한 고전적인 자체 목표입니다. HMAC는 보기보다 제대로 구축하기가 어렵기 때문에 존재합니다.

그 밖의 모든 것(파일 지문, 콘텐츠 주소, 무결성 속성)의 경우 SHA-256이 합리적인 기본값이며 SHA-512은 더 넓은 다이제스트를 제공하면서 64비트 하드웨어에서 더 빠른 경우가 많습니다.

여기 해시가 브라우저에서 나오는 이유

이 툴킷의 다이제스트는 이 사이트에서 제공되는 JavaScript가 아닌 브라우저의 자체 웹 암호화 구현인 SubtleCrypto에 의해 계산됩니다. 이는 의도적인 선택입니다. 브라우저의 구현은 감사되고 유지되며 일반적으로 최적화된 네이티브 코드로 실행됩니다. 페이지 번들에 직접 작성한 SHA-256는 아무런 이점도 없이 신뢰할 수 있는 코드입니다.

여기에는 한 가지 눈에 띄는 결과가 있습니다. Web Crypto는 https:// 또는 localhost를 의미하는 보안 컨텍스트에서만 노출됩니다. LAN 주소의 일반 HTTP를 통해 이 페이지를 열면 crypto.subtle가 정의되지 않으므로 해시 유틸리티는 자동으로 실패하거나 더 약한 것으로 대체하는 대신 명확하게 알려줍니다.

동일한 추론이 UUID 생성기를 구동합니다. crypto.randomUUID()는 또한 보안 컨텍스트 전용이므로 이를 사용할 수 없는 경우 툴킷은 여전히 동일한 암호화 보안 소스인 crypto.getRandomValues()로 대체되고 버전 및 변형 비트 자체를 설정합니다. 결코 하지 않을 일은 Math.random()으로 돌아가는 것입니다. 이는 짧은 출력에서 내부 상태를 복구할 수 있는 빠른 비암호화 PRNG이며 식별자는 세션 키 및 비밀번호 재설정 링크로 승격되는 불행한 습관이 있습니다. 보안 소스가 없으면 이 도구는 아무것도 생성하지 않고 그 이유를 알려줍니다.

붙여넣은 내용은 어떻게 되나요?

  • 모든 변환, 해시, 디코드 및 차이점은 브라우저 탭에서 실행됩니다. 페이지가 로드된 후에는 관련된 서버가 없기 때문에 입력이 서버에 업로드되거나 기록되거나 저장되지 않습니다.
  • 해시는 브라우저의 자체 웹 암호화 구현에서 나오고 UUID는 암호화된 보안 무작위 생성기에서 나옵니다. 둘 다 네트워크 호출과 관련이 없습니다.
  • 귀하가 입력하는 내용은 로컬 저장소나 쿠키에 기록되지 않습니다. 페이지를 다시 로드하면 페이지가 삭제됩니다. 탭을 닫으면 삭제됩니다.
  • 사이트 전체 분석은 구성된 정식 프로덕션 호스트에서만 실행되며 개인정보 보호정책에 공개됩니다. 로컬 및 미리보기 호스트는 이를 거부합니다. 붙여넣은 값, 토큰, URL 및 파일 내용은 ToolAcre 자체 분석 이벤트에서 제외됩니다. 현재 구성에서는 광고가 비활성화되어 있습니다.
  • 즉, JWT 또는 API 키는 실시간 자격 증명입니다. 안전한 습관은 자신이 작성하지 않은 웹 페이지에 내용을 붙여넣지 않는 것입니다. 이 내용을 포함하여 그 주장은 신뢰할 만합니다.

질문

base64가 데이터를 숨기는 방법인가요?

아니요. 키가 없는 되돌릴 수 있는 텍스트 표현이며, 누구나 단 몇 분의 1초 안에 해독할 수 있습니다. 데이터가 텍스트 전용 채널에서 살아남도록 합니다. 비밀로 하지 않습니다. 정말로 민감한 것은 암호화가 필요하며, 암호화된 결과는 종종 전송을 위해 base64로 인코딩되는데, 이것이 혼란의 원인입니다.

내 base64가 입력보다 긴 이유는 무엇입니까?

4개의 출력 문자가 3개의 입력 바이트를 전달하므로 출력은 대략 4/3 크기에 최대 2개의 패딩 문자를 더한 크기입니다. 이는 형식에 내재되어 있습니다. 크기가 중요하다면 인코딩하기 전에 압축하십시오. 인코딩 후에는 압축하지 마십시오. base64 출력이 제대로 압축되지 않기 때문입니다.

어떤 URL 인코딩 기능을 사용해야 합니까?

URL에 삽입하는 단일 조각(쿼리 값, 경로 세그먼트, 조각)에는 encodeURIComponent를 사용하세요. 공백이나 비ASCII만 포함하는 이미 구조화된 전체 URL이 있는 경우에만 encodeURI를 사용하세요. 쿼리 문자열을 작성하는 경우 올바른 규칙을 적용하고 공백을 더한 차이를 처리하는 URLSearchParams를 선호합니다.

내 디코더에서 "URI 형식이 잘못되었습니다"가 발생하는 이유는 무엇입니까?

입력의 % 뒤에 두 개의 16진수가 나오지 않기 때문입니다. 일반적으로 텍스트에는 인코딩되지 않은 리터럴 백분율 기호("50% off")가 포함되어 있습니다. 리터럴 백분율은 %25로 작성해야 합니다. 여기의 URL 유틸리티는 단순히 거부하는 대신 문제가 되는 이스케이프의 정확한 위치를 보고합니다.

SHA-256를 사용하여 비밀번호를 저장할 수 있나요?

아니요. SHA-256는 설계상 빠르므로 데이터베이스를 훔치는 공격자가 상용 하드웨어에서 초당 수십억 개의 후보 비밀번호를 테스트할 수 있습니다. 비밀번호에는 Argon2id, scrypt 또는 bcrypt와 같이 느리고 메모리 하드하며 솔트 처리된 기능이 필요합니다. 이것은 이 분야에서 가장 흔한 심각한 실수입니다.

SHA-1가 깨졌는데 왜 여전히 여기에 있나요?

이미 존재하는 SHA-1 값(git 객체 ID, 이전 TLS 인증서 지문, 레거시 API 요청 서명)을 재현해야 하기 때문입니다. 상호 운용성의 가치를 계산할 수 있다는 것은 보안에 의존하는 것과 다릅니다. 이 툴킷에 나타나는 모든 SHA-1에는 그에 따라 레이블이 지정되어 있습니다.

두 도구가 동일한 텍스트에 대해 서로 다른 해시를 제공하는 이유는 무엇입니까?

거의 항상 알고리즘이 아닌 바이트 수의 차이입니다. 일반적인 원인은 후행 줄 바꿈(파일이 1로 끝나지만 텍스트 상자는 그렇지 않을 수 있음), 다른 텍스트 인코딩 또는 CRLF 대 LF 줄 끝입니다. 이 도구는 사용자가 입력한 내용의 UTF-8 bytes를 해시하고 바이트 수를 표시하므로 일반적으로 불일치가 분명해집니다.

제한사항

  • 명명된 HTML 엔터티 테이블은 마크업에 중요한 문자, 타이포그래피, 통화, 화살표, 수학, 그리스어 및 라틴어(1)와 같은 실용적인 하위 집합을 다룹니다. 모든 2,231 HTML5 명명된 참조는 아닙니다. 인식할 수 없는 이름은 보고되고 추측된 것이 아니라 쓰여진 대로 정확하게 남습니다.
  • 엔터티 디코딩에는 종료 세미콜론이 필요합니다. HTML5는 레거시 참조가 없는 소수의 레거시 참조를 허용하지만 이를 올바르게 디코딩하는 것은 독립형 텍스트 도구에는 없는 주변 마크업 컨텍스트에 따라 달라집니다.
  • 해싱 및 UUID 생성에는 Web Crypto가 노출되지 않으므로 보안 컨텍스트(https:// 또는 localhost)가 필요합니다. 도구는 더 약한 구현을 대체하는 대신 이를 보고합니다.
  • SHA-1, SHA-256, SHA-384 및 SHA-512만 사용할 수 있습니다. 왜냐하면 SubtleCrypto가 구현하는 것이기 때문입니다. MD5는 선택과 필요성에 의해 존재하지 않습니다.
  • 여기에는 HMAC, 키 파생 및 암호화가 없습니다. 여기에는 키 관리가 필요하며 이는 인터넷에서 찾은 페이지가 처리해야 하는 것이 아닙니다.
  • 모든 것이 하나의 브라우저 탭에서 실행되므로 모든 것이 장치의 메모리에 의해 제한됩니다. 입력은 유틸리티당 몇 메가바이트로 제한되며 도구는 동결보다는 과도한 작업을 거부합니다.