Passer des chaînes encodées en base64 dans l'URL

Réponses:

206

Non, vous devrez le coder par URL, car les chaînes base64 peuvent contenir les caractères "+", "=" et "/" qui pourraient modifier la signification de vos données - ressembler à un sous-dossier.

Les caractères base64 valides sont ci-dessous.

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
Thiyagaraj
la source
4
Le codage d'URL est une perte d'espace, d'autant plus que base64 lui-même laisse de nombreux caractères inutilisés.
Michał Górny
21
Je ne suis pas sûr de comprendre ce que vous dites - le codage d'URL ne modifiera aucun des caractères, à l'exception des trois derniers caractères de la liste ci-dessus, et c'est pour les empêcher d'être interprétés incorrectement car ils ont d'autres significations dans les URL. Il en va de même pour la base64, les données d'origine peuvent être binaires ou quoi que ce soit, mais elles sont encodées sous une forme qui peut être transmise facilement à l'aide de protocoles simples.
Thiyagaraj
3
Tout d'abord, vous devez également échapper à «+» car il peut être converti en espace. Deuxièmement, il y a au moins quelques caractères qui peuvent être utilisés en toute sécurité dans les URL et ne sont pas utilisés dans le jeu de caractères «standard». Votre méthode peut même augmenter la taille des données transférées trois fois dans certaines situations; tout en remplaçant ces caractères par d'autres fera l'affaire tout en conservant la même longueur. Et c'est aussi une solution tout à fait standard.
Michał Górny
8
en.wikipedia.org/wiki/Base64#URL_applications - il indique clairement que l'échappement «rend la chaîne inutilement plus longue» et mentionne la variante de jeu de caractères alternative.
Michał Górny
1
À cause de cette réponse, j'ai diagnostiqué mon problème comme étant exactement ce qu'il mentionnait. Certains des 64 caractères de base (+, /, =) ont été modifiés en raison du traitement des URL. Lorsque j'ai URL encodé la chaîne de base 64, le problème a été résolu.
Chuck Krutsinger
272

Il existe des spécifications supplémentaires en base64. (Voir le tableau ici pour les détails). Mais essentiellement, vous avez besoin de 65 caractères pour coder: 26 minuscules + 26 majuscules + 10 chiffres = 62.

Vous avez besoin de deux autres ['+', '/'] et un caractère de remplissage '='. Mais aucun d'entre eux n'est compatible avec les URL, alors utilisez simplement des caractères différents pour eux et vous êtes prêt. Les standards du graphique ci-dessus sont ['-', '_'], mais vous pouvez utiliser d'autres caractères tant que vous les décodez de la même manière, et que vous n'avez pas besoin de les partager avec les autres.

Je recommanderais simplement d'écrire vos propres assistants. Comme ceux des commentaires sur la page de manuel php pour base64_encode :

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

function base64_url_decode($input) {
 return base64_decode(strtr($input, '._-', '+/='));
}
Joe Flynn
la source
53
Excellente solution, sauf que la virgule n'est pas sans réserve dans les URL. Je recommande d'utiliser '~' (tilde) ou '.' (point) à la place.
kralyk
11
@kralyk: Je recommande simplement d'utiliser urlencodecomme suggéré par la réponse de rodrigo-silveira. Créer deux nouvelles fonctions pour enregistrer quelques caractères en longueur d'URL, c'est comme entrer dans votre maison en passant par la fenêtre au lieu d'utiliser simplement la porte.
Marco Demaio
5
@MarcoDemaio, sans savoir comment il sera utilisé, il est impossible de dire qu'il ne s'agit que de quelques caractères. Chaque caractère encodé aura une longueur triple, et pourquoi "+++ ..." ne serait-il pas une chaîne base64 valide? Les URL ont des limites de navigateur, et tripler une URL peut vous faire atteindre ces limites.
leewz
10
@RandalSchwartz tilde est sans danger pour les URL. De RFC3986:unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
kralyk
3
Puisque ,devrait être encodé en url %2C, je suggère d'utiliser ._- au lieu de -_,comme la seule variante de en.wikipedia.org/wiki/Base64#Variants_summary_table qui garde le suivi =
PaulH
75

@joeshmo Ou au lieu d'écrire une fonction d'assistance, vous pouvez simplement encoder la chaîne encodée en base64. Cela ferait exactement la même chose que votre fonction d'assistance, mais sans avoir besoin de deux fonctions supplémentaires.

$str = 'Some String';

$encoded = urlencode( base64_encode( $str ) );
$decoded = base64_decode( urldecode( $encoded ) );
rodrigo-silveira
la source
2
Le résultat n'est pas exactement le même. urlencode utilise 3 caractères pour encoder les caractères non valides et la solution de joeshmo en utilise 1. Ce n'est pas une grande différence, mais c'est quand même un gaspillage.
Josef Borkovec
1
@JosefBorkovec Vraiment? Cela signifierait également que le même nombre d'octets codés en base64-> url-> pourrait avoir une variété de longueurs résultantes différentes, tandis que l'autre solution donne une longueur prévisible, non?
humanityANDpeace
@humanityANDpeace Oui, l'urlencode est une solution de merde car il triple la taille de certaines chaînes base64. Vous ne pouvez pas non plus réutiliser le tampon car la sortie est plus grande que l'entrée.
Navin
4
L'extension de 1 à 3 caractères se produit sur 3 des 64 caractères en moyenne, il s'agit donc d'un surcoût de 9% (2 *
3/64
Soyez prudent avec le /caractère si vous le transmettez non pas comme paramètre GET, mais comme chemin d'accès dans l'URL. Cela changera votre chemin si vous ne remplacez pas /par quelque chose d'autre des deux côtés.
NeverEndingQueue
41

