Passando cadeias codificadas em base64 na URL

Respostas:

206

Não, você precisaria codificá-lo por URL, pois as cadeias base64 podem conter os caracteres "+", "=" e "/" que podem alterar o significado dos seus dados - parecendo uma subpasta.

Os caracteres base64 válidos estão abaixo.

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
Thiyagaraj
fonte
4
A codificação de URL é um desperdício de espaço, especialmente porque o próprio base64 deixa muitos caracteres sem uso.
Michał Górny
21
Não sei se entendi o que você está dizendo - a codificação de URL não altera nenhum dos caracteres, exceto os três últimos na lista acima, e isso evita que eles sejam interpretados incorretamente, pois têm outros significados nos URLS. O mesmo vale para base64, os dados originais podem ser binários ou algo assim, mas são codificados em um formato que pode ser transmitido facilmente usando protocolos simples.
Thiyagaraj
3
Primeiramente, você deve escapar do '+' também, pois ele pode ser convertido em espaço. Em segundo lugar, existem pelo menos alguns caracteres que são seguros para uso em URLs e não são usados ​​no conjunto de caracteres 'padrão'. Seu método pode até aumentar o tamanho dos dados transferidos três vezes em determinadas situações; A substituição desses caracteres por outra fará o truque, preservando o mesmo comprimento. E é uma solução bastante padrão também.
Michał Górny
8
en.wikipedia.org/wiki/Base64#URL_applications - diz claramente que escapar 'torna a cadeia desnecessariamente mais longa' e menciona a variante alternativa do conjunto de caracteres.
Michał Górny
1
Por causa dessa resposta, eu diagnóstico meu problema como sendo exatamente o que ele mencionou. Alguns dos 64 caracteres básicos (+, /, =) estavam sendo alterados devido ao processamento do URL. Quando codifiquei a URL da cadeia de base 64, o problema foi resolvido.
Chuck Krutsinger
272

Existem especificações base64 adicionais. (Veja a tabela aqui para detalhes). Mas essencialmente você precisa de 65 caracteres para codificar: 26 em minúscula + 26 em maiúscula + 10 dígitos = 62.

Você precisa de mais dois ['+', '/'] e um caractere de preenchimento '='. Mas nenhum deles é compatível com URL, portanto, use caracteres diferentes para eles e pronto. Os padrões do gráfico acima são ['-', '_'], mas você pode usar outros caracteres desde que os decodifique da mesma forma e não precise compartilhar com outros.

Eu recomendo apenas escrever seus próprios ajudantes. Assim como nos comentários na página de manual do php para base64_encode :

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

function base64_url_decode($input) {
 return base64_decode(strtr($input, '._-', '+/='));
}
Joe Flynn
fonte
53
Ótima solução, exceto vírgula não é reservada em URLs. Eu recomendo usar '~' (til) ou '.' (ponto) em vez disso.
Kralyk #
11
@kralyk: Eu recomendo apenas usar urlencodecomo sugerido pela resposta de rodrigo-silveira. Criando duas novas funções para economizar alguns caracteres no tamanho da URL, é como entrar na sua casa passando pela janela em vez de apenas usar a porta.
Marco Demaio
5
@MarcoDemaio, sem saber como será usado, é impossível dizer que são apenas alguns caracteres. Cada caractere codificado terá o triplo do comprimento e por que "+++ ..." não seria uma string base64 válida? URLs têm limites de navegador, e triplicar um URL pode fazer você atingir esses limites.
leewz
10
O @RandalSchwartz til é seguro para URL. De RFC3986:unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
kralyk
3
Desde ,devem ser urlencoded para %2C, eu sugiro usar ._- em vez de -_,ser a única variante em en.wikipedia.org/wiki/Base64#Variants_summary_table que mantém o arrasto =
PaulH
75

@joeshmo Ou, em vez de escrever uma função auxiliar, você pode simplesmente codificar a string codificada em base64. Isso faria exatamente a mesma coisa que sua função auxiliar, mas sem a necessidade de duas funções extras.

$str = 'Some String';

$encoded = urlencode( base64_encode( $str ) );
$decoded = base64_decode( urldecode( $encoded ) );
rodrigo-silveira
fonte
2
O resultado não é exatamente o mesmo. O urlencode usa 3 caracteres para codificar caracteres não válidos e a solução da joeshmo usa 1. Não é uma grande diferença, mas ainda é um desperdício.
Josef Borkovec 01/03
1
@JosefBorkovec Really? Então isso também significaria que o mesmo número de bytes codificado em base64-> url-> poderia ter uma variedade de diferentes tamanhos resultantes, enquanto a outra solução fornece um comprimento previsível, certo?
humanityANDpeace
@humanityANDpeace Sim, o urlencode é uma solução de merda porque triplica o tamanho de certas strings base64. Você também não pode reutilizar o buffer, pois a saída é maior que a entrada.
Navin
4
Expansão de 1 a 3 caracteres ocorre em 3 de 64 caracteres, em média, de modo que é uma sobrecarga de 9% (2 * 3/64)
PaulH
Tenha cuidado com o /caractere se você o transmitir não como um parâmetro GET, mas como um caminho na URL. Isso mudará seu caminho se você não substituir /por outra coisa nos dois lados.
NeverEndingQueue
41

