PHP無法在$_SERVER內取得自訂header

最近測試新版PHP時,有人提到他的程式在新的環境執行時無法取得自訂的header。可是,在舊環境執行卻可正常取得該header。

程式是採用$_SERVER取得header。原以為是新版PHP的問題,但查了PHP的版本變更記錄(PHP: Appendices - Manual)卻沒有相關的說明。於是,懷疑和apache有關,傳給PHP的資料就有問題。

此次,除了PHP升級外,Apache也一併升級到2.4。在官方說明-Overview of new features in Apache HTTP Server 2.4 - Apache HTTP Server Version 2.中提到,Apache 2.4為了增加安全做了以下調整…
Translation of headers to environment variables is more strict than before to mitigate some possible cross-site-scripting attacks via header injection. Headers containing invalid characters (including underscores) are now silently dropped. Environment Variables in Apache has some pointers on how to work around broken legacy clients which require such headers. (This affects all modules which use these environment variables.)
依據上述資訊,重新查了有問題的程式。原來,程式中所自訂的header名稱中包含了『_』(底線、underscores)。觸發了Apache 2.4上述的安全加強,PHP就無法在$_SERVER取得header名稱中有底線『_』的header。當然,自訂的header名稱中沒有使用底線『_』的header,是可以正常取得的。

以下,簡單說明問題發生的原因、與相關的狀況,與處理方式…

下圖,我以postman傳送『Test-Header1』、『Test_Header2』兩個header給PHP。程式中僅用var_dump()丟出$_SERVER內容,結果如下…
  • Test-Header1,被改名為HTTP_TEST_HEADER1(『-』變成了『_』)
  • Test_Header2,因為有底線、underscores(_),因此$_SERVER中沒有這個header
apache24-header-underscores-orgapache 2.4
會將不合法字元的header改名(綠色箭頭處)
header名稱中有底線者,則被丟棄

為何會有上面兩種結果?在Environment Variables in Apache - Apache HTTP Server Version 2.4中有提到…
Some Caveats
  • For portability reasons, the names of environment variables may contain only letters, numbers, and the underscore character. In addition, the first character may not be a number. Characters which do not match this restriction will be replaced by an underscore when passed to CGI scripts and SSI pages.
  • A special case are HTTP headers which are passed to CGI scripts and the like via environment variables (see below). They are converted to uppercase and only dashes are replaced with underscores; if the header contains any other (invalid) character, the whole header is silently dropped.

此狀況解決方案有兩種
  1. 修改apache的httpd.conf
  2. 不要使用$_SERVER,改用apache_request_headers()

方法一:修改apache的httpd.conf
以剛剛的例子,可做在httpd.conf中做以下的設定,讓apache仿原本的作法傳出HTTP_TEST_HEADER2

SetEnvIfNoCase ^Test.Header2$ ^(.*)$ HTTP_TEST_HEADER2=$1

修改後的執行結果如下…
apache24-header-underscores-fixr藉由調整apache httpd.conf
讓原本被丟棄不合法的header,又可以傳給PHP的$_SERVER
請注意第一個綠色箭頭


方法二:不要使用$_SERVER,改用apache_request_headers()
個人覺得這作法比較簡單,只需程式開發人員自己調整即可。畢竟,一般負責程式開發、和系統維護者為不同的人。另外,要考量httpd.conf既有設定內容的複雜度。
註:如使用CodeIgniter開發,可改用$this->input->request_headers()。

以下,是使用var_dump()呈現apache_request_headers()所傳回的內容。請注意綠色箭頭處,和上面截圖的差異。
apache24-header-apache_request_headers使用apache_request_headers(),可以得到原始的header
注意:是原始header的名稱,不會有『HTTP_』

但個人覺得方法一不方便…因為…需要逐一針對有問題的header做設定…除非程式真的無法異動,才考量此解決方案吧~

不過,重點依舊是,header取名還是按照規範吧~

參考資料



留言