Note introductive Je suis enclin à publier quelques clarifications car certaines des réponses ici étaient un peu trompeuses (sinon incorrectes).

La réponse est NON , vous ne pouvez pas simplement passer un paramètre codé en base64 dans une chaîne de requête URL car les signes plus sont convertis en un ESPACE dans le tableau global $ _GET. En d'autres termes, si vous avez envoyé test.php? MyVar = stringwith + sign to

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

le résultat serait:
stringwith sign

Le moyen le plus simple de résoudre ce problème consiste simplement à urlencode()insérer votre chaîne base64 avant de l'ajouter à la chaîne de requête pour échapper les caractères +, = et / aux codes% ##. Par exemple, urlencode("stringwith+sign")renvoiestringwith%2Bsign

Lorsque vous traitez l'action, PHP se charge de décoder automatiquement la chaîne de requête lors du remplissage de la variable globale $ _GET. Par exemple, si j'ai envoyé test.php? MyVar = stringwith% 2Bsign to

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

le résultat serait:
stringwith+sign

Vous ne voulez pas de urldecode()la chaîne $ _GET retournée car les + seront convertis en espaces.
En d'autres termes, si j'ai envoyé le même test.php? MyVar = stringwith% 2Bsign to

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

le résultat est inattendu:
stringwith sign

Il serait sûr pour rawurldecode()l'entrée, cependant, il serait redondant et donc inutile.

Jeffory J. Beckers
la source
1
Bonne réponse. Vous pouvez utiliser du code PHP sans les balises de début et de fin sur ce site si la question est balisée php (également le plus souvent, cela ressort clairement du contexte de la question). Si vous ajoutez deux espaces à la fin d'une ligne, vous verrez le <br>, donc pas besoin de taper beaucoup de code HTML. J'espère que cela aide, j'ai un peu modifié votre réponse pour encore plus l'améliorer.
hakre
Merci d'avoir mentionné que PHP décode l'URL pour vous. Cela m'évite de tomber dans un terrier de lapin.
Cocest
Bonne réponse -> Vous ne voulez pas urldecode () la chaîne $ _GET retournée car les + seront convertis en espaces. Il serait cependant sûr de rawurldecode () l'entrée
MarcoZen
14

Oui et non.

Le jeu de caractères de base de base64 peut dans certains cas entrer en collision avec les conventions traditionnelles utilisées dans les URL. Mais de nombreuses implémentations en base64 vous permettent de modifier le jeu de caractères pour mieux correspondre aux URL ou même de les accompagner (comme Python urlsafe_b64encode()).

Un autre problème auquel vous pouvez être confronté est la limite de longueur d'URL ou plutôt - l'absence de cette limite. Parce que les normes ne spécifient aucune longueur maximale, les navigateurs, serveurs, bibliothèques et autres logiciels fonctionnant avec le protocole HTTP peuvent définir ses propres limites. Vous pouvez consulter cet article: FAQ WWW: Quelle est la longueur maximale d'une URL?

Michał Górny
la source
8

C'est un encodage base64url que vous pouvez essayer, sa juste extension du code de joeshmo ci-dessus.

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
la source
Cela fonctionne pour les données encodées avec JavaBase64.getUrlEncoder().withoutPadding().encodeToString()
4

Je ne pense pas que cela soit sûr car, par exemple, le caractère "=" est utilisé dans la base brute 64 et est également utilisé pour différencier les paramètres des valeurs dans un HTTP GET.

Mischa
la source
1

En théorie, oui, tant que vous ne dépassez pas la longueur maximale de l'url et / ou de la chaîne de requête pour le client ou le serveur.

En pratique, les choses peuvent devenir un peu plus compliquées. Par exemple, il peut déclencher une HttpRequestValidationException sur ASP.NET si la valeur contient un "on" et que vous laissez dans le dernier "==".

Nicole Calinoiu
la source
vous ne faites aucune mention des caractères +, / ou = qui rendent les URL invalides dans certains cas.
Will Bickford le
0

Pour un encodage sûr, comme base64.urlsafe_b64encode(...)en Python, le code ci-dessous fonctionne à 100% pour moi

function base64UrlSafeEncode(string $input)
{
   return str_replace(['+', '/'], ['-', '_'], base64_encode($input));
}
Igor Sazonov
la source
-10

Oui, c'est toujours sûr. bien sûr, base64 contient: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= mais une chaîne encodée en base64 n'en a généralement pas +. +sera converti en un espace vide, entraîne une chaîne décodée incorrecte. /est sûr dans une paire de paramètres get. =est toujours à la fin de la chaîne encodée en base64 et le côté serveur peut être résolu =directement.

gouchaoer
la source
Je suppose que c'est correct, car les expériences que j'ai faites avec le codage base64 (sans le codage url) ont réussi, mais je me demande s'il y a de la documentation que vous pourriez fournir pour sauvegarder cela?
Sean the Bean
1
vous dites "toujours en sécurité", mais vous dites "généralement pas de +". Donc vous vous contredisez. Le signe + semble causer des problèmes si vous l'avez dans votre chaîne base64.
Nick Humrich