почему символы «+» отсутствуют в файлах cookie или строках данных, передаваемых в PHP, и как я могу это исправить - PullRequest
2 голосов
/ 03 июля 2019

Я думал, что у меня есть идеальная схема, использующая данные в кодировке base64 для файлов cookie на страницах посетителей, чтобы идентифицировать посетителя. (На самом деле файлы cookie представляют собой кодированный RC4, повторно обработанный с помощью base64, чтобы получить «безопасный файл cookie». Поскольку в любом браузере нет выводимых символов с помощью Base 64, которые являются недопустимыми для файлов cookie, я был уверен, что это не вызовет проблем. Далее я надеялся проверить cookie из PHP-скрипта через массив $ _COOKIE. Казалось, что все идет хорошо, пока конкретное значение cookie не окажется в кодировке base64 как ...

9xu3EhM5 + 6duW4feCL4aHuxOceo =

Определенно не было проблем с записью или чтением этого значения cookie в моем браузере. Если я создаю его с помощью JavaScript, а затем проверяю его с помощью параметров конфиденциальности браузера, он НЕ поврежден. Если я читаю cookie через javascript и отображаю его в alert () или на консоли, он также НЕ поврежден. Но после «чтения» этого файла cookie из массива PHP $ _COOKIE я получил следующее сообщение:

9xu3EhM5 6duW4feCL4aHuxOceo =

Это PHP 5.6, если это имеет значение. Почему отсутствует символ «+»? И, к сожалению, проблема не ограничивается массивом $ _COOKIE! Даже при написании простой PHP-программы, которая отвечает мне тем, что я отправляю (через GET-запрос), я все еще вижу знак «+» в ответе.

Если это проблема, связанная с кодировкой символов, я не вижу, как это сделать. Даже если я просто вставлю URL-адрес своего PHP-скрипта в адресную строку браузера, где ни одна активная страница не установила кодировку символов, знак «+» теряется по пути к скрипту. И я также проверил, что простой скрипт, который ничего не делает, кроме как отвечает жестко закодированной «не поврежденной» строкой, работает нормально.

Очевидно, что проблема заключается в передаче данных из браузера в PHP. И даже если бы я мог придумать какую-нибудь сумасшедшую схему для компенсации строк, передаваемых вручную (например, с помощью запроса POST), я не вижу способа контролировать то, что PHP-скрипт видит, когда данные извлекаются из массива $ _COOKIE.

Что я могу сделать? Я действительно рассчитывал на то, что скрипт сможет выполнить эту, казалось бы, простую задачу.

--- EDIT ---------------

Хотя я обнаружил, что другие жалуются на пропажу таинственного символа «+» с момента публикации, я не нашел простого решения и решил реализовать свое собственное. Так как я в любом случае выполняю все свои base64 (кодирую и декодирую) из своих скриптов PHP, и так как мой код - единственное место, где эти строки должны быть созданы, сохранены и восстановлены, я решил запустить все закодированные base64 Строки через эту процедуру (ниже), прежде чем использовать его для хранения куки. Аналогично, я передам каждый полученный cookie (например, через массив $ _COOKIE) через него до декодирования Base-64.

// from browser to PHP. substitute troublesome chars with 
// other cookie safe chars, or vis-versa.  

function fix64($inp) {
    $out =$inp;
    for($i = 0; $i < strlen($inp); $i++) {
        $c = $inp[$i];
        switch ($c) {
          case '+':  $c = '*'; break;   // definitly won't transfer!
          case '*':  $c = '+'; break;

          case '=':  $c = ':'; break; // = symbol seems like a bad idea
          case ':':  $c = '='; break;

          case '/':  $c = '_'; break; // no good for dir name!!!
          case '_':  $c=  '/'; break;

            default: continue;
            }
        $out[$i] = $c;
        }
    return $out;
    }

Я просто заменяю "+" (и я также решил "=") другими символами "cookie", прежде чем возвращать закодированное значение на страницу, для использования в качестве cookie.

EDIT ----- Я немного добавил и изменил вышеприведенное, чтобы также удалить / заменить символ "/", что не является проблемой для массива $ _COOKIE, но это неприятный символ, если, например, вы хотите написать файл или создать каталог с тем же именем, что и cookie.

Обратите внимание, что длина обрабатываемой строки не изменяется. Когда та же самая (или другая страница на сайте) снова запускает мой PHP-скрипт и я восстанавливаю cookie, я могу затем передать его обратно через тот же вызов fix64 (), который я создал, зная, что оттуда я могу декодировать его как обычный base64 .

Я не ответил на свой собственный вопрос, так как надеялся, что будет какая-то простая «официальная» настройка PHP, которую я мог бы вызвать, которая изменит это поведение, и я все еще надеюсь, что такая вещь существует. Но для моего случая и на данный момент это разумный подход, который можно легко изменить, если мне когда-нибудь понадобится.

1 Ответ

2 голосов
/ 03 июля 2019

setcookie () существует с PHP / 4 и выдает значения в кодировке URL:

setcookie('a', '9xu3EhM5+6duW4feCL4aHuxOceo=');
Set-Cookie: a=9xu3EhM5%2B6duW4feCL4aHuxOceo%3D

Соответственно, $_COOKIE URL-декодирует значения:

Cookie: a=9xu3EhM5%2B6duW4feCL4aHuxOceo%3D
array(1) {
  ["a"]=>
  string(28) "9xu3EhM5+6duW4feCL4aHuxOceo="
}

Начиная с PHP / 5, есть также setrawcookie () с единственной целью - не значениями кодирования URL:

setrawcookie('b', '9xu3EhM5+6duW4feCL4aHuxOceo=');
Set-Cookie: b=9xu3EhM5+6duW4feCL4aHuxOceo=

Но $_COOKIE по-прежнему предполагает кодированный URL и разрывы ввода (+ является устаревшей кодировкой для U-0020 'SPACE', то есть старого доброго пробела):

Cookie: b=9xu3EhM5+6duW4feCL4aHuxOceo=
array(1) {
  ["b"]=>
  string(28) "9xu3EhM5 6duW4feCL4aHuxOceo="
}

Интересно, что я не смог найти аналога для setrawcookie(). Это оставляет вас в ситуации необходимости написать свой собственный парсер: -! $_SERVER['HTTP_COOKIE'] содержит необработанное значение заголовка HTTP, который представляет собой разделенный точкой с запятой список, например ::

a=9xu3EhM5%2B6duW4feCL4aHuxOceo%3D; b=9xu3EhM5+6duW4feCL4aHuxOceo=

Например, в микрорамке Slim есть метод Cookies :: parseHeader () , который делает именно это (не знаю почему, поскольку они все равно urldecode()):

public static function parseHeader($header)
{
    if (is_array($header) === true) {
        $header = isset($header[0]) ? $header[0] : '';
    }
    if (is_string($header) === false) {
        throw new InvalidArgumentException('Cannot parse Cookie data. Header value must be a string.');
    }
    $header = rtrim($header, "\r\n");
    $pieces = preg_split('@[;]\s*@', $header);
    $cookies = [];
    foreach ($pieces as $cookie) {
        $cookie = explode('=', $cookie, 2);
        if (count($cookie) === 2) {
            $key = urldecode($cookie[0]);
            $value = urldecode($cookie[1]);
            if (!isset($cookies[$key])) {
                $cookies[$key] = $value;
            }
        }
    }
    return $cookies;
}

Полагаю, вы можете использовать этот код и пропустить часть декодирования.

...