おかしなHTMLエスケープ

PHPでのプログラミングを取り上げたサイトで、少々おかしなコードを見つけた。
調べてみると、他にも同じような書き方をしているサイトをいくつか見つけたので、日記のネタにしてみようかと思う。

そのコードは、以下のようなもの(表示の都合で折り返している)。

<a href="foo.php?SESS=<?= htmlspecialchars($sid) ?>">次へ</a>

$sidはセッションIDで、これをHTMLエスケープしてアンカーURLの後ろにくっつけている。リンクを辿った際に、セッションIDが次ページに引き継がれるようにするためだ。

ちなみにPHPでは、クライアントが送ってきたセッションID(GET/POST/COOKIE問わず)が、サーバ側で管理しているセッションIDの一覧の中に無い場合、送られてきたセッションIDでセッションを開始するというという動作をする*1。そのため、セッションID文字列中に、"<>を含む任意の文字が含まれる可能性がある。

本題に戻って、上記のコードの何が違うかというと、$sidはクエリパラメータに付ける値だから、この文脈でするべきはhtmlspecialcharsではなく、urlencodeでしょうということ*2 *3

PHPに限らず、この手の間違いは割と良く見かけられる。以前、よそのサイトから私の担当していたサイトにリンクを張ってもらうことがあった。そのリンクでパラメータを渡してもらうのだが、なぜかそのパラメータは、HTMLエスケープした後にURLエンコードされていた。「なんとなくエスケープ」していると、そんなことも起きてしまう。

*1:これはpermissiveなセッション管理と呼ばれるらしい。詳しくは以下のページを参照
おさかなラボ - strictなセッションに対するSession Fixation対策
yohgaki's blog - Strict Session管理パッチ

*2:論理的には、urlencodeした上で、さらにhtmlspecialcharsするべきだろうか。私が現場にいたときはそこまでやってなかった。

*3:他にも、セッションIDをGETで引き回すなとか、htmlspecialcharsの第二引数を指定すべしとか、テンプレートを使えとか、色々と突っ込みどころはあるでしょうが、今回はそれ以前の話。