home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

技術/HTTP/Cookie, Set-Cookieヘッダにおける記号の扱いのバラつきについて

技術/HTTP/Cookie, Set-Cookieヘッダにおける記号の扱いのバラつきについて

技術 / HTTP / Cookie, Set-Cookieヘッダにおける記号の扱いのバラつきについて
id: 1260 所有者: msakamoto-sf    作成日: 2014-02-08 21:13:53
カテゴリ: HTTP 

お仕事の関係で、HTTPリクエストのCookieヘッダの値はどう解釈されているのか調べてみました。

通常はCookieヘッダの値部分は、空白以外の値で、一般的には余計な記号など入り込まないように言語やライブラリ、フレームワーク、あるいはプログラマが処理してます。
しかし、特殊な要件を満たすため、普段は設定しない記号系の文字もCookieで使う必要がありました。
そこでいくつかのメジャーなWebアプリ実行環境について、実際にCookieに特殊な記号を入れて送ってみて、Webアプリケーション側ではどのように処理されて、見えるようになるのか調べてみました。また、逆に、特殊な記号を入れた値をアプリケーションからCookieとして設定すると、最終的なSet-Cookieでどのようにエンコードやエスケープされるかも確認してみました。

2014-02-08時点での確認状況

言語/プラットフォーム Cookieヘッダの解釈 Set-Cookieでのエンコード
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

細かい状況については、以下の個別の詳細確認状況を御覧ください。



検証に使用した記号

検証に使用した文字種:

(0x20)
!
"
#
$
%
&
'
(
)
=
~
|
-
^
\
@
[
]
{
}
;
+
:
*
,
.
<
>
?
_

HTTPリクエストのCookieヘッダの検証では、上記をエンコードせずにそのままサーバに対して送信したケースと、URLエンコードしたケースの2パターンを確認した。

サーバからのSet-Cookieの検証では、上記を含んだ値を言語処理系やライブラリ・フレームワークの提供する機能を用いてSet-Cookieした時の挙動を確認した。

PHP

環境:

CentOS 6.5 (x64)
PHP 5.3.3 + Apache/2.2.15 (Apache 2.0 Handler)
magic_quotes_gpc : off

サンプルコード:cookies.php (HTTPリクエストのCookie値の解釈を確認)

<?php var_dump($_COOKIE);

サンプルコード:setcookie.php (setcookie()の動きを確認)

<?php setcookie(@$_GET['cn'], @$_GET['cv']);

サンプルコード:setrawcookie.php (setrawcookie()の動きを確認)

<?php setrawcookie(@$_GET['cn'], @$_GET['cv']);

HTTPリクエストのCookie値の解釈

URLデコードする。ただしURLエンコードされていない文字についてはそのまま素通しになる。

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値が分割されるようだ。

Cookie: abc=123;DEF; def=456bGHI
->
array(3) {
  ["abc"]=>
  string(3) "123"
  ["DEF"]=>
  string(0) ""
  ["def"]=>
  string(7) "456bGHI"
}

Cookie値がURLエンコードされていると、URLデコードされてアプリ空間から参照できた。

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エンコードする。

(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 ...
(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)

環境:

CentOS 6.5 (x64)
Oracle JDK 1.7.0_51 (x64), Server VM
Apache Tomcat 7.0.50

サンプルコード:cookies.jsp (HTTPリクエストのCookie値の解釈を確認)

<%@page
contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"
%>
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Cookie Viewer</title>
</head>
<body>
<%
Cookie[] cookies = request.getCookies();
if (null != cookies) {
  for (Cookie c : cookies) {
    out.println("Cookie[" + c.getName() + "]=[" + c.getValue() + "]<br>");
  }
}
%>
</body>
</html>

サンプルコード:setcookie.jsp (Set-Cookieの挙動を確認)

<%@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デコードされず、そのまま取得されるケース:

Cookie: abc=123%DEF; def=456bGHI
->
Cookie[abc]=[123%DEF]<br>

Cookie: abc=123.DEF; def=456bGHI
->
Cookie[abc]=[123.DEF]<br>

Cookie: abc=123+DEF; def=456bGHI
->
Cookie[abc]=[123+DEF]<br>

Cookie: abc=123%2FDEF; def=456bGHI
->
Cookie[abc]=[123%2FDEF]<br>

他: !, #, $, &, ', ~, |, -, ^, *, _

以降の値が削除されてしまったケース:

Cookie: abc=123"DEF; def=456bGHI
->
Cookie[abc]=[123]<br>

Cookie: abc=123,DEF; def=456bGHI
->
Cookie[abc]=[123]<br>

他: (, ), =, \, @, [, ], {, }, ;, :, <, >, ?

""で囲むことで、削除されなくなる例:

Cookie: abc="123,DEF"; def=456bGHI
->
Cookie[abc]=[123,DEF]<br>

Cookie: abc="123@DEF"; def=456bGHI
->
Cookie[abc]=[123@DEF]<br>

Cookie: abc="123(DEF"; def=456bGHI
->
Cookie[abc]=[123(DEF]<br>

ただし " は変化なし。
Cookie: abc="123"DEF"; def=456bGHI
->
Cookie[abc]=[123]<br>

→ \" としてバックスラッシュエスケープすれば残る。
Cookie: abc="123\"DEF"; def=456bGHI
->
Cookie[abc]=[123"DEF]<br>


また、 \ はその文字だけ削除される。
Cookie: abc="123\DEF"; def=456bGHI
->
Cookie[abc]=[123DEF]<br>

→ \\ としてバックスラッシュエスケープすれば残る。
Cookie: abc="123\\DEF"; def=456bGHI
->
Cookie[abc]=[123\DEF]<br>

Set-Cookieするときの特徴

基本的にはそのままSet-Cookieヘッダに出力される。

(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"が自動で追加された。

(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ベースアプリ)

環境:

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

サンプルコード:

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エンコードされていない文字についてはそのまま素通しになる。

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値が分割されるようだ。

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デコードされてアプリ空間から参照できた。

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エンコードする。

(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


プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2014-02-08 21:21:16
md5:e8f326822994d861db3b210e686d7452
sha1:0cd0b54716b182a6528c60462f4566bc649fab47
コメント
コメントを投稿するにはログインして下さい。