php - safe - urlencode vs rawurlencode?



special characters in url (8)

http://us2.php.net/manual/en/function.urlencode.php : Isso difere da »codificação RFC 1738 (veja rawurlencode ()) em que, por razões históricas, os espaços são codificados como sinais de mais (+).

https://src-bin.com

Se eu quiser criar um URL usando uma variável, tenho duas opções para codificar a string. urlencode() e rawurlencode() .

Quais são exatamente as diferenças e quais são as preferidas?


Answer #1

1. Quais são exatamente as diferenças e

A única diferença está na maneira como os espaços são tratados:

urlencode - baseado na implementação legada converte espaços para +

rawurlencode - baseado no RFC 1738 converte espaços em% 20

A razão para a diferença é porque + é reservado e válido (não codificado) em urls.

2. qual é o preferido?

Eu realmente gostaria de ver algumas razões para escolher uma sobre a outra ... Eu quero ser capaz de escolher uma delas e usá-la para sempre com o mínimo de confusão.

É justo, eu tenho uma estratégia simples que eu sigo quando tomo essas decisões, as quais vou compartilhar com você na esperança de que isso possa ajudar.

Eu acho que foi a especificação HTTP / 1.1 RFC 2616, que pediu " aplicações tolerantes "

Os clientes devem ser tolerantes ao analisar a linha de status e servidores tolerantes ao analisar a linha de solicitação.

Quando confrontados com questões como essas, a melhor estratégia é sempre consumir o máximo possível e produzir o que é compatível com os padrões.

Então, meu conselho é usar rawurlencode para produzir seqüências de caracteres codificadas RFC 1738 compatíveis com padrões e usar urldecode para ser compatível com versões anteriores e acomodar qualquer coisa que você pode encontrar para consumir.

Agora você pode simplesmente acreditar em mim, mas vamos provar que devemos ...

php > $url = <<<'EOD'
<<< > "Which, % of Alice's tasks saw $s @ earnings?"
<<< > EOD;
php > echo $url, PHP_EOL;
"Which, % of Alice's tasks saw $s @ earnings?"
php > echo urlencode($url), PHP_EOL;
%22Which%2C+%25+of+Alice%27s+tasks+saw+%24s+%40+earnings%3F%22
php > echo rawurlencode($url), PHP_EOL;
%22Which%2C%20%25%20of%20Alice%27s%20tasks%20saw%20%24s%20%40%20earnings%3F%22
php > echo rawurldecode(urlencode($url)), PHP_EOL;
"Which,+%+of+Alice'[email protected]+earnings?"
php > // oops that's not right???
php > echo urldecode(rawurlencode($url)), PHP_EOL;
"Which, % of Alice's tasks saw $s @ earnings?"
php > // now that's more like it

Parece que o PHP tinha exatamente isso em mente, mesmo que eu nunca tenha encontrado ninguém recusando nenhum dos dois formatos, eu não consigo pensar em uma estratégia melhor para adotar como sua estratégia de defesa, não é?

nJoy!


Answer #2

A diferença está nos valores de retorno, ou seja:

http://us2.php.net/manual/en/function.urlencode.php :

Retorna uma string na qual todos os caracteres não alfanuméricos, exceto -_. foram substituídos por um sinal de porcentagem (%) seguido por dois dígitos hexadecimais e espaços codificados como sinais de mais (+). Ele é codificado da mesma forma que os dados postados de um formulário da WWW são codificados, da mesma forma que no tipo de mídia application / x-www-form-urlencoded. Isso difere da »codificação RFC 1738 (veja rawurlencode ()) em que, por razões históricas, os espaços são codificados como sinais de mais (+).

http://us2.php.net/manual/en/function.rawurlencode.php :

Retorna uma string na qual todos os caracteres não alfanuméricos, exceto -_. foram substituídos por um sinal de porcentagem (%) seguido por dois dígitos hexadecimais. Essa é a codificação descrita em »RFC 1738 para proteger os caracteres literais de serem interpretados como delimitadores de URL especiais e para proteger os URLs de serem desconfigurados pela mídia de transmissão com conversões de caracteres (como alguns sistemas de email).

