Przekazywanie zakodowanych ciągów base64 w adresie URL

Odpowiedzi:

206

Nie, musisz go zakodować, ponieważ ciągi base64 mogą zawierać znaki „+”, „=” i „/”, które mogą zmieniać znaczenie twoich danych - wyglądają jak podfolder.

Prawidłowe znaki base64 są poniżej.

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
Thiyagaraj
źródło
4
Kodowanie adresów URL to strata miejsca, zwłaszcza że sama baza64 pozostawia wiele znaków nieużywanych.
Michał Górny,
21
Nie jestem pewien, czy rozumiem, co mówisz - kodowanie adresów URL nie zmieni żadnego ze znaków poza trzema ostatnimi znakami z powyższej listy, a to zapobiegnie ich nieprawidłowej interpretacji, ponieważ mają one inne znaczenie w adresach URL. To samo dotyczy base64, oryginalne dane mogą być binarne lub cokolwiek, ale są zakodowane w formie, którą można łatwo przesłać za pomocą prostych protokołów.
Thiyagaraj
3
Po pierwsze, powinieneś także uciec od znaku „+”, ponieważ może on zostać przekształcony w przestrzeń. Po drugie, istnieje co najmniej kilka znaków, które można bezpiecznie używać w adresach URL i nie są one używane w „standardowym” zestawie znaków. Twoja metoda może nawet trzykrotnie zwiększyć rozmiar przesyłanych danych w niektórych sytuacjach; zastępując te postacie innymi, załatwi sprawę, zachowując tę ​​samą długość. Jest to również dość standardowe rozwiązanie.
Michał Górny,
8
en.wikipedia.org/wiki/Base64#URL_applications - wyraźnie mówi, że ucieczka „sprawia, że ​​łańcuch jest niepotrzebnie dłuższy” i wspomina o alternatywnym wariancie zestawu znaków.
Michał Górny,
1
Z powodu tej odpowiedzi zdiagnozowałem mój problem jako dokładnie to, o czym wspomniałem. Niektóre podstawowe 64 znaki (+, /, =) były zmieniane z powodu przetwarzania adresów URL. Gdy kodowałem adres URL podstawowego ciągu 64, problem został rozwiązany.
Chuck Krutsinger
272

Istnieją dodatkowe specyfikacje base64. (Szczegóły podano w tabeli tutaj ). Ale zasadniczo potrzebujesz 65 znaków do zakodowania: 26 małych liter + 26 wielkich liter + 10 cyfr = 62.

Potrzebujesz jeszcze dwóch ['+', '/'] i padding char '='. Ale żaden z nich nie jest przyjazny dla adresów URL, więc po prostu użyj dla nich różnych znaków i gotowe. Standardowe z powyższej tabeli to ['-', '_'], ale możesz używać innych znaków, o ile dekodujesz je tak samo i nie musisz dzielić się z innymi.

Polecam po prostu napisać własnych pomocników. Podobnie jak w komentarzach na stronie podręcznika php dla base64_encode :

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

function base64_url_decode($input) {
 return base64_decode(strtr($input, '._-', '+/='));
}
Joe Flynn
źródło
53
Świetne rozwiązanie, z wyjątkiem przecinków w adresach URL. Polecam użycie „~” (tylda) lub „.” (kropka) zamiast.
kralyk
11
@kralyk: Polecam po prostu, urlencodejak sugeruje odpowiedź rodrigo-silveira. Utworzenie dwóch nowych funkcji w celu zaoszczędzenia kilku znaków w adresie URL to jak wejście do domu przez okno zamiast po prostu korzystania z drzwi.
Marco Demaio
5
@MarcoDemaio, nie wiedząc, jak zostanie użyty, nie można powiedzieć, że to tylko kilka znaków. Każdy zakodowany znak będzie miał potrójną długość i dlaczego „+++ ...” nie byłby prawidłowym ciągiem base64? Adresy URL mają ograniczenia przeglądarki, a trzykrotny URL może spowodować, że je przekroczysz.
leewz
10
@RandalSchwartz tylda jest bezpieczna pod adresem URL. Z RFC3986:unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
kralyk
3
Ponieważ ,należy to zaadresować %2C, sugeruję użycie ._- zamiast -_,jak jedynego wariantu w en.wikipedia.org/wiki/Base64#Variants_summary_table, który utrzymuje końcowy =
PaulH
75

