Передача строк в кодировке base64 в URL

244

Безопасно ли передавать строки в кодировке base64 через параметры GET?

Аликс Аксель
источник
1
Возможный дубликат
пиксель
4
Нет, это не так - связанный вопрос более новый. Так что это делает связанный вопрос дубликатом этого ...
Серж

Ответы:

206

Нет, вам нужно будет его кодировать по URL-адресу, поскольку строки base64 могут содержать символы «+», «=» и «/», которые могут изменить значение ваших данных - они выглядят как подпапка.

Допустимые символы base64 приведены ниже.

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
Thiyagaraj
источник
4
URL-кодирование - пустая трата пространства, тем более что само base64 оставляет много символов неиспользованными.
Михал Гурни,
21
Я не уверен, что понимаю, что вы говорите - кодировка URL не изменит никаких символов, кроме последних трех символов в приведенном выше списке, и это предотвратит их неверную интерпретацию, поскольку они имеют другие значения в URL-адресах. То же самое касается base64, исходные данные могут быть двоичными или что-то еще, но они кодируются в форме, которая может быть легко передана с использованием простых протоколов.
Тиягарадж
3
Во-первых, вы также должны экранировать «+», так как он может быть преобразован в пространство. Во-вторых, есть как минимум несколько символов, которые безопасны для использования в URL и не используются в «стандартной» кодировке. Ваш метод может даже увеличить размер передаваемых данных три раза в определенных ситуациях; Заменив эти символы другими, мы добьемся успеха, сохранив ту же длину. И это вполне стандартное решение тоже.
Михал Гурни
8
en.wikipedia.org/wiki/Base64#URL_applications - в нем четко сказано, что экранирование «делает строку излишне длиннее» и упоминается альтернативный вариант кодировки.
Михал Гурни
1
Благодаря этому ответу я поставил диагноз своей проблеме именно так, как она и была упомянута. Некоторые из базовых 64 символов (+, /, =) были изменены из-за обработки URL. Когда я URL закодировал строку base 64, проблема была решена.
Чак Круцингер
272

Есть дополнительные спецификации base64. (Смотрите таблицу здесь для уточнения). Но по сути вам нужно 65 символов для кодирования: 26 строчных + 26 прописных + 10 цифр = 62.

Вам нужно еще два ['+', '/'] и дополнительный символ '='. Но ни один из них не является дружественным к URL, поэтому просто используйте разные символы для них, и все готово. Стандартными из приведенного выше графика являются ['-', '_'], но вы можете использовать другие символы, если вы расшифровали их одинаково, и вам не нужно делиться с другими.

Я бы порекомендовал просто написать своих собственных помощников. Как это из комментариев на странице руководства php для base64_encode :

function base64_url_encode($input) {
 return strtr(base64_encode($input), '+/=', '._-');
}

function base64_url_decode($input) {
 return base64_decode(strtr($input, '._-', '+/='));
}
Джо Флинн
источник
53
Отличное решение, за исключением того, что запятая не зарезервирована в URL. Я рекомендую использовать «~» (тильда) или «.» (точка) вместо.
kralyk
11
@kralyk: я рекомендую использовать только то, urlencodeчто предложено ответом Родриго-Силвейры. Создание двух новых функций для сохранения нескольких символов в длине URL, это все равно что войти в свой дом, проходя через окно, а не просто используя дверь.
Марко Демайо
5
@MarcoDemaio, не зная, как он будет использоваться, невозможно сказать, что это всего лишь несколько символов. Каждый закодированный символ будет иметь тройную длину, и почему бы "+++ ..." не быть допустимой строкой base64? URL-адреса имеют ограничения браузера, и утроение URL-адреса может привести к достижению этих ограничений.
Leewz
10
@RandalSchwartz тильды есть URL-сейф. Из RFC3986:unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
kralyk
3
Так как ,должен быть urlencoded к %2C, я предлагаю использовать ._- вместо -_,единственного варианта в en.wikipedia.org/wiki/Base64#Variants_summary_table, который сохраняет трейлинг =
PaulH
75

@joeshmo Или вместо написания вспомогательной функции, вы можете просто urlencode строки в кодировке base64. Это будет делать то же самое, что и ваша вспомогательная функция, но без необходимости использования двух дополнительных функций.

$str = 'Some String';

