php setcookie對cookie值進行urlencode的問題及解決
阿新 • • 發佈:2019-01-04
1. 問題
有如下程式碼
setcookie.php
class Cookie{
protected $_key = "person";
protected $_val = "name:ball,sex:male";
public function set(){
$duration = 0;
$path = "/";
setcookie($this->_key, $this->_val, $duration, $path);
}
public function get(){
echo $_COOKIE[$this->_key];
}
}
我們先呼叫set(), 再呼叫get()。頁面上輸出
name:ball,sex:male
按說這是符合預期的。
但是使用chrome的debug工具檢視cookie,發現person的值為
name%3Aball%2Csex%3Amale
在console中執行document.cookie,結果為
"person=name%3Aball%2Csex%3Amale"
也就是說,雖然php側能設定並正常的取到cookie值,但是從瀏覽器或js側看來,這個cookie是被編了碼的。不方便js使用,也不方便人工排查問題時檢視cookie。
2.解決
查手冊,發現setcookie的確是對cookie值進行了urlencode。怎麼繞開呢?我們想到setcookie的本質就是在response header中加入Set-Cookie響應頭,於是決定嘗試直接用header方法。set()程式碼調整如下:
public function set(){
$str = sprintf("Set-Cookie:%s=%s;path=/", $this->_key, $this->_val);
header($str);
}
這時再從chrome側檢視cookie中的person值如下,並沒有進行編碼。
name:ball,sex:male
3.風險
2中的方法雖然解決了cookie值被編碼的問題,但是會不會帶來風險呢?
答案是會的。比如,如果cookie中帶了分號(http協議中,Set-Cookie用來分隔鍵值對的關鍵字),就會產生bug。
為了詳細說明問題,我們先看下2中例子的response header(主要擷取Set-Cookie部分)
Server: nginx/1.4.1
Set-Cookie: person=name:ball,sex:male;path=/
Transfer-Encoding: chunked
下面修改程式碼
將
protected $_val = "name:ball,sex:male";
改為
protected $_val = "name:ball;sex:male";
response header變為
Server: nginx/1.4.1
Set-Cookie: person=name:ball;sex:male;path=/
Transfer-Encoding: chunked
ball後的分號將person值打斷,後面的sex:male;被協議解析為無法識別的鍵值對,因而忽略。
get()方法的輸出及瀏覽器中看的person值也變為
name:ball
4.建議
cookie值儘量簡單,不含特殊符號,這樣即使setcookie進行了urlencode也不會有什麼變化。如果一定需要包含特殊字元,請注意避開協議保留字。