@joeshmo Lub zamiast pisać funkcję pomocnika, możesz po prostu urlencode kodowany ciąg base64. To zrobiłoby dokładnie to samo, co funkcja pomocnika, ale bez potrzeby dwóch dodatkowych funkcji.

$str = 'Some String';

$encoded = urlencode( base64_encode( $str ) );
$decoded = base64_decode( urldecode( $encoded ) );
rodrigo-silveira
źródło
2
Wynik nie jest dokładnie taki sam. urlencode używa 3 znaków do kodowania nieważnych znaków, a rozwiązanie joeshmo używa 1. To nie jest duża różnica, ale to wciąż marnotrawstwo.
Josef Borkovec
1
@JosefBorkovec Naprawdę? Oznaczałoby to również, że zakodowana ta sama liczba bajtów base64-> url-> może mieć różną różną wynikową długość, podczas gdy inne rozwiązanie daje przewidywalną długość, prawda?
humanityANDpeace
@humanityANDpeace Tak, urlencode jest gównianym rozwiązaniem, ponieważ trzykrotnie zwiększa rozmiar niektórych ciągów base64. Nie można również ponownie użyć bufora, ponieważ dane wyjściowe są większe niż dane wejściowe.
Navin
4
Rozszerzenie od 1 do 3 znaków występuje średnio na 3 z 64 znaków, więc jest to 9% narzut (2 *
3/64
Uważaj na /znak, jeśli przekazujesz go nie jako parametr GET, ale jako ścieżkę w adresie URL. Zmieni to twoją ścieżkę, jeśli nie zastąpisz /czegoś innego po obu stronach.
NeverEndingQueue,
41

Uwaga wstępna Skłaniam się do opublikowania kilku wyjaśnień, ponieważ niektóre z odpowiedzi tutaj były nieco mylące (jeśli nie nieprawidłowe).

Odpowiedź brzmi NIE , nie można po prostu przekazać parametru zakodowanego w base64 w ciągu zapytania do adresu URL, ponieważ znaki plus są konwertowane na SPACJĘ w globalnej tablicy $ _GET. Innymi słowy, jeśli wysłałeś test.php? MyVar = stringwith + sign to

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

wynik byłby:
stringwith sign

Łatwym sposobem rozwiązania tego jest po prostu urlencode()ciąg base64 przed dodaniem go do ciągu zapytania, aby uciec od znaków +, = i / do kodów% ##. Na przykład urlencode("stringwith+sign")zwracastringwith%2Bsign

Podczas przetwarzania akcji PHP automatycznie dekoduje ciąg zapytania, gdy zapełni on globalną wartość $ _GET. Na przykład, jeśli wysłałem test.php? MyVar = stringwith% 2Bsign to

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

wynikiem byłoby:
stringwith+sign

Zdajesz nie chcą urldecode()zwrócony ciąg $ _GET jak s + 'zostaną zamienione na spacje.
Innymi słowy, jeśli wysłałem ten sam test.php? MyVar = stringwith% 2Bsign to

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

wynik jest nieoczekiwany:
stringwith sign

Byłby bezpieczny dla rawurldecode()danych wejściowych, jednak byłby zbędny, a zatem niepotrzebny.

Jeffory J. Beckers
źródło
1
Niezła odpowiedź. Możesz użyć kodu PHP bez początkowych i końcowych znaczników na tej stronie, jeśli pytanie jest oznaczone php (również najczęściej wynika to z kontekstu pytania). Jeśli dodasz dwie spacje na końcu linii, zobaczysz <br>, więc nie musisz pisać dużo HTML. Mam nadzieję, że to pomoże. Zredagowałem trochę twoją odpowiedź, aby jeszcze bardziej ją poprawić.
hakre
Dziękujemy za wspomnienie, że PHP dekoduje dla ciebie adres URL. To ratuje mnie przed wpadnięciem do króliczej nory.
Cocest,
Świetna odpowiedź -> Nie chcesz urldecode () zwracanego ciągu $ _GET, ponieważ znaki + zostaną przekonwertowane na spacje. Bezpiecznie byłoby jednak wprowadzić kod źródłowy rawurld ()
MarcoZen
14

Tak i nie.

Podstawowy zestaw znaków base64 może w niektórych przypadkach kolidować z tradycyjnymi konwencjami stosowanymi w adresach URL. Jednak wiele implementacji base64 pozwala na zmianę zestawu znaków w celu lepszego dopasowania adresów URL lub nawet dostarczenia go z jednym (np. Pythona urlsafe_b64encode()).

Innym problemem, z którym możesz się spotkać, jest limit długości adresu URL, a raczej jego brak. Ponieważ standardy nie określają maksymalnej długości, przeglądarki, serwery, biblioteki i inne oprogramowanie współpracujące z protokołem HTTP mogą określać własne ograniczenia. Możesz rzucić okiem na ten artykuł: Często zadawane pytania na temat WWW: Jaka jest maksymalna długość adresu URL?

Michał Górny
źródło
8

Jest to kod base64url, który możesz wypróbować, to tylko rozszerzenie powyższego kodu 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));
}
Andy
źródło
Działa to w przypadku danych zakodowanych w JavieBase64.getUrlEncoder().withoutPadding().encodeToString()
4

