为什么传递给 PHP 的 cookie 或数据字符串中缺少“+”字符,我该如何更正此问题
why are "+" characters missing from cookies or data strings passed to PHP, and how can I correct this
我以为我有一个完美的方案,在访问者页面中使用 base64 编码的 cookie 数据来识别访问者。 (实际上 cookie 代表 RC4 编码,用 base64 重新处理得到 "cookie safe" 结果。由于 base 64 输出的字符在任何浏览器中对于 cookie 都是非法的,我相信这不会造成问题。我进一步希望通过 $_COOKIE 数组从 PHP 脚本中检查 cookie。一切似乎都很顺利,直到特定的 cookie 值最终被 base64 编码为...
9xu3EhM5+6duW4feCL4aHuxOceo=
将此 cookie 值写入或读取到我的浏览器绝对没有问题。如果我使用 javascript 创建它,然后使用浏览器的隐私选项检查它,它没有损坏。如果我通过 javascript 读取 cookie 并将其显示在 alert() 或控制台中,它也没有损坏。但是在 PHP $_COOKIE 数组中的 "reading that" cookie 上,我得到的是...
9xu3EhM5 6duW4feCL4aHuxOceo=
如果重要的话,这是 PHP 5.6。为什么缺少“+”符号?遗憾的是,问题并不局限于 $_COOKIE 数组!即使编写一个简单的 PHP 程序来响应我发送的内容(通过 GET 请求),我仍然看到响应中缺少“+”号。
如果这是与字符编码相关的问题,我看不出如何解决。即使我只是将我的 PHP 脚本 URL 插入浏览器的地址栏,在没有活动页面设置任何字符编码的情况下,“+”号也会在脚本的路径中丢失。而且我还验证了一个简单的脚本,除了用硬编码的 "non corrupt" 字符串响应外什么都不做,工作正常。
很明显,问题仅限于从浏览器到 PHP 的数据传输。即使我能想出一些疯狂的方案来补偿手动传递的字符串(比如通过 POST 请求),我也看不到任何方法来控制 PHP 脚本在数据是从 $_COOKIE 数组中提取。
我能做什么?我真的一直指望脚本能够完成这个看似简单的任务。
---编辑----------------
虽然我发现其他人抱怨这个神秘的“+”字符在发帖后不见了,但我没有看到简单的解决方案,因此决定实施自己的解决方案。因为我一直在我的 PHP 脚本中完成我所有的 base64(编码和解码),并且因为我的代码是唯一必须创建、存储和恢复这些字符串的地方,所以我决定运行 在使用它存储 cookie 之前,所有 base64 编码的字符串都通过此例程(如下)。同样,在 base-64 解码之前,我将通过它传递每个获得的 cookie(例如,通过 $_COOKIE 数组)。
// 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 safe" 字符替换“+”(我也决定也使用“=”),然后将编码值返回到页面,用作 cookie。
编辑-----
我添加并稍微修改了上面的 remove/replace "/" 字符,这对 $_COOKIE 数组来说不是问题,但是如果你想写一个文件或创建一个与 cookie 同名的目录。
请注意,正在处理的字符串的长度不会改变。当相同的(或站点上的另一个页面)再次 运行 我的 PHP 脚本,我恢复了 cookie,然后我可以通过我创建的相同 fix64() 调用将它传回,知道从那里我可以像普通的 base64 一样解码它。
我没有回答我自己的问题,因为我希望可以调用一些简单的 "official" PHP 设置来改变这种行为,我仍然希望这样的事情存在.但就我的情况而言,就目前而言,这是一种合理的方法,如果有一天我需要,可以很容易地撤销它。
setcookie() 自 PHP/4 以来就存在并产生 URL-encoded 值:
setcookie('a', '9xu3EhM5+6duW4feCL4aHuxOceo=');
Set-Cookie: a=9xu3EhM5%2B6duW4feCL4aHuxOceo%3D
因此,$_COOKIE
URL-decodes 值:
Cookie: a=9xu3EhM5%2B6duW4feCL4aHuxOceo%3D
array(1) {
["a"]=>
string(28) "9xu3EhM5+6duW4feCL4aHuxOceo="
}
因为 PHP/5 还有 setrawcookie() 的唯一目的不是 URL-encoding 值:
setrawcookie('b', '9xu3EhM5+6duW4feCL4aHuxOceo=');
Set-Cookie: b=9xu3EhM5+6duW4feCL4aHuxOceo=
但是 $_COOKIE
仍然假定 URL-encoded 输入和内容中断(+
是 U-0020 'SPACE' 的过时编码,又名好旧的空白):
Cookie: b=9xu3EhM5+6duW4feCL4aHuxOceo=
array(1) {
["b"]=>
string(28) "9xu3EhM5 6duW4feCL4aHuxOceo="
}
有趣的是,我找不到 setrawcookie()
的对应项。这让您不得不编写自己的解析器 :-! $_SERVER['HTTP_COOKIE']
包含 HTTP header 的原始值,这是一个 semicolon-separated 列表,例如:
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;
}
我猜你可以使用这段代码并跳过解码部分。
我以为我有一个完美的方案,在访问者页面中使用 base64 编码的 cookie 数据来识别访问者。 (实际上 cookie 代表 RC4 编码,用 base64 重新处理得到 "cookie safe" 结果。由于 base 64 输出的字符在任何浏览器中对于 cookie 都是非法的,我相信这不会造成问题。我进一步希望通过 $_COOKIE 数组从 PHP 脚本中检查 cookie。一切似乎都很顺利,直到特定的 cookie 值最终被 base64 编码为...
9xu3EhM5+6duW4feCL4aHuxOceo=
将此 cookie 值写入或读取到我的浏览器绝对没有问题。如果我使用 javascript 创建它,然后使用浏览器的隐私选项检查它,它没有损坏。如果我通过 javascript 读取 cookie 并将其显示在 alert() 或控制台中,它也没有损坏。但是在 PHP $_COOKIE 数组中的 "reading that" cookie 上,我得到的是...
9xu3EhM5 6duW4feCL4aHuxOceo=
如果重要的话,这是 PHP 5.6。为什么缺少“+”符号?遗憾的是,问题并不局限于 $_COOKIE 数组!即使编写一个简单的 PHP 程序来响应我发送的内容(通过 GET 请求),我仍然看到响应中缺少“+”号。
如果这是与字符编码相关的问题,我看不出如何解决。即使我只是将我的 PHP 脚本 URL 插入浏览器的地址栏,在没有活动页面设置任何字符编码的情况下,“+”号也会在脚本的路径中丢失。而且我还验证了一个简单的脚本,除了用硬编码的 "non corrupt" 字符串响应外什么都不做,工作正常。
很明显,问题仅限于从浏览器到 PHP 的数据传输。即使我能想出一些疯狂的方案来补偿手动传递的字符串(比如通过 POST 请求),我也看不到任何方法来控制 PHP 脚本在数据是从 $_COOKIE 数组中提取。
我能做什么?我真的一直指望脚本能够完成这个看似简单的任务。
---编辑----------------
虽然我发现其他人抱怨这个神秘的“+”字符在发帖后不见了,但我没有看到简单的解决方案,因此决定实施自己的解决方案。因为我一直在我的 PHP 脚本中完成我所有的 base64(编码和解码),并且因为我的代码是唯一必须创建、存储和恢复这些字符串的地方,所以我决定运行 在使用它存储 cookie 之前,所有 base64 编码的字符串都通过此例程(如下)。同样,在 base-64 解码之前,我将通过它传递每个获得的 cookie(例如,通过 $_COOKIE 数组)。
// 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 safe" 字符替换“+”(我也决定也使用“=”),然后将编码值返回到页面,用作 cookie。
编辑----- 我添加并稍微修改了上面的 remove/replace "/" 字符,这对 $_COOKIE 数组来说不是问题,但是如果你想写一个文件或创建一个与 cookie 同名的目录。
请注意,正在处理的字符串的长度不会改变。当相同的(或站点上的另一个页面)再次 运行 我的 PHP 脚本,我恢复了 cookie,然后我可以通过我创建的相同 fix64() 调用将它传回,知道从那里我可以像普通的 base64 一样解码它。
我没有回答我自己的问题,因为我希望可以调用一些简单的 "official" PHP 设置来改变这种行为,我仍然希望这样的事情存在.但就我的情况而言,就目前而言,这是一种合理的方法,如果有一天我需要,可以很容易地撤销它。
setcookie() 自 PHP/4 以来就存在并产生 URL-encoded 值:
setcookie('a', '9xu3EhM5+6duW4feCL4aHuxOceo=');
Set-Cookie: a=9xu3EhM5%2B6duW4feCL4aHuxOceo%3D
因此,$_COOKIE
URL-decodes 值:
Cookie: a=9xu3EhM5%2B6duW4feCL4aHuxOceo%3D
array(1) {
["a"]=>
string(28) "9xu3EhM5+6duW4feCL4aHuxOceo="
}
因为 PHP/5 还有 setrawcookie() 的唯一目的不是 URL-encoding 值:
setrawcookie('b', '9xu3EhM5+6duW4feCL4aHuxOceo=');
Set-Cookie: b=9xu3EhM5+6duW4feCL4aHuxOceo=
但是 $_COOKIE
仍然假定 URL-encoded 输入和内容中断(+
是 U-0020 'SPACE' 的过时编码,又名好旧的空白):
Cookie: b=9xu3EhM5+6duW4feCL4aHuxOceo=
array(1) {
["b"]=>
string(28) "9xu3EhM5 6duW4feCL4aHuxOceo="
}
有趣的是,我找不到 setrawcookie()
的对应项。这让您不得不编写自己的解析器 :-! $_SERVER['HTTP_COOKIE']
包含 HTTP header 的原始值,这是一个 semicolon-separated 列表,例如:
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;
}
我猜你可以使用这段代码并跳过解码部分。