같은 바이트가 언제나 같은 문자열이 되게 해, 문자열로 비교할 때와 바이트로 비교할 때 결과가 갈리지 않게 합니다.
base64url은 단순한 표기 방식처럼 보이지만 보안 경계에서는 사정이 다릅니다. 디코더 대부분이 관대해서 서로 다른 문자열이 같은 바이트로 디코딩되고, 그러면 문자열로 비교하는 곳과 바이트로 비교하는 곳의 판단이 엇갈립니다. 디코딩한 값을 다시 인코딩해 원본과 같을 때만 받아들이는 왕복 검사 하나로 이 문제가 닫힙니다.
디코딩한 값을 다시 인코딩해 원본과 같을 때만 통과시켜, 정규형이 아닌 표기를 걸러 냅니다.
마지막 문자에는 쓰이지 않는 비트가 남기 때문에, 서로 다른 문자가 같은 바이트로 디코딩될 수 있습니다.
중복 제거, 캐시 키, 사용 완료 목록이 같은 값을 다르게 세지 않도록 표기를 하나로 고정합니다.
"QQ"와 "QR"은 둘 다 0x41 한 바이트로 디코딩됩니다. 마지막 문자가 담는 6비트 중 실제로 쓰이는 것은 2비트뿐이고 나머지 4비트는 무시되기 때문입니다. 사용 완료 토큰 목록을 문자열로 관리한다면, 공격자는 마지막 글자만 바꿔 같은 토큰을 다시 쓸 수 있습니다. 패딩(=)을 붙이거나 표준 base64 알파벳(+, /)을 섞거나 앞뒤에 공백을 넣는 것도 같은 부류의 문제입니다.
이 규칙은 원래 secret-encryption과 signed-values 안에 따로따로 들어 있었습니다. 흩어져 있으면 새 envelope 형식을 추가할 때 빠뜨리기 쉽고, 그런 누락은 실제로 형식 검증 우회로 이어집니다. 한곳에 모아 두면 새 형식은 이 함수를 쓰기만 하면 됩니다.
아래 증명은 글로 된 설명이 아닙니다. 버튼을 누르면 이 서버가 지금 직접 계산합니다.
base64url은 base64 알파벳의 두 글자를 바꾸고 패딩을 없앤 형식이라 URL에 그대로 넣을 수 있습니다. 규격 벡터를 재현하고, 어떤 바이트열이든 인코딩한 뒤 디코딩하면 원래대로 돌아오는지 확인합니다.
표준 문서에 실린 테스트 벡터를 그대로 넣어 같은 값이 나오는지 확인합니다.
"QQ"와 "QR"은 둘 다 0x41로 디코딩됩니다. 마지막 문자에서 남는 비트를 무시하기 때문입니다. 사용이 끝난 토큰을 문자열로 관리하면 마지막 글자만 바꿔 다시 쓸 수 있습니다. 왕복 검사 하나로 이런 변형을 모두 막습니다.
막아야 할 입력을 실제로 넣고 거부되는지 확인합니다.
규격 원문을 먼저 실었습니다. 사례는 이 분야에서 실제로 보안이 뚫린 일을 다룬 자료입니다.
인코딩의 원문입니다. §10에 시험 벡터가, §3.5에 남는 비트에 관한 규칙이 있습니다.
같은 문제를 JSON에서 푼 사례로, 서명 대상을 왜 정규화해야 하는지 보여 줍니다.
다음 단계
서명 대상이 되는 JSON 구조에 RFC 8785 정규화를 적용할지 검토합니다.