【PHP8.4】ついにURLへのセッションID埋め込みが禁止される

いにしえの時代、PHPはセッションハイジャックに致命的に脆弱でした。

かつてのPHPは`http://example.com/?phpsessid=hoge`というURLでアクセスすると`hoge`というセッションIDでセッションが始まるという仕様であり、これは仕様というか[セッション固定攻撃](https://en.wikipedia.org/wiki/Session_fixation)という脆弱性そのものです。

そもそもなんでこんな仕様があったのかというと歴史的経緯というやつです。

PHPが誕生した時代は、そもそもCookieに対応していないブラウザ・端末が普通に存在していた時代でした。
たとえばスマホ以前に存在したガラケーはほとんどがCookieに未対応です。
そんな端末・ブラウザでもログインしたりショッピングカートを使いたい、そんな場合はどうすればいいか。
そこでURLにセッションIDを載せて運用しよう、という発想が生まれました。

この発想は当時はPHP以外の言語でも行われていたのですが、すぐに問題が発覚しました。
URLをコピーすると通常は`https://qiita.com/rana_kualu/items/4118511b430fff0135d1`のようになるわけですが、URLにセッションIDを乗せている場合は`https://qiita.com/rana_kualu/items/4118511b430fff0135d1?PHPSESSID=5f4dcc3b5aa765d61d8327deb882cf99`のようになります。
で、うっかりこれをこのまま共有してしまうと、他者が自分としてログインできてなりすましができてしまうというわけです。
これが実装された当時はそもそもセキュリティなどほとんど誰も気に留めないおおらかな時代だったので、仕方ないといえば仕方ないんですけどね。

しかしその後問題が知れ渡るにつれ、他言語ではこの機能自体を削除するようになり、PHPでも[設定を増やしたり](https://www.php.net/manual/ja/session.security.ini.php)、[デフォルト値を安全側に倒したり](http://php.adamharvey.name/manual/ja/migration53.ini.php)と問題が起きにくいようにしてきました。
が、PHP8.3時点でもいまだに、設定を甘くすることによって脆弱にすることが可能です。

しかしまあ、いいかげんCookieを使えない環境も絶滅したし機も熟したということで、システム的に脆弱にすること自体を不可能にしようということになりました。
このRFCは既に受理されており、PHP9以降、PHPはセッション固定攻撃から完全に解き放たれます。
スケジュールとしてはPHP8.4でDeprecate、PHP9で機能自体の削除という流れになります。

以下は該当のRFC、[Deprecate GET/POST sessions](https://wiki.php.net/rfc/deprecate-get-post-sessions)の日本語訳です。


# PHP RFC: Deprecate GET/POST sessions

## Introduction

PHPは、セッションIDを継続する方法を2種類提供しています。
Cookieと、URLパラメータです。

このうちCookieは事実上の標準となっており、推奨される方法です。
URLによるパラメータは、使い勝手と安全性の両方の面で問題がありますが、Cookieが常に使用できるとは限らなかったことから、代替オプションとしてメカニズムを[供給してきました](https://www.php.net/manual/session.idpassing.php)。

このフォールバックを容易にするため、`use_trans_sid`という機能が用意されています。
この機能は、全てのURLに自動的にセッションIDを埋め込みます。
これにより、Cookieが有効でない場合はURLパラメータを使ったセッションにシームレスに切り替えることができます。

GET/POSTパラメータにセッションIDを埋め込むことは、一般的には悪い方法だと考えられています。
理由としてはたとえば[Why is passing the session id as URL parameter insecure?](https://security.stackexchange.com/q/14093/188415)を参照してください。


## Proposal

GET/POSTパラメータで受け取ったセッションIDを使ったセッションの有効化、およびURLへのセッションID自動埋め込みの両方を非推奨とします。

現在、これらの設定は初期状態で無効になっています、
従って、機能が有効になっている場合にのみ廃止の警告を表示することにします。

以下のデフォルト設定では、非推奨の警告は発生しません。

```ini
session.use_only_cookies=On
session.use_trans_sid=Off
```

デフォルト設定から変更されている場合は警告が発生します。

```ini
session.use_only_cookies=Off ; Will generate a deprecation warning
session.use_trans_sid=On ; Will generate a deprecation warning
```

以下のini設定項目は非推奨になります。

・session.use_only_cookies
・session.use_trans_sid
・session.trans_sid_tags
・session.trans_sid_hosts
・session.referer_check

関数[session_start](http://www.php.net/session_start)の引数として渡すことも非推奨となります。

また定数SIDも、このRFCの一環として非推奨になります。

そして、PHP9.0ではこれらの機能が完全に削除されます。
上記のini設定、および定数SIDも削除されます。


## session.use_cookies = Off

Cookieを発行せずにCookieセッションを使う用途があったのかは定かではありません。
しかし、この設定を使うとPSR-7を遵守することができます。

`session.use_cookies`を無効にすると、PHPはセッションを開始してもセッションIDのCookieを発行しません。
その後都合のいいときにセッションIDのCookieを発行する役割は、開発者にあります。

このini設定は、本RFCに影響を受けません。


## Backward Incompatible Changes

PHP8.4では、非推奨のアラートが出る以外、下位互換性のない変更点はありません。

ただし、PHP9.0では機能自体が使用できなくなります。

PHP9.0以降でURL埋め込みセッションを使いたい場合は、ユーザランドで実装する必要があります。
URLパラメータからのセッションID読み込みも行わなくなるので、[session_id](http://www.php.net/session_id)を用いて自力で実装する必要があります。


## Unaffected Functionality

関数[output_add_rewrite_var](http://www.php.net/output_add_rewrite_var)は影響を受けません。

ini設定`session.use_cookies`を使用した、自力でのCookie制御は今後も引き続き可能です。


## Version

PHP8.4でDeprecate。
PHP9で機能削除。


## Vote

投票期間は2024/04/09～2024/04/23、投票の2/3の賛成で受理されます。

本RFCは賛成29反対0の全会一致で可決されました。


# 感想

ガラケーもとっくに滅びたし、今さらこんな機能があったところで事故の元でしかないので削除は妥当なところでしょう。
これに依存しているサイトなんてとっくに絶滅しているとは思いますが、万一残っているようであれば今のうちに対処しておきましょう。
まあ対処と言っても、よほどおかしなことでもしていないかぎり、ini設定をデフォルトにするだけで自動的にCookie制御になりますけどね。

それにしても[output_add_rewrite_var](http://www.php.net/output_add_rewrite_var)なんて関数初めて知ったわ。
`a href`のURLや`form`のパラメータに値を自動で追加してくれるという、HTML専用にもほどがある関数です。
他言語では言語仕様に入ってくることなど絶対ないであろう、利便性に全振りしたPHP言語ならではの機能ですね。