$encoded = urlencode( base64_encode( $str ) );
$decoded = base64_decode( urldecode( $encoded ) );
Rodrigo-Силвейра
источник
2
Результат не совсем то же самое. urlencode использует 3 символа для кодирования недопустимых символов, а решение joeshmo использует 1. Это не большая разница, но это все еще пустая трата времени.
Йозеф Борковец
1
@JosefBorkovec Действительно? Тогда это также будет означать, что одинаковое количество байтов, закодированных base64-> url->, может иметь различную результирующую длину, в то время как другое решение дает предсказуемую длину, верно?
человечествоANDpeace
@humanityANDpeace Да, urlencode - дерьмовое решение, потому что оно утраивает размер некоторых строк base64. Вы также не можете повторно использовать буфер, так как вывод больше, чем ввод.
Навин
4
Расширение с 1 до 3 символов происходит в среднем на 3 из 64 символов, так что это накладные расходы на 9% (2 *
3/64
Будьте осторожны с /символом, если вы передаете его не как параметр GET, а как путь в URL. Это изменит ваш путь, если вы не замените /что-то еще с обеих сторон.
NeverEndingQueue
41

Вводное примечание Я склонен опубликовать несколько разъяснений, поскольку некоторые ответы здесь немного вводят в заблуждение (если не неверно).

Ответ НЕТ , вы не можете просто передать закодированный в base64 параметр в строке запроса URL, поскольку знаки плюс преобразуются в ПРОБЕЛ в глобальном массиве $ _GET. Другими словами, если вы отправили test.php? MyVar = stringwith + sign to

//test.php
print $_GET['myVar'];

результат будет:
stringwith sign

Самый простой способ решить эту проблему - просто urlencode()добавить строку base64 перед добавлением ее в строку запроса, чтобы экранировать символы +, = и / в кодах% ##. Например, urlencode("stringwith+sign")возвращаетstringwith%2Bsign

Когда вы обрабатываете действие, PHP автоматически расшифровывает строку запроса, когда она заполняет глобальную переменную $ _GET. Например, если я отправил test.php? MyVar = stringwith% 2Bsign в

//test.php
print $_GET['myVar'];

результат будет:
stringwith+sign

Вы не хотите urldecode()возвращать строку $ _GET, так как + будут преобразованы в пробелы.
Другими словами, если я отправил тот же test.php? MyVar = stringwith% 2Bsign в

//test.php
$string = urldecode($_GET['myVar']);
print $string;

результат неожиданный:
stringwith sign

Это было бы безопасно для rawurldecode()ввода, однако, это было бы излишним и, следовательно, ненужным.

Джефори Дж. Беккерс
источник
1
Хороший ответ. Вы можете использовать код PHP без начального и конечного тегов на этом сайте, если вопрос помечен php (также чаще всего это ясно из контекста вопроса). Если вы добавите два пробела в конце строки, вы увидите <br>, поэтому не нужно вводить много HTML. Надеюсь, это поможет, я немного отредактировал ваш ответ, чтобы еще лучше его улучшить.
Хакре
Спасибо, что упомянули, что PHP декодирует URL для вас. Это спасает меня от падения в кроличью нору.
Cocest
Отличный ответ -> Вы не хотите, чтобы urldecode () возвращал строку $ _GET, так как + будет преобразован в пробелы. Впрочем, было бы безопасно использовать rawurldecode () для ввода
MarcoZen
14

Да и нет.

Базовый набор символов base64 может в некоторых случаях вступать в противоречие с традиционными соглашениями, используемыми в URL. Но многие реализации base64 позволяют изменить кодировку, чтобы она лучше соответствовала URL-адресам, или даже поставляются с ней (например, в Python urlsafe_b64encode()).

Другая проблема, с которой вы можете столкнуться, - это ограничение длины URL или, скорее, отсутствие такого ограничения. Поскольку в стандартах не указана максимальная длина, браузеры, серверы, библиотеки и другое программное обеспечение, работающее по протоколу HTTP, могут определять свои собственные ограничения. Вы можете взглянуть на эту статью: Часто задаваемые вопросы WWW: Какова максимальная длина URL?

Михал Гурны
источник
8

Это кодировка base64url, которую вы можете попробовать, это просто расширение кода Joeshmo, описанного выше.

function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

function base64url_decode($data) {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
Энди
источник
Это работает для данных, закодированных с помощью JavaBase64.getUrlEncoder().withoutPadding().encodeToString()
4

Я не думаю, что это безопасно, потому что, например, символ "=" используется в raw base 64 и также используется для дифференциации параметров от значений в HTTP GET.

Mischa
источник
1

Теоретически, да, если вы не превышаете максимальную длину URL-адреса и / или строки запроса для клиента или сервера.

На практике все может стать немного сложнее. Например, он может вызвать исключение HttpRequestValidationException в ASP.NET, если значение содержит «on» и вы оставляете в конце «==».

Николь Калиною
источник
вы не упоминаете символы +, / или =, которые в некоторых случаях делают URL недействительными.
Уилл Бикфорд
0

Для безопасного URL-кодирования, как base64.urlsafe_b64encode(...)в Python код ниже, работает для меня на 100%

function base64UrlSafeEncode(string $input)
{
   return str_replace(['+', '/'], ['-', '_'], base64_encode($input));
}
Игорь Сазонов
источник
-10

Да, это всегда безопасно. конечно base64 содержит: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= но строка в кодировке base64 обычно не имеет +. +будет преобразован в пустое пространство, что приведет к неправильной декодированной строке. /безопасен в паре параметров получения. =всегда находится в конце строки, закодированной в base64, и серверная сторона может разрешить =напрямую.

gouchaoer
источник
Я предполагаю, что это правильно, поскольку эксперименты, которые я провел с кодировкой base64 (без URL-кодирования), были успешными, но мне интересно, есть ли какая-нибудь документация, которую вы могли бы предоставить, чтобы поддержать это?
Шон Боб
1
вы говорите «всегда безопасно», но потом говорите «обычно нет +». Так что ты сам себе противоречишь. Знак + сшивается, чтобы вызвать проблемы, если он у вас есть в строке base64.
Ник Хамрич