Nota introdutória Estou inclinado a postar alguns esclarecimentos, já que algumas das respostas aqui foram um pouco enganadoras (se não incorretas).

A resposta é NÃO , você não pode simplesmente passar um parâmetro codificado em base64 em uma sequência de consulta de URL, pois os sinais de adição são convertidos em um ESPAÇO dentro da matriz global $ _GET. Em outras palavras, se você enviou test.php? MyVar = stringwith + sign para

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

o resultado seria:
stringwith sign

A maneira mais fácil de resolver isso é simplesmente urlencode()sua string base64 antes de adicioná-la à string de consulta para escapar dos caracteres +, = e / / aos códigos% ##. Por exemplo, urlencode("stringwith+sign")retornastringwith%2Bsign

Quando você processa a ação, o PHP cuida da decodificação automática da string de consulta quando preenche o global $ _GET. Por exemplo, se eu enviei test.php? MyVar = stringwith% 2Bsign para

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

o resultado seria:
stringwith+sign

Você não deseja a urldecode()string $ _GET retornada, pois os + serão convertidos em espaços.
Em outras palavras, se eu enviar o mesmo test.php? MyVar = stringwith% 2Bsign para

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

o resultado é inesperado:
stringwith sign

Seria seguro para rawurldecode()a entrada, no entanto, seria redundante e, portanto, desnecessário.

Jeffory J. Beckers
fonte
1
Boa resposta. Você pode usar o código PHP sem as tags inicial e final neste site se a pergunta for marcada com php (também na maioria das vezes fica claro no contexto da pergunta). Se você adicionar dois espaços no final de uma linha, verá o <br>, portanto, não é necessário digitar muito HTML. Espero que isso ajude, editei sua resposta um pouco para melhorá-la ainda mais.
hakre
Obrigado por mencionar que o PHP decodifica o URL para você. Isso me salva de cair dentro de uma toca de coelho.
Cocest
Ótima resposta -> Você não deseja urldecode () a string $ _GET retornada, pois os + serão convertidos em espaços. No entanto, seria seguro rawurldecode () a entrada
MarcoZen
14

Sim e não.

O conjunto de caracteres básico de base64 pode, em alguns casos, colidir com convenções tradicionais usadas em URLs. Porém, muitas das implementações de base64 permitem alterar o conjunto de caracteres para corresponder melhor às URLs ou até mesmo com uma (como a do Python urlsafe_b64encode()).

Outro problema que você pode estar enfrentando é o limite do tamanho do URL ou melhor - a falta desse limite. Como os padrões não especificam tamanho máximo, navegadores, servidores, bibliotecas e outros softwares que trabalham com o protocolo HTTP podem definir seus próprios limites. Você pode dar uma olhada neste artigo: WWW FAQs: Qual é o tamanho máximo de um URL?

Michał Górny
fonte
8

É uma codificação base64url que você pode experimentar, é apenas uma extensão do código do joeshmo acima.

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
fonte
Isso funciona para os dados codificados com JavaBase64.getUrlEncoder().withoutPadding().encodeToString()
4

Eu não acho que isso seja seguro porque, por exemplo, o caractere "=" é usado na base bruta 64 e também é usado para diferenciar os parâmetros dos valores em um HTTP GET.

Mischa
fonte
1

Em teoria, sim, desde que você não exceda o comprimento máximo da URL e / ou da consulta para o cliente ou servidor.

Na prática, as coisas podem ficar um pouco mais complicadas. Por exemplo, ele pode disparar uma HttpRequestValidationException no ASP.NET se o valor contiver um "on" e você deixar no final "==".

Nicole Calinoiu
fonte
você não faz menção a +, / ou = caracteres que invalidam os URLs em certos casos.
Will Bickford
0

Para codificação segura de URL, como base64.urlsafe_b64encode(...)em Python, o código abaixo, funciona para mim 100%

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

Sim, é sempre seguro. é claro que base64 contém: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/= mas uma string codificada em base64 geralmente não possui +. +será convertido em um espaço em branco, resultando em uma string decodificada incorreta. /é seguro em um par de parâmetros get. =está sempre no final da cadeia codificada em base64 e o lado do servidor pode resolver =diretamente.

gouchaoer
fonte
Acho que isso está correto, pois os experimentos que fiz com a codificação base64 (sem codificação de URL) foram bem-sucedidos, mas estou imaginando se há alguma documentação que você possa fornecer para fazer backup disso.
Sean the Bean
1
você diz "sempre seguro", mas depois diz "geralmente não tem +". Então você está se contradizendo. O sinal de + é costurado para causar problemas se você o tiver na string base64.
Nick Humrich