Search
🔎

쿠키값 이중 인코딩 이슈 - Base64 특수문자 처리

가. 현상 - 쿠키값 이중 인코딩 로직 발견

1) 기존 방법 - Base64 Encoder + URL Encoder 활용

이중 인코딩 및 디코딩 로직을 발견했습니다. base64 인코딩 후 일부 특수문자 처리를 위해 URL 인코딩을 한 번 더 거칩니다. 디코딩의 경우, URL 디코딩 후에 base64 디코딩 과정을 추가적으로 거칩니다.
이중 인코딩 코드 참고

2) 이중 인코딩 원인 - 특수문자 처리 실패

base64로 단일 인코딩한 쿠키 값이 브라우저에 저장되지 않습니다.
이하 Set-Cookie 필드 참고
HTTP/1.1 200 Vary: origin,access-control-request-method,access-control-request-headers,accept-encoding Set-Cookie: cookie=Sq09f43ep4dxl2gfmsKvfQ==; Domain=.x.me; SameSite=Lax
Plain Text
복사
위의 쿠키값에는 '/''='이 포함되어 있습니다. 참고로 쿠키값에는 앞의 두 개의 특수문자를 포함하여 @, 쉼표, 마침표 등을 허용하지 않습니다.
base64 Index Table을 보면 Index 63에 '/'가 맵핑되고, '='는 인코딩의 끝을 알리는 문자로 사용됩니다.

나. 해결 - 단일 인코딩 대안 및 성능 비교

두 가지 대안 모두 90% 이상의 성능 상 이점이 있었지만, RFC6265을 참고하여 첫번째 대안을 최종적으로 채택했습니다.

1) 대안 1 - Base64 단일 인코딩

Base64 라이브러리에 정의된 Base64.getUrlEncoder().withoutPadding() 로 인코더를 생성하면, 문제가 되는 특수문자를 다른 문자로 대체되어 인코딩됩니다.
예를 들어, getUrlEncoder()'/'_로 대체하고, withoutPadding()'='를 제거하는 옵션이 추가됩니다. 이를 사용하면 쿠키값에 문제가 되는 특수문자가 모두 대체되거나 제거됩니다.
이하의 쿠키값을 보면 문제가 되는 특수문자가 모두 대체되거나 제거된 것을 확인할 수 있습니다.
HTTP/1.1 200 Vary: origin,access-control-request-method,access-control-request-headers,accept-encoding Set-Cookie: cookie=ZM3VpJqGcJVzWp_hPOgclA; Domain=.x.me; SameSite=Lax
Kotlin
복사
Base64 단일 인코딩 코드 참고

2) 대안 2 - URLEncoder 단일 인코딩

URLEncoder만 활용하여 인코딩하는 것이 두번재 대안입니다. URL 인코딩 방식은 unsafe 문자로 분류되는 것에 대해 ‘%xx’의 형태로 인코딩하는 것입니다.
URLEncoder 단일 인코딩 코드 참고

다. Base64 파헤치기

1) Base64란

Base64 인코딩은 binary data를 기본적인 문자만으로 이루어진 문자열로 표현하기 위해 설계되었습니다. 여기서 말하는 기본적인 문자란 대소문자 알파벳, 숫자 0-9, 그리고 세 개의 특수문자(+, /, =)를 의미합니다.
Base64 인코딩의 목적은 binary data가 시스템에 따라 다르게 해석되는 문자로 표현될 경우, 의도하지 않은 동작을 방지하는 것입니다. 시스템별로 다르게 해석되는 문자의 예로 개행문자를 들 수 있습니다. Unix, MacOS, Windows의 개행문자는 각각 \n, \r, \r\n입니다. 다시 말해, ASCII 코드로 플랫폼 간 통신에 사용하는 것은 부작용을 야기하므로 Base64 방식으로 추가적인 인코딩이 필요합니다.
Base64 인코딩 방식은 이메일 등 텍스트 기반 전송 프로토콜을 사용하는 시스템에서 binary data를 추가할 때 사용됩니다. 예를 들어, 이메일에 이미지를 첨부할 때, 이미지는 binary data로 구성되어 있으므로 이를 문자열로 변환한 후에 이메일에 첨부할 수 있습니다. 이 때, Base64 인코딩 방식으로 binary data를 변환한다면 OS별로 다르게 처리될 가능성을 제거할 수 있습니다. 참고로 binary data 전용의 프로토콜에서는 인코딩 과정이 필요 없습니다.
HTTP의 일부 기능에서도 Base64 인코딩을 권장합니다. RFC6265에 따르면 웹 클라이언트의 호환성을 고려하여 서버에서 HTTP Cookie의 값에 대해 Base64로 인코딩하여 전달할 것을 권합니다. 브라우저와 같은 웹 클라이언트에 따라 일부 기능에서 특수문자에 대한 허용정책이 다를 수 있기 때문입니다.
이하 Base64 Index Table

2) 인코딩 절차

Base64 인코딩의 과정은 결과적으로 ASCII 문자를 Base64 기반의 문자로 변경하는 방향으로 동작합니다. 구체적으로 8비트(1바이트)의 데이터를 6비트 단위로 쪼개고, 이를 네 단위(24비트)로 묶는 방식으로 진행됩니다.
인코딩 과정은 다음 네 단계를 거칩니다. 텍스트 콘텐츠 → ASCII Index → 비트 패턴 → Base64 Index → Base64 인코딩
24비트를 모두 채울 수 없는 경우 0과 패딩(=)으로 채워 넣습니다.
이하 Encoding Process 예시 참고

참고자료