슬래시는 언제 구분자이고 언제 인코딩한 값의 일부일까?
웹 주소는 구조가 있는 문자열이다. 구분 역할의 문자와 데이터의 문자가 있고 퍼센트 기호와 16진수로 바이트를 표현한다. 그래도 소프트웨어는 어떤 부분을 언제 해독할지 결정해야 한다. 읽기 쉬운 표시가 그 차이를 가릴 수 있다.
선명한 표시는 완전한 해석이 아니다.
인코딩은 비밀이 아니다
%20은 URL 구성 요소에서 흔히 공백 바이트를 나타내며 읽는 데 키가 필요 없다. 퍼센트 인코딩은 암호화나 목적지 신뢰의 증거가 아니다. 이스케이프가 많은 주소는 평범한 데이터일 수 있고 짧고 예쁜 주소도 부적절한 요청으로 이어질 수 있다.
원문, 파싱한 구성 요소, 특정 요소의 해독 표시를 구별하자. 이를 같은 것으로 다루면 예상 밖 결과가 생긴다. 슬래시는 보통 경로를 나누지만 인코딩한 슬래시는 서버와 라우팅 계층마다 다르게 처리할 수 있다. 외형보다 실제 처리 시스템이 의미를 정한다.
한 번 해독하면 같아지는 경로
로컬 예시는 https://example.com/a%2Fb와 https://example.com/a/b다. 원래 경로는 다르지만 첫 경로를 한 번 일반적으로 해독하면 /a/b로 같아진다. 두 호스트는 example.com이다. 문자열 결과일 뿐 실제 서버에서 같은 자원을 반환한다는 증거는 아니다.
다른 예시는 example.com 주소의 쿼리에 인코딩된 example.org 주소를 넣는다. 바깥 호스트는 여전히 example.com이다. 안쪽 주소는 앱이 용도를 정하기 전에는 쿼리 데이터이며 나중에 이동에 쓰이거나 다른 용도일 수 있다. 어느 목적지에도 접속하지 않았다.

두 번 해독은 다른 작업이다
/%252F를 한 번 해독하면 %25가 퍼센트가 되어 /%2F가 된다. 두 번째에는 슬래시 두 개가 된다. 서둘러 같은 표현이라 생각하기 쉽지만 문자열은 다르다. 예상 밖으로 두 번 해독하는 프로그램은 한 번만 하는 프로그램과 다르게 해석할 수 있다.
고정 문자열과 표준 라이브러리로 설명했으며 실제 공격을 만들지 않았다. 브라우저와 프록시, 프레임워크, 서버의 규칙은 다르다. URL을 보기 좋게 만드는 함수가 보안 필터가 되는 것은 아니다. 동등성에는 실제 동작과 명확한 정책이 필요하다.
독자에게는 다른 경로가 쉽다
비밀번호를 입력할지 정하기 전에 복잡한 주소를 직접 해독할 필요는 없다. 계정 문제를 주장하는 메시지는 평소 앱이나 북마크에서 확인한다. 익숙한 호스트뿐 아니라 요청 행동과 도착 경로도 중요하다. HTTPS가 모든 요구를 적절하게 만들지는 않는다.
주소를 본다면 전체에서 익숙한 단어를 찾기보다 실제 스킴과 호스트부터 읽자. 목적지처럼 보이는 부분이 경로나 쿼리에만 있는지 살핀다. 단축 링크와 리디렉션은 문자만으로 해결할 수 없는 다음 단계를 더한다. 중요한 행동 앞에서 불확실하면 멈출 만하다.

개발자는 문맥을 보존하자
임시 문자열 치환 대신 검증된 URL과 프레임워크 API를 사용한다. 검증과 비교는 최종 소비 구성 요소의 해석과 맞아야 한다. 한 표현을 검사하고 다른 계층이 다른 표현으로 행동하면 틈이 생긴다. 이 글은 배포할 허용 목록이나 정규화 알고리즘을 제공하지 않는다.
일반 독자에게 교훈은 간단하다. 형식이 복잡할 수는 있어도 불투명한 요청을 믿도록 압박해서는 안 된다. 중요한 서비스에 독립 진입 경로를 두자. 링크를 잘 읽는 능력과 직접 모호함을 풀지 않아도 되는 때를 아는 능력 모두 유용하다.
더 읽어 보기
RFC 3986: URI generic syntax ↗Python: URL parsing security ↗WHATWG: URL Standard ↗공식 문서와 함께 읽으면 좋은 자료입니다.
