LoginSignup
4
3

More than 5 years have passed since last update.

RewriteRuleのCO=でドメイン属性なしが出来ない問題とその解決

Last updated at Posted at 2015-11-25

序文

mod_rewriteRewriteRuleでは[CO=xx] という命令を使うとクッキー発行できる、ということになっているがこの仕様が糞すぎて使いものにならないので解決編の設定を使ったほうが良いよというお話。

まとめエントリ的には最初に結論を書いておいた方が後で見る人に親切だろうから解決策を最初においておく。

解決編:CO=なんて要らんかったんや!

で、RewriteRuleの糞仕様に散々悩まされた結果、そんなん使わなきゃいいんじゃね?てことでCO=を使わないで自分でヘッダ出すことにした。これなら完全に自分でヘッダ内容コントロールできるし最初からこうしてれば良かったわ。

CO=なんかを使う奴は情弱
RewriteRule . - [E=cookie_foo:bar]
Header add Set-Cookie "foo=%{cookie_foo}e; path=/; max-age=1800" env=cookie_foo
  • クッキーに入れたい値は一旦環境変数に入れて、環境変数の有無を見て Header add する。
  • Set-Cookieは複数出力されるのが普通な仕様のヘッダなので、set でも append でもなく add で出力。
  • この例では固定値だけど本来はRewriteCondやRewriteRuleのマッチで作ったグループを$1とか%1とかで後方参照してクッキーにセットするのが目的なのでmod_rewriteを使っている。その場合 [E=foo:$1] とかすれば良い。

CO=の問題点

こっから先は色々試した軌跡なだけなので気になる人以外見なくて良いです。

  • domain指定が無いクッキーを発行できない

コレに尽きる。いや他にもpathも無しに出来ないとか幾つか突っ込みどころはあるけどそれらは元々指定するだろうしそんなに困ることはないのでどうでもいい。

CookieのDomain属性は 指定しない が一番安全 | 徳丸浩の日記

でも指摘されているように、domain属性は無いのが一番範囲限定できて望ましい。

なのに CO= では domain 属性が必須になっているためにそのホスト名のみに範囲を限定することが出来ない。

例えば www.example.com でドメイン指定なしで有効期限30分のクッキーを発行したい場合の設定を色々試してみよう、とても残念な結果が得られる。

1.本来やりたいことをイメージした設定

本来やりたいことをイメージした設定
RewriteRule . - [CO=foo1:bar::1800:/]
出力されるヘッダ
Set-Cookie: foo1=bar; path=/; domain=1800; expires=Wed, 25-Nov-2015 07:58:31 GMT

ファッ!?なぜかドメイン部に有効期限の値が使われとる!ちなみにexpiresは30分後じゃなく現在時刻(多分/が数字にパース出来なかったときのデフォルト)が入ってる。pathも多分未指定に解釈された筈だがコレも何故か省略不可な仕様なのでデフォルトのpath=/が帰ってきていると思われる。ちなみにソース確認したところセパレータ(:または;)が連続した場合の処理が適当で空カラムは存在しない事になってた。もう色々だめだこれ…。

2.ドメイン部分に空の代わりに-とか書いてみる

ドメイン部分に空の代わりに-とか書いてみる
RewriteRule . - [CO=foo2:bar:-:1800:/]
出力されるヘッダ
Set-Cookie: foo2=bar; path=/; domain=-; expires=Wed, 25-Nov-2015 08:28:31 GMT

ちょ、domain=- とかそのまま出すなよww 当然ブラウザはこんなクッキーは食ってくれないので意味が無いヘッダになる。

3.どうせabc.www.example.comとか使ってないし妥協でホスト名を付けてみる

どうせabc.www.example.comとか使ってないし妥協でホスト名をそのまま付けてみる
RewriteRule . - [CO=foo3:bar:www.example.com:1800:/]
出力されるヘッダ
Set-Cookie: foo3=bar; path=/; domain=www.example.com; expires=Wed, 25-Nov-2015 08:28:31 GMT

HTTPの仕様ではこれで *.www.example.comwww.example.com の範囲でクッキーを送ってもらえるはずなので、使用する予定がないサブドメインに溢れるのは目をつぶってこれで解決としよう。
ダウト!!実はブラウザ実装の問題でこれじゃ解決しません!

どうやら一部ブラウザは domain=www.example.com と指定すると *.www.example.com のみにクッキーを送り、www.example.comには送ってくれない奴らがいるようです。
PCブラウザは大抵大丈夫っぽいけど、スマホでクッキーを送ってくれない奴が居ます。詳しくバージョンとか集計してないけどAndroidでもiPhoneでもポツポツといるようです。

ということでこの設定はボツ。

4.とりあえず確実にクッキー食わせたいので妥協策…

とりあえず確実にクッキー食わせたいので妥協策…
RewriteRule . - [CO=foo4:bar:.example.com:1800:/]
出力されるヘッダ
Set-Cookie: foo4=bar; path=/; domain=.example.com; expires=Wed, 25-Nov-2015 08:28:31 GMT

domain指定がおかしいブラウザの為に、1個上のドメインのワイルドカード指定でクッキーがダダ漏れるホスト名範囲がめちゃめちゃ広くなって気分悪いけど、もうCO=に付き合いたくない、諦めた、これでいいだろっていうやり投げ感漂う設定となってます。

なお、www.example.com のホスト上で domain=example.com なクッキーを発行すると今度は権限がないホスト名と判断する駄目ブラウザがいてクッキー食ってくれない実装がいて駄目なので、domain=.example.com ならどうだ!?て試したら何となく大体みんな食ってくれてるようなのでこうした。

蛇足

jsとかphpでクッキーセットしたら?

コンテンツは触らずにサーバ設定だけでどうにかするのが要件だったんよ。

mod_luaでクッキー出力とかは?

それも考えた。mod_luaって早くてパワフルだし凄い便利だよね。だけど httpd-2.4 じゃなくて httpd-2.2 しか使えない環境だってん…。

そもそもどういう要件だったの?

  • 環境
    • コンテンツは触れない
    • サーバは httpd-2.2
    • 既に既存で凄い複雑な設定がしてあって httpd-2.2 を捨てる選択肢やコストは今は無い
  • やりたいこと
    • サイト内の何処のURLとかは決まってないけど、ランディングページのURLに afid=xxx なクエリが付いてたら、afid_track=xxx なクッキーを発行して後の処理に引き継いで元のxxxの値を使えるようにお膳立てすることがミッション内容。

で実際には最終的に、以下の様な設定にして落ち着きました。

クエリにafidがあったらafid_trackクッキーに90日間保持させるよ
# CO=を使わないのには理由があるのでこちら参照 http://bit.ly/1PNNU8N
RewriteCond %{QUERY_STRING} (^|&)afid=([a-zA-Z0-9]+)
RewriteRule . - [E=afid_track:%2]
Header add Set-Cookie "afid_track=%{afid_track}e; path=/; max-age=7776000" env=afid_track

というのが本エントリを書いた経緯です。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3