#navi_header|技術|
お仕事の関係で、HTTPリクエストのCookieヘッダの値はどう解釈されているのか調べてみました。
通常はCookieヘッダの値部分は、空白以外の値で、一般的には余計な記号など入り込まないように言語やライブラリ、フレームワーク、あるいはプログラマが処理してます。
しかし、特殊な要件を満たすため、普段は設定しない記号系の文字もCookieで使う必要がありました。
そこでいくつかのメジャーなWebアプリ実行環境について、実際にCookieに特殊な記号を入れて送ってみて、Webアプリケーション側ではどのように処理されて、見えるようになるのか調べてみました。また、逆に、特殊な記号を入れた値をアプリケーションからCookieとして設定すると、最終的なSet-Cookieでどのようにエンコードやエスケープされるかも確認してみました。
2014-02-08時点での確認状況
|言語/プラットフォーム|Cookieヘッダの解釈|Set-Cookieでのエンコード|h
|PHP5|URLデコードする|setcookie():URLエンコード, setrawcookie():そのまま|
|Tomcat7|一部記号を除きそのまま|基本そのまま、一部記号が入ると""囲み|
|Ruby(Rackベース)|URLデコードする|URLエンコードする|
|ASP|(未検証)|(未検証)|
|ASP.NET|(未検証)|(未検証)|
|ASP.NET MVC|(未検証)|(未検証)|
|Python(WSGIベース)|(未検証)|(未検証)|
|node.js|(未検証)|(未検証)|
|Play(Nettyベース)|(未検証)|(未検証)|
|(その他)|(未検証)|(未検証)|
- Cookieのname=valueのvalue部分に、";" や "," がそのまま入ると、いずれもname=valueの区切り文字としてRFCにある文字なので、そこで分割してしまう処理系が多いようです。
- 記号を含めた値でCookieを発行しようとすると、PHP5とRackの場合は、処理系の方で自動的にURLエンコードしてくれました。Javaの場合は""で囲んでくれました。
-- ただし本記事で確認しているのは記号系の扱いであり、日本語やバイナリデータの場合の挙動までは未確認です。Javaの場合は、アプリケーション側でURLエンコードやBase64エンコードするなど、何らかの対応が必要になるかもしれません。
- 参考:[Studying HTTP] HTTP Cookies
-- http://www.studyinghttp.net/cookies
細かい状況については、以下の個別の詳細確認状況を御覧ください。
#more||
----
#outline||
----
* 検証に使用した記号
検証に使用した文字種:
#pre||>
(0x20)
!
"
#
$
%
&
'
(
)
=
~
|
-
^
\
@
[
]
{
}
;
+
:
*
,
.
<
>
?
_
||<
HTTPリクエストのCookieヘッダの検証では、上記をエンコードせずにそのままサーバに対して送信したケースと、URLエンコードしたケースの2パターンを確認した。
サーバからのSet-Cookieの検証では、上記を含んだ値を言語処理系やライブラリ・フレームワークの提供する機能を用いてSet-Cookieした時の挙動を確認した。
* PHP
環境:
#pre||>
CentOS 6.5 (x64)
PHP 5.3.3 + Apache/2.2.15 (Apache 2.0 Handler)
magic_quotes_gpc : off
||<
サンプルコード:cookies.php (HTTPリクエストのCookie値の解釈を確認)
#pre||>
Cookie: abc=123 DEF; def=456bGHI
->
array(2) {
["abc"]=>
string(7) "123 DEF"
["def"]=>
string(7) "456bGHI"
}
Cookie: abc=123!DEF; def=456bGHI
->
string(7) "123!DEF"
Cookie: abc=123%DEF; def=456bGHI
->
string(5) "123(0xDE)F"
Cookie: abc=123+DEF; def=456bGHI
->
string(7) "123 DEF"
Cookie: abc=123,DEF; def=456bGHI
->
string(7) "123,DEF"
||<
";"がそのまま混入すると、そこでCookie値が分割されるようだ。
#pre||>
Cookie: abc=123;DEF; def=456bGHI
->
array(3) {
["abc"]=>
string(3) "123"
["DEF"]=>
string(0) ""
["def"]=>
string(7) "456bGHI"
}
||<
Cookie値がURLエンコードされていると、URLデコードされてアプリ空間から参照できた。
#pre||>
Cookie: abc=123%20DEF; def=456bGHI
->
array(2) {
["abc"]=>
string(7) "123 DEF"
["def"]=>
string(7) "456bGHI"
}
Cookie: abc=123%25DEF; def=456bGHI
->
string(7) "123%DEF"
Cookie: abc=123%3bDEF; def=456bGHI
->
string(7) "123;DEF"
Cookie: abc=123%2bDEF; def=456bGHI
->
string(7) "123+DEF"
Cookie: abc=123%3aDEF; def=456bGHI
->
string(7) "123:DEF"
Cookie: abc=123%2aDEF; def=456bGHI
->
string(7) "123*DEF"
Cookie: abc=123%2cDEF; def=456bGHI
->
string(7) "123,DEF"
Cookie: abc=123%2eDEF; def=456bGHI
->
string(7) "123.DEF"
||<
** Set-Cookieするときの特徴
setcookie()を使う場合は、URLエンコードする。
#pre||>
(setcookie() にセットした値)
->
(実際にサーバから送信されたSet-Cookieヘッダ)
123 DEF
->
Set-Cookie: abc=123+DEF
123_DEF
->
Set-Cookie: abc=123_DEF
123?DEF
->
Set-Cookie: abc=123%3FDEF
123.DEF
->
Set-Cookie: abc=123.DEF
123,DEF
->
Set-Cookie: abc=123%2CDEF
123*DEF
->
Set-Cookie: abc=123%2ADEF
123;DEF
->
Set-Cookie: abc=123%3BDEF
123+DEF
->
Set-Cookie: abc=123%2BDEF
123-DEF
->
Set-Cookie: abc=123-DEF
||<
setrawcookie()を使う場合は、URLエンコードされない。ただし、いくつかの文字は使えず、Set-Cookieヘッダは発行されずに、Warningが発生してしまう。
Warning: Cookie values cannot contain any of the following ',; \t\r\n\013\014' in ...
#pre||>
(setrawcookie() にセットした値)
->
(実際にサーバから送信されたSet-Cookieヘッダ)
123 DEF
->
(上記Warning発生)
123_DEF
->
Set-Cookie: abc=123_DEF
123?DEF
->
Set-Cookie: abc=123?DEF
123.DEF
->
Set-Cookie: abc=123.DEF
123,DEF
->
(上記Warning発生)
123*DEF
->
Set-Cookie: abc=123*DEF
123;DEF
->
(上記Warning発生)
123+DEF
->
Set-Cookie: abc=123+DEF
123-DEF
->
Set-Cookie: abc=123-DEF
||<
* Tomcat7 (Servlet 3.0)
環境:
#pre||>
CentOS 6.5 (x64)
Oracle JDK 1.7.0_51 (x64), Server VM
Apache Tomcat 7.0.50
||<
サンプルコード:cookies.jsp (HTTPリクエストのCookie値の解釈を確認)
#pre||>
<%@page
contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"
%>
Cookie Viewer
<%
Cookie[] cookies = request.getCookies();
if (null != cookies) {
for (Cookie c : cookies) {
out.println("Cookie[" + c.getName() + "]=[" + c.getValue() + "]
");
}
}
%>
||<
サンプルコード:setcookie.jsp (Set-Cookieの挙動を確認)
#pre||>
<%@page
contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"
%>
<%
String cn = request.getParameter("cn");
String cv = request.getParameter("cv");
if (null != cn && null != cv) {
response.addCookie(new Cookie(cn, cv));
}
%>
||<
** HTTPリクエストのCookie値の解釈
- URLデコードされず、そのまま取得される。
- ただし、"(" や ")" など、いくつかの記号が混入すると、それ以降の値は削除されてしまう。
- "" で囲むことで、削除されなくなる。
URLデコードされず、そのまま取得されるケース:
#pre||>
Cookie: abc=123%DEF; def=456bGHI
->
Cookie[abc]=[123%DEF]
Cookie: abc=123.DEF; def=456bGHI
->
Cookie[abc]=[123.DEF]
Cookie: abc=123+DEF; def=456bGHI
->
Cookie[abc]=[123+DEF]
Cookie: abc=123%2FDEF; def=456bGHI
->
Cookie[abc]=[123%2FDEF]
他: !, #, $, &, ', ~, |, -, ^, *, _
||<
以降の値が削除されてしまったケース:
#pre||>
Cookie: abc=123"DEF; def=456bGHI
->
Cookie[abc]=[123]
Cookie: abc=123,DEF; def=456bGHI
->
Cookie[abc]=[123]
他: (, ), =, \, @, [, ], {, }, ;, :, <, >, ?
||<
""で囲むことで、削除されなくなる例:
#pre||>
Cookie: abc="123,DEF"; def=456bGHI
->
Cookie[abc]=[123,DEF]
Cookie: abc="123@DEF"; def=456bGHI
->
Cookie[abc]=[123@DEF]
Cookie: abc="123(DEF"; def=456bGHI
->
Cookie[abc]=[123(DEF]
ただし " は変化なし。
Cookie: abc="123"DEF"; def=456bGHI
->
Cookie[abc]=[123]
→ \" としてバックスラッシュエスケープすれば残る。
Cookie: abc="123\"DEF"; def=456bGHI
->
Cookie[abc]=[123"DEF]
また、 \ はその文字だけ削除される。
Cookie: abc="123\DEF"; def=456bGHI
->
Cookie[abc]=[123DEF]
→ \\ としてバックスラッシュエスケープすれば残る。
Cookie: abc="123\\DEF"; def=456bGHI
->
Cookie[abc]=[123\DEF]
||<
** Set-Cookieするときの特徴
基本的にはそのままSet-Cookieヘッダに出力される。
#pre||>
(response.addCookie(new Cookie("abc", ...)) にセットした値)
->
(実際にサーバから送信されたSet-Cookieヘッダ)
123%DEF
->
Set-Cookie: abc=123%DEF
123&DEF
->
Set-Cookie: abc=123&DEF
123+DEF
->
Set-Cookie: abc=123+DEF
他、このケースで確認した記号: !, #, $, ', ~, |, -, ^, *, ., _
||<
一部の記号は "" で囲われ、 " 自体はバックスラッシュエスケープされる。また、"Version=1"が自動で追加された。
#pre||>
(response.addCookie(new Cookie("abc", ...)) にセットした値)
->
(実際にサーバから送信されたSet-Cookieヘッダ)
123\DEF
->
Set-Cookie: abc="123\DEF"; Version=1
123,DEF
->
Set-Cookie: abc="123,DEF"; Version=1
123;DEF
->
Set-Cookie: abc="123;DEF"; Version=1
123 DEF
->
Set-Cookie: abc="123 DEF"; Version=1
123"DEF
->
Set-Cookie: abc="123\"DEF"; Version=1
他、このケースで確認した記号: (, ), =, @, [, ], {, }, :, <, >, ?
||<
* Ruby(Rackベースアプリ)
環境:
#pre||>
ruby-2.0.0-p353-i386-mingw32
Windows7 Pro SP1 64bit
> gem environment
RubyGems Environment:
- RUBYGEMS VERSION: 2.0.14
- RUBY VERSION: 2.0.0 (2013-11-22 patchlevel 353) [i386-mingw32]
(...)
- RUBYGEMS PLATFORMS:
- ruby
- x86-mingw32
(...)
> gem install sinatra
rack-1.5.2
tilt-1.4.1
rack-protection-1.5.2
sinatra-1.4.4
||<
サンプルコード:
#pre||>
require 'sinatra'
get '/cookies' do
request.cookies.inspect
end
get '/setcookie' do
response.set_cookie(params[:cn], params[:cv])
end
get '/hi' do
"Hello World!"
end
||<
** HTTPリクエストのCookie値の解釈
URLデコードする。ただしURLエンコードされていない文字についてはそのまま素通しになる。
#pre||>
Cookie: abc=123 DEF; def=456bGHI
->
{"abc"=>"123 DEF", "def"=>"456bGHI"}
Cookie: abc=123!DEF; def=456bGHI
->
{"abc"=>"123!DEF", "def"=>"456bGHI"}
Cookie: abc=123%DEF; def=456bGHI
->
{"abc"=>"123\xDEF", "def"=>"456bGHI"}
Cookie: abc=123+DEF; def=456bGHI
->
{"abc"=>"123 DEF", "def"=>"456bGHI"}
||<
";"や","がそのまま混入すると、そこでCookie値が分割されるようだ。
#pre||>
Cookie: abc=123;DEF; def=456bGHI
->
{"abc"=>"123", "DEF"=>nil, "def"=>"456bGHI"}
Cookie: abc=123,DEF; def=456bGHI
->
{"abc"=>"123", "DEF"=>nil, "def"=>"456bGHI"}
||<
Cookie値がURLエンコードされていると、URLデコードされてアプリ空間から参照できた。
#pre||>
Cookie: abc=123%20DEF; def=456bGHI
->
{"abc"=>"123 DEF", "def"=>"456bGHI"}
Cookie: abc=123%25DEF; def=456bGHI
->
{"abc"=>"123%DEF", "def"=>"456bGHI"}
Cookie: abc=123%3bDEF; def=456bGHI
->
{"abc"=>"123;DEF", "def"=>"456bGHI"}
Cookie: abc=123%2bDEF; def=456bGHI
->
{"abc"=>"123+DEF", "def"=>"456bGHI"}
Cookie: abc=123%3aDEF; def=456bGHI
->
{"abc"=>"123:DEF", "def"=>"456bGHI"}
Cookie: abc=123%2aDEF; def=456bGHI
->
{"abc"=>"123*DEF", "def"=>"456bGHI"}
Cookie: abc=123%2cDEF; def=456bGHI
->
{"abc"=>"123,DEF", "def"=>"456bGHI"}
Cookie: abc=123%2eDEF; def=456bGHI
->
{"abc"=>"123.DEF", "def"=>"456bGHI"}
||<
** Set-Cookieするときの特徴
URLエンコードする。
#pre||>
(response.set_cookie("abc", ...) にセットした値)
->
(実際にサーバから送信されたSet-Cookieヘッダ)
123 DEF
->
Set-Cookie: abc=123+DEF
123_DEF
->
Set-Cookie: abc=123_DEF
123?DEF
->
Set-Cookie: abc=123%3FDEF
123.DEF
->
Set-Cookie: abc=123.DEF
123,DEF
->
Set-Cookie: abc=123%2CDEF
123*DEF
->
Set-Cookie: abc=123*DEF
123;DEF
->
Set-Cookie: abc=123%3BDEF
123+DEF
->
Set-Cookie: abc=123%2BDEF
123-DEF
->
Set-Cookie: abc=123-DEF
||<
#navi_footer|技術|