Os dois são muito semelhantes, mas o último (rawurlencode) substituirá espaços com um '%' e dois dígitos hexadecimais, que é adequado para codificar senhas ou algo semelhante, onde um '+' não é, por exemplo:

echo '<a href="ftp://user:', rawurlencode('foo @+%/'),
     '@ftp.example.com/x.txt">';
//Outputs <a href="ftp://user:foo%20%40%2B%25%[email protected]/x.txt">

Answer #3

A prova está no código fonte do PHP.

Vou levá-lo através de um processo rápido de como descobrir esse tipo de coisa no futuro, quando quiser. Tenha comigo, haverá um monte de código-fonte em C que você pode passar por cima (eu explico). Se você quiser escovar alguns C, um bom lugar para começar é o nosso wiki SO .

Baixe a fonte (ou use http://lxr.php.net/ para navegar on-line), grep todos os arquivos para o nome da função, você encontrará algo como isto:

PHP 5.3.6 (mais recente no momento da escrita) descreve as duas funções em seu código C nativo no arquivo url.c.

RawUrlEncode ()

PHP_FUNCTION(rawurlencode)
{
    char *in_str, *out_str;
    int in_str_len, out_str_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
                              &in_str_len) == FAILURE) {
        return;
    }

    out_str = php_raw_url_encode(in_str, in_str_len, &out_str_len);
    RETURN_STRINGL(out_str, out_str_len, 0);
}

UrlEncode ()

PHP_FUNCTION(urlencode)
{
    char *in_str, *out_str;
    int in_str_len, out_str_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
                              &in_str_len) == FAILURE) {
        return;
    }

    out_str = php_url_encode(in_str, in_str_len, &out_str_len);
    RETURN_STRINGL(out_str, out_str_len, 0);
}

Ok, então o que é diferente aqui?

Ambos estão essencialmente chamando duas funções internas diferentes respectivamente: php_raw_url_encode e php_url_encode

Então vá procurar essas funções!

Vamos ver o php_raw_url_encode

PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)
{
    register int x, y;
    unsigned char *str;

    str = (unsigned char *) safe_emalloc(3, len, 1);
    for (x = 0, y = 0; len--; x++, y++) {
        str[y] = (unsigned char) s[x];
#ifndef CHARSET_EBCDIC
        if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||
            (str[y] < 'A' && str[y] > '9') ||
            (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||
            (str[y] > 'z' && str[y] != '~')) {
            str[y++] = '%';
            str[y++] = hexchars[(unsigned char) s[x] >> 4];
            str[y] = hexchars[(unsigned char) s[x] & 15];
#else /*CHARSET_EBCDIC*/
        if (!isalnum(str[y]) && strchr("_-.~", str[y]) != NULL) {
            str[y++] = '%';
            str[y++] = hexchars[os_toascii[(unsigned char) s[x]] >> 4];
            str[y] = hexchars[os_toascii[(unsigned char) s[x]] & 15];
#endif /*CHARSET_EBCDIC*/
        }
    }
    str[y] = '\0';
    if (new_length) {
        *new_length = y;
    }
    return ((char *) str);
}

E, claro, php_url_encode:

PHPAPI char *php_url_encode(char const *s, int len, int *new_length)
{
    register unsigned char c;
    unsigned char *to, *start;
    unsigned char const *from, *end;

    from = (unsigned char *)s;
    end = (unsigned char *)s + len;
    start = to = (unsigned char *) safe_emalloc(3, len, 1);

    while (from < end) {
        c = *from++;

        if (c == ' ') {
            *to++ = '+';
#ifndef CHARSET_EBCDIC
        } else if ((c < '0' && c != '-' && c != '.') ||
                   (c < 'A' && c > '9') ||
                   (c > 'Z' && c < 'a' && c != '_') ||
                   (c > 'z')) {
            to[0] = '%';
            to[1] = hexchars[c >> 4];
            to[2] = hexchars[c & 15];
            to += 3;
#else /*CHARSET_EBCDIC*/
        } else if (!isalnum(c) && strchr("_-.", c) == NULL) {
            /* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
            to[0] = '%';
            to[1] = hexchars[os_toascii[c] >> 4];
            to[2] = hexchars[os_toascii[c] & 15];
            to += 3;
#endif /*CHARSET_EBCDIC*/
        } else {
            *to++ = c;
        }
    }
    *to = 0;
    if (new_length) {
        *new_length = to - start;
    }
    return (char *) start;
}

Um pouco de conhecimento antes de avançar, o EBCDIC é outro conjunto de caracteres , semelhante ao ASCII, mas um concorrente total. O PHP tenta lidar com ambos. Mas basicamente, isso significa byte EBCDIC 0x4c byte não é o L em ASCII, é realmente um < . Tenho certeza que você vê a confusão aqui.

Ambas as funções gerenciam o EBCDIC se o servidor da Web o tiver definido.

Além disso, ambos usam uma matriz de chars (tipo de string de pensamento) look-up para obter alguns valores, a matriz é descrita como tal:

/* rfc1738:

   ...The characters ";",
   "/", "?", ":", "@", "=" and "&" are the characters which may be
   reserved for special meaning within a scheme...

   ...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
   reserved characters used for their reserved purposes may be used
   unencoded within a URL...

   For added safety, we only leave -_. unencoded.
 */

static unsigned char hexchars[] = "0123456789ABCDEF";

Além disso, as funções são realmente diferentes e vou explicá-las em ASCII e EBCDIC.

Diferenças em ASCII:

URLENCODE:

  • Calcula um comprimento inicial / final da string de entrada, aloca memória
  • Percorre um loop while, incrementa até chegarmos ao final da string
  • Agarra o personagem atual
  • Se o caractere for igual a ASCII Char 0x20 (isto é, um "espaço"), adicione um sinal + à string de saída.
  • Se não for um espaço, e também não é alfanumérico ( isalnum(c) ), e também não é e _ , - ou . caractere, então nós, saída um sinal de % para a posição 0, faça uma matriz olhar para a matriz hexchars para uma pesquisa para array os_toascii (uma matriz do Apache que converte char para código hexadecimal) para a chave de c (o caractere atual ), nós, então, bitwise shift right por 4, atribuímos esse valor ao caractere 1, e para position 2 atribuímos a mesma lookup, exceto que preformamos um logico e para ver se o valor é 15 (0xF), e retornamos um 1 em Nesse caso, ou um 0 caso contrário. No final, você vai acabar com algo codificado.
  • Se acabar não é um espaço, é alfanumérico ou um dos _-. chars, produz exatamente o que é.

RAWURLENCODE:

  • Aloca memória para a string
  • Itera sobre ele com base no comprimento fornecido na chamada de função (não calculado na função como com URLENCODE).

Nota: Muitos programadores provavelmente nunca viram um loop for iterar desta forma, é um tanto hack e não é a convenção padrão usada com a maioria dos loops, presta atenção, atribui x e y , verifica a saída em len atingindo 0 e incrementa ambos x e y . Eu sei, não é o que você esperaria, mas é um código válido.

  • Atribui o caractere atual a uma posição de caractere correspondente em str .
  • Ele verifica se o caractere atual é alfanumérico ou um dos _-. chars, e se não é, nós fazemos quase a mesma tarefa que com o URLENCODE, onde ele pré-pesquisa, no entanto, nós incrementamos diferentemente, usando y++ ao invés to[1] , isto é porque as strings estão sendo construídas de maneiras diferentes, mas alcançar o mesmo objetivo no final de qualquer maneira.
  • Quando o loop é feito e o comprimento desaparece, ele realmente termina a string, atribuindo o byte \0 .
  • Ele retorna a string codificada.

Diferenças:

  • O UrlEncode verifica o espaço, atribui um sinal de +, RawURLEncode não.
  • UrlEncode não atribui um byte de \0 à string, o RawUrlEncode (isso pode ser um ponto discutível)
  • Eles iterar diferentemente, um pode ser propenso a transbordar com seqüências de caracteres malformados, estou apenas sugerindo isso e não tenho realmente investigado.

Eles basicamente iteram diferentemente, um designa um sinal + no evento de ASCII 20.

Diferenças no EBCDIC:

URLENCODE:

  • Mesma configuração de iteração como no ASCII
  • Ainda traduzindo o caractere "espaço" para um sinal de + . Note - Eu acho que isso precisa ser compilado em EBCDIC ou você vai acabar com um bug? Alguém pode editar e confirmar isso?
  • Ele verifica se o char atual é um caractere antes de 0 , com exceção de ser um . ou - , OU menor que A mas maior que char 9 , OR maior que Z e menor que a mas não _ . OU maior que z (sim, o EBCDIC está meio bagunçado para trabalhar). Se corresponder a qualquer um desses, faça uma pesquisa semelhante à encontrada na versão ASCII (ela simplesmente não requer uma pesquisa em os_toascii).

RAWURLENCODE:

  • Mesma configuração de iteração como no ASCII
  • A mesma verificação, conforme descrito na versão EBCDIC do URL Encode, com a exceção de que, se for maior que z , exclui ~ do código da URL.
  • Mesma atribuição que o código-fonte ASCII RawUrlEncode
  • Ainda anexando o byte \0 à string antes de retornar.

Grande Resumo

  • Ambos usam a mesma tabela de consulta hexchars
  • URIEncode não finaliza uma string com \ 0, raw faz.
  • Se você estiver trabalhando no EBCDIC, sugiro usar o RawUrlEncode, pois ele gerencia o ~ que o UrlEncode não faz ( esse é um problema relatado ). Vale a pena notar que ASCII e EBCDIC 0x20 são ambos espaços.
  • Eles iteram de maneira diferente, um pode ser mais rápido, um pode ser propenso à memória ou a explorações baseadas em string.
  • O URIEncode cria um espaço em + , o RawUrlEncode cria um espaço em %20 por meio de pesquisas de matriz.

Disclaimer: Eu não toquei C em anos, e eu não olhei para EBCDIC em um tempo muito longo. Se eu estiver errado em algum lugar, me avise.

Implementações sugeridas

Com base em tudo isso, rawurlencode é o caminho a percorrer na maioria das vezes. Como você vê na resposta de Jonathan Fingland, fique com ele na maioria dos casos. Ele lida com o esquema moderno para componentes de URI, onde como urlencode faz as coisas do jeito old school, onde + significava "espaço".

Se você estiver tentando converter entre o formato antigo e os novos formatos, certifique-se de que seu código não fique maluco e transforme algo que seja um sinal decodificado em um espaço por codificação acidentalmente dupla ou cenários "oops" semelhantes em torno desse espaço / 20% / + problema.

Se você está trabalhando em um sistema mais antigo com software mais antigo que não prefere o novo formato, use o urlencode, no entanto, acredito que o% 20 será realmente compatível, já que sob o antigo padrão% 20 funcionou, simplesmente não preferido. Dê uma chance se estiver disposto a brincar, conte-nos como funcionou para você.

Basicamente, você deve ficar com o raw, a menos que o seu sistema EBCDIC realmente te odeie. A maioria dos programadores nunca encontrará o EBCDIC em nenhum sistema feito depois do ano 2000, talvez até mesmo em 1990 (isso é difícil, mas ainda é provável na minha opinião).


Answer #4

Eu acredito que urlencode é para parâmetros de consulta, enquanto o rawurlencode é para os segmentos de caminho. Isso se deve principalmente a %20 para segmentos de caminho vs + para parâmetros de consulta. Veja esta resposta que fala sobre os espaços: Quando codificar o espaço para mais (+) ou% 20?

No entanto, o %20 agora também funciona nos parâmetros de consulta, e é por isso que o rawurlencode é sempre mais seguro. No entanto, o sinal de mais tende a ser usado onde a experiência do usuário de edição e legibilidade dos parâmetros de consulta é importante.

Note que isso significa que rawurldecode não decodifica + em espaços ( http://au2.php.net/manual/en/function.rawurldecode.php ). É por isso que o $ _GET é sempre passado automaticamente pelo urldecode , o que significa que + e %20 são ambos decodificados em espaços.

Se você quiser que a codificação e a decodificação sejam consistentes entre entradas e saídas e você selecionou sempre usar + e não %20 para parâmetros de consulta, então urlencode é bom para parâmetros de consulta (chave e valor).

A conclusão é:

Segmentos de caminho - use sempre rawurlencode / rawurldecode

Parâmetros de consulta - para decodificação sempre use urldecode (feito automaticamente), para codificação, tanto rawurlencode ou urlencode é bom, basta escolher um para ser consistente, especialmente quando se compara URLs.


Answer #5

Isso dependerá do seu propósito. Se a interoperabilidade com outros sistemas é importante, então parece rawurlencode é o caminho a percorrer. A única exceção são os sistemas legados que esperam que a string de consulta siga o estilo de codificação de formulário de espaços codificados como + em vez de% 20 (nesse caso, você precisa de urlencode).

O rawurlencode segue o RFC 1738 antes do PHP 5.3.0 e do RFC 3986 depois (veja http://us2.php.net/manual/en/function.rawurlencode.php )

Retorna uma string na qual todos os caracteres não alfanuméricos exceto -_. ~ Foram substituídos por um sinal de porcentagem (%) seguido por dois dígitos hexadecimais. Essa é a codificação descrita em »RFC 3986 para proteger os caracteres literais de serem interpretados como delimitadores de URL especiais e para proteger os URLs de serem desconfigurados pela mídia de transmissão com conversões de caracteres (como alguns sistemas de email).

Nota sobre RFC 3986 vs 1738. rawurlencode antes de php 5.3 codificado o caractere til ( ~ ) de acordo com RFC 1738. A partir do PHP 5.3, no entanto, rawurlencode segue RFC 3986 que não requer codificação de caracteres de til.

urlencode codifica espaços como sinais de mais (não como %20 como feito em rawurlencode) (veja http://us2.php.net/manual/en/function.urlencode.php )

Retorna uma string na qual todos os caracteres não alfanuméricos, exceto -_. foram substituídos por um sinal de porcentagem (%) seguido por dois dígitos hexadecimais e espaços codificados como sinais de mais (+). Ele é codificado da mesma forma que os dados postados de um formulário da WWW são codificados, da mesma forma que no tipo de mídia application / x-www-form-urlencoded. Isso difere da »codificação RFC 3986 (veja rawurlencode ()) porque, por razões históricas, os espaços são codificados como sinais de mais (+).

Isso corresponde à definição para application / x-www-form-urlencoded no RFC 1866 .

Leitura Adicional:

Você também pode querer ver a discussão em http://bytes.com/groups/php/5624-urlencode-vs-rawurlencode .

Além disso, o RFC 2396 vale a pena dar uma olhada. O RFC 2396 define a sintaxe válida do URI. A parte principal em que estamos interessados ​​é no 3.4 Componente de Consulta:

Dentro de um componente de consulta, os caracteres ";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
são reservados.

Como você pode ver, o + é um caractere reservado na string de consulta e, portanto, precisaria ser codificado de acordo com a RFC 3986 (como em rawurlencode).


Answer #6

simples * rawurlencode o caminho - caminho é a parte antes do "?" - os espaços devem ser codificados como% 20 * urlencode a string de consulta - A string de consulta é a parte depois do "?" espaços são melhor codificados como "+" = rawurlencode é mais compatível geralmente


Answer #7
echo rawurlencode('http://www.google.com/index.html?id=asd asd');

rendimentos

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd%20asd

enquanto

echo urlencode('http://www.google.com/index.html?id=asd asd');

rendimentos

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd+asd

A diferença é o asd%20asd vs asd+asd

urlencode difere do RFC 1738 codificando espaços como + vez de %20





url-encoding