Строка запроса: проблемы и решения
Строка запроса проста… Нет!
Темы, затронутые в этой статье
- Определение строки запроса
- Спецификации строки запроса
- Проблемы и решения
Определение строки запроса
Строка запроса — удобный и простой способ хранения данных с использованием URI (Uniform Resource Identifier — компактная последовательность символов, идентифицирующая абстрактный или физический ресурс).
Общий синтаксис URI состоит из иерархической последовательности компонентов, называемых схемой, полномочием, путем, запросом и фрагментом. Компонент запроса обозначается первым символом вопросительного знака ("?") и завершается знаком номера ("#") или концом URI.
foo://example.com:8042/over/there?name=ferret#nose \_/ \______________/\_________/ \_________/ \__/ | | | | | scheme authority path query fragment | _____________________|__ / \ / \ urn:example:animal:ferret:nose
Компонент запроса содержит неиерархические данные. Это означает, что в строке запроса не указан путь к ресурсу.
Спецификации строки запроса
Строка запроса является частью синтаксиса URI. Первая спецификация URI rfc1630 была создана в 1994 году сетевой рабочей группой совместно с T. Berners-Lee и был расширен синтаксисом URN в 1997 году. Эта спецификация была пересмотрена и расширена IETF. rfc2986, созданный в январе 2005 года, стал одним из самых актуальных и широко известных стандартов. Этот стандарт неоднократно расширялся:
- Представление идентификаторов зоны IPv6 в адресных литералах и унифицированных идентификаторах ресурсов
- Дизайн URI и право собственности
- Руководство и процедуры регистрации схем URI
- Документ протокола передачи гипертекста
Группа технической архитектуры W3C (основная международная организация по стандартизации Всемирной паутины) также опубликовала несколько документов об URI.
- Спецификация URI
- "Часто задаваемые вопросы"
- Рекомендации
Проблемы и решения
Специальные символы
Дело
Данные кодирования Base64 без процентного кодирования в параметре запроса
Проблема
Конфликтуют символы, закодированные в base64, с символами разделителей URI.
Описание
Например, ваше приложение использует строку запроса в кодировке base64.
import {encode} from 'js-base64'; encode('Георгий'); // 0JPQtdC+0YDQs9C40Lk=
Существует конфликт между символом подразделителя URI «+» и строкой запроса в кодировке base64. Следующий шаг — попытаться разобрать строку запроса:
import {parse} from 'qs'; parse('data=0JPQtdC+0YDQs9C40Lk='); // { data: '0JPQtdC 0YDQs9C40Lk=' }
Строка с пробелом не является строкой base64. В конце концов, давайте выполним atob для этой строки.
import {decode} from 'js-base64'; decode('0JPQtdC 0YDQs9C40Lk='); // Гед`4,�.4.
Непредвиденный!
Решение
Вы должны использовать кодировку base64 для параметров запроса. Давайте прочитаем о кодировании и символах.
Символы и значения URI
URI состоит из ограниченного набора символов, состоящего из цифр, букв и нескольких графических символов. Зарезервированное подмножество этих символов может использоваться для разграничения компонентов синтаксиса в URI, в то время как остальные символы, включая как незарезервированный набор, так и те зарезервированные символы, которые не действуют как разделители, определяют данные идентификации каждого компонента.
Процентное кодирование
Механизм процентного кодирования используется для представления октета данных в компоненте, когда соответствующий символ этого октета находится за пределами допустимого набора или используется в качестве разделителя или внутри компонента.
Зарезервированные символы
URI включают компоненты и подкомпоненты, которые разделены символами в «зарезервированном» наборе. Эти символы называются «зарезервированными», потому что они могут (или не могут) быть определены как разделители общим синтаксисом, синтаксисом каждой конкретной схемы или синтаксисом конкретной реализации алгоритма разыменования URI.
gen-delims = “:” / “/” / “?” / «#» / «[“ / «]» / «@»
sub-delims = “!” / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / «=»
не зарезервировано = АЛЬФА / ЦИФРА / «-» / «.» / «_» / «~»
Незарезервированные символы
Символы, которые разрешены в URI, но не имеют зарезервированного назначения, называются незарезервированными. К ним относятся прописные и строчные буквы, десятичные цифры, дефис, точка, подчеркивание и тильда.
Если данные для компонента URI будут противоречить назначению зарезервированного символа в качестве разделителя, конфликтующие данные должны быть закодированы в процентах до формирования URI.
Base64 имеет потенциальные конфликты с зарезервированными символами URI. Я рекомендую вам использовать безопасное URL-адрес base64: tools.ietf.org.
Обращать внимание:
Эта кодировка не должна рассматриваться как такая же, как кодировка "base64", и не должна упоминаться только как "base64". Если не указано иное, «base64» относится к основанию 64 в предыдущем разделе.
Пакеты NPM для URL-безопасной кодировки base64:
Неожиданные зарезервированные символы
Дело
Использование параметров запроса без процентного кодирования
Проблема
Неожиданные зарезервированные символы
Описание
Как я упоминал выше, спецификация URI предоставляет список зарезервированных и незарезервированных символов. Если данные строки запроса содержат зарезервированные символы, они должны быть закодированы в процентах.
Давайте проверим пример:
axios.get(`/?q=${userInput}`);
Чтобы проиллюстрировать это, просто введите символ решетка (#), и строка запроса станет пустой, поскольку решетка является символом gen-delim (tools.ietf.org). Все данные после символа хэш перемещаются в хеш-значение компонента URI.
new URL(‘http://localhost:3000/?q=#myHashTag'); // {hash: '#myHashTag', search: '?q='}
Решение
Всегда кодируйте параметры запроса:
аксиос
axios({params: {q: '#myHashTag'}}); // q=%23myHashTag
qs
stringify({q: '#myHashTag'}); // q=%23myHashTag
encodeURIComponent
encodeURIComponent('q=#myHashTag'); // q%3D%23myHashTag
Процент закодированных символов URI
Дело
Декодировать параметр запроса base64 с процентом закодированных символов
Проблема
Неожиданные символы в параметрах запроса
Описание
Давайте проверим пример. Знак равенства (=) обычно используется в строке base64. Этот символ должен быть закодирован в процентах, потому что это зарезервированный символ URI:
encodeURIComponent('='); // %3D
Если вы попытаетесь декодировать закодированную в процентах строку с помощью window.atob — будет выброшено исключение:
atob('%3D'); // Uncaught DOMException: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
Тем не менее, js-base64, которая является одной из самых популярных библиотек для base64, не делает этого.
import {decode} from 'js-base64'; decode('%3D'); // �
Решение
В целом не доверяйте библиотекам и убедитесь, что вы используете декодированное значение строки запроса:
qs.parse('data=%3D'); // {data: '='} decodeURIComponent('data=%3D'); // data==
Заключение
"Это не те дроиды, которых вы ищете..."
Простые черты могут принести много неприятностей. После этого вы должны потратить больше времени на изучение документации и исследование наиболее опасных угловых случаев строки запроса.