Передача рядків, закодованих base64 в URL


243

Чи безпечно передавати сирі кодовані рядки base64 через параметри GET?



4
Ні його немає - пов'язане питання є новішим. Тож це робить зв'язане питання дублікатом цього ...
сержа

Відповіді:


206

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

Дійсні базові символи нижче.

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

4
URL-кодування є марною витратою місця, тим більше, що саме base64 залишає багато символів невикористаними.
Michał Górny

21
Я не впевнений, що я розумію, що ви говорите - кодування URL-адреси не змінить жодного символу, за винятком останніх трьох символів у наведеному вище списку, і це запобігає неправильній інтерпретації, оскільки вони мають інші значення в URL-адресах. Те саме стосується base64, вихідні дані можуть бути двійковими або будь-якими, але вони кодуються у формі, яку можна легко передавати за допомогою простих протоколів.
Thiyagaraj

3
По-перше, вам також слід уникнути "+", оскільки це може бути перетворено у простір. По-друге, є щонайменше небагато символів, які безпечні для використання в URL-адресах і не використовуються у «стандартній» схемі. Ваш метод навіть може збільшити розмір переданих даних у три рази в певних ситуаціях; в той час як заміни цих символів на якісь інші зробить трюк, зберігаючи однакову довжину. І це цілком стандартне рішення.
Michał Górny

8
en.wikipedia.org/wiki/Base64#URL_applications - там чітко сказано, що епіляція "робить рядок зайвим довше" та згадує альтернативний варіант діаграми.
Michał Górny

1
Через цю відповідь я діагностував свою проблему як саме ту, про яку вона згадувала. Деякі базові 64 символи (+, /, =) були змінені через обробку URL-адрес. Коли я URL-кодував базовий рядок 64, проблема була вирішена.
Чак Крутсінгер

272

Є додаткові специфікації base64. (Деталі див. У таблиці тут ). Але по суті вам потрібно 65 символів для кодування: 26 малих регістрів + 26 великих регістрів + 10 цифр = 62.

Вам потрібні ще два ['+', '/'] та char '='. Але жодна з них не є 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-адресах. Я рекомендую використовувати "~" (tilde) або "." (крапка) натомість.
kralyk

11
@kralyk: Рекомендую використовувати лише те urlencode, що пропонується у відповіді rodrigo-silveira. Створення двох нових функцій для збереження декількох символів у довжині URL-адреси - це як вхід у ваш будинок, що проходить через вікно, а не просто двері.
Марко Демайо,

5
@MarcoDemaio, не знаючи, як він буде використовуватися, неможливо сказати, що це всього лише кілька символів. Кожен закодований символ матиме потрійну довжину, а чому б "+++ ..." не був дійсним рядком base64? В URL-адресах є обмеження для веб-переглядача, і утроювання URL-адреси може призвести до досягнення цих обмежень.
leewz

10
@RandalSchwartz тильди є URL-сейф. З RFC3986:unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
кралик

3
Оскільки ,слід звертатися до коду %2C, я пропоную використовувати ._- замість -_,як єдиний варіант у en.wikipedia.org/wiki/Base64#Variants_summary_table, який зберігає
затримку

75

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

$str = 'Some String';

$encoded = urlencode( base64_encode( $str ) );
$decoded = base64_decode( urldecode( $encoded ) );

2
Результат точно не той. urlencode використовує 3 символи для кодування недійсних символів, а рішення joeshmo використовує 1. Це не велика різниця, але це все-таки марно.
Йозеф Борковець

1
@JosefBorkovec Дійсно? Тоді це також означатиме, що однакова кількість байтів base64-> url->, що кодується, може бути різною різною довжиною, а інше рішення дає передбачувану довжину, правда?
humanityANDpeace

@humanityANDpeace Так, urlencode - це нерозумне рішення, оскільки втричі перевищує розмір певних рядків base64. Ви також не можете повторно використовувати буфер, оскільки вихід більший за вхідний.
Навін

4
Розширення від 1 до 3 символів відбувається в середньому на 3 з 64 символів, тож це 9% накладні витрати (2 *
3/64

Будьте обережні з /символом, якщо ви передаєте його не як параметр GET, а як шлях у URL-адресі. Це змінить ваш шлях, якщо ви не заміните /чимось іншим з обох сторін.
NeverEndingQueue

41

Вступне зауваження Я схильний опублікувати декілька роз'яснень, оскільки деякі відповіді тут були трохи оманливими (якщо не неправильними).

Відповідь "НІ" , ви не можете просто передати кодований параметр base64 у рядку запиту URL, оскільки знаки плюс перетворюються на SPACE всередині глобального масиву $ _GET. Іншими словами, якщо ви послали test.php? MYVAR = stringwith + знак , щоб

//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 для вас. Це рятує мене від попадання всередину кролячої нори.
Кошти

Відмінний відповідь -> Ви не хочете urldecode () повернуту рядок $ _GET, оскільки + буде перетворена на пробіли. Однак було б безпечно rawurldecode () ввести,
MarcoZen

14

Так і ні.

Основна схема діапазону64 може в деяких випадках стикатися з традиційними умовами, що використовуються в URL-адресах. Але багато реалізацій base64 дозволяють вам змінити діаграму, щоб краще відповідати URL-адресам, або навіть поставитись із такою (як Python urlsafe_b64encode()).

Ще одна проблема, з якою ви можете зіткнутися, - це обмеження довжини URL-адреси, а точніше - відсутність такого обмеження. Оскільки в стандартах не визначена максимальна довжина, браузери, сервери, бібліотеки та інше програмне забезпечення, що працює з протоколом HTTP, можуть визначати власні обмеження. Ви можете поглянути на цю статтю: FAQ WWW: Яка максимальна довжина URL-адреси?


8

Його базовий код 64url, який ви можете спробувати, його просто розширення коду 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

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


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, зазвичай не мають +. +буде перетворено у порожній простір, що призведе до неправильного розшифрованого рядка. /безпечно в парі параметрів get. =завжди знаходиться в кінці кодованого рядка base64 і сторона сервера може вирішуватись =безпосередньо.


Я думаю, що це правильно, оскільки експерименти, які я робив з кодуванням base64 (без кодування URL), були успішними, але мені цікаво, чи є якась документація, яку ви могли б надати для резервного копіювання?
Шон Бін

1
ви кажете "завжди безпечно", але тоді ви кажете "зазвичай немає +". Тож ваш суперечить собі. Шви знака + можуть спричинити проблеми, якщо у вас є його у вашому рядку base64.
Нік Гумріч
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.