Nie sądzę, że jest to bezpieczne, ponieważ np. Znak „=” jest używany w surowej bazie 64 i jest także używany do odróżniania parametrów od wartości w HTTP GET.

Mischa
źródło
1

Teoretycznie tak, o ile nie przekroczysz maksymalnej długości ciągu adresu URL i / lub zapytania dla klienta lub serwera.

W praktyce może być trochę trudniej. Na przykład może wywołać wyjątek HttpRequestValidationException na platformie ASP.NET, jeśli wartość zawiera „on”, a ty zostawisz końcowe „==”.

Nicole Calinoiu
źródło
nie wspominasz o znakach +, / lub =, które w niektórych przypadkach powodują, że adresy URL są nieprawidłowe.
Will Bickford,
0

Dla bezpiecznego base64.urlsafe_b64encode(...)kodu URL, jak w Pythonie poniższy kod działa dla mnie w 100%

function base64UrlSafeEncode(string $input)
{
   return str_replace(['+', '/'], ['-', '_'], base64_encode($input));
}
Igor Sazonow
źródło
-10

Tak, zawsze jest bezpiecznie. oczywiście base64 zawiera: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= ale łańcuch zakodowany base64 zwykle nie ma +. +zostanie przekonwertowany na puste miejsce, co spowoduje nieprawidłowy dekodowanie ciągu. /jest bezpieczny w parze parametrów get. =jest zawsze na końcu łańcucha zakodowanego w base64, a strona serwera może rozwiązać =bezpośrednio.

gouchaoer
źródło
Zgaduję, że jest to poprawne, ponieważ eksperymenty, które przeprowadziłem z kodowaniem base64 (bez kodowania adresu URL) zakończyły się powodzeniem, ale zastanawiam się, czy jest jakaś dokumentacja, którą mógłbyś dostarczyć, aby to zrobić?
Sean the Bean
1
mówisz „zawsze bezpieczny”, ale potem mówisz „zwykle nie ma +”. Więc zaprzeczasz sobie. Szwy + powodują problemy, jeśli masz je w ciągu base64.
Nick Humrich