トークン認証で問題が発生したため、セッションをコードレベルで即時反映を行うようにした話

つい先程まで仕事中に、とある問題に悩まされたところ。

現在進行系のホームページの案件があるため、システム開発は進まないだろうと、傍らに今あるバグを潰すかってことで作れているところまでの動作を検証していた。

その際に、セッションを利用した処理を多々書いていたところを見直しながら、動作を検証していたわけだが、トークンの認証あたりでページの読み込み完了後の間髪入れずのページ遷移をテストしていた際に問題発生。

 

原因を探るのにそれなりに時間がかかったため、備忘録として残しておくことにする。

 

起きた事象としては、多重送信防止用のトークンでごくごく一般的な認証をメソッド単位で呼び出しや書き込みを切り分けて行っていた際に、トークンに設定した値が正常に取れなくなるといったもの。

そこまで重たい処理をまだ回してはいないのに、それが不特定のタイミングで確率的に発生する。

 

昔、PHPファイルで直接処理を指定し、通常のファイル管理でセッションを保存していたときには全く発生してなかった問題で、今更こんな問題で・・・と我ながら苦虫を噛み潰したような状態となった。

前々から言っている通り、ゼロからフレームワークを組んで開発を行っているわけだが、フレームワークレベルでのルーティングとMVC分離を行って開発している最中に起きかつ、初めての事象ということで結構重大な問題で非常に困惑。

 

CakePHP使いとかlaravel使いの人は普通こんなところで悩まんやろwwwとか思うだろうけど・・・いくらスクリプト言語のPHPであるとはいえ、使い慣れたものであるこれに関して問題が発生したということで自分の内心は非常に由々しきものだった。

しかも傍らで進めているため、マルチタスクが苦手目な自分にとっては、この時間はホームページ!この時間はシステム開発!と、睡眠不足と空腹も加えてまるでおぼつかない集中力。

 

あんまり状況的にはよくなかったが、とりあえずいつもどおりvar_dump()でデバッグ情報を出して、初心に戻って出てきたデータをハードコピーしてじっくりと検証した。

 

今回の問題そのものが確率的に出ているもののため、

  • 今まで組んできたロジックに問題はないか
  • プログラム的な処理の流れは適切か
  • 処理に必要な値はきちんと正常に切り替わっているか

と、トップダウンで検証してみたわけだが・・・

 

皆目見当がつかん。

 

・・・とはいえ、確率的に発生しているということは、値の切り替わるタイミングで何かが起きているということは漠然と予想がついた。

恐らくセッション変数を不適切に使ったからこそのバグであるということである。

 

そこでPHPのセッションに関するドキュメンテーションをメジャーなところから読み返した。

 

公式のsession_rewrite_close()の項目で気になる文章を発見。

 

セッションデータは、session_write_close() をコールしなくても、スクリプト終了時に保存されます。しかし、 セッションデータは、同時書き込みを防ぐためにロックされるため、 ある時点であるセッションの処理ができるスクリプトは、1つだけです。

 

・・・セッションは「スクリプト終了時に保存されます」ってーことは、あるページ(画面)のあるリクエストにおいて、それらに関わるコードが全て実行された後に保存されるってことのようだ。

 

ということは、いかにセッション変数でトークンの値を保持したとしてもそれが実際に反映されるのは、PHPのコードが一連で実行されてから描画までが終わった直後であるということなのだろう。

 

・・・・。

・・・・・・・。

 

手続き式でまんまファイルでやっていたら気づかんわ!

つーか、気にしたこともねぇわ!

今まで作ってきたプログラム実は結構やばい・・・?

 

ともあれ、この一文は非常に的を射ており、問題解決のためのヒントは得られた。

そこからさらにvar_dump()とハードコピーで目的の値を追う作業の中で、やはりおおよそ自分の予想が的中していることを確信し始めた。

 

リクエストが交わされる中でセッション変数の書き込みが起きているため、書き込みが終了する前に読み込みを行ってしまっているというのが原因として非常に濃厚。

元々書いているロジックそのものには問題がないが、トークンで参照する値がこのリクエストの中で、あるときはきちんとスクリプト終了時、あるときはスクリプト終了前といった形で読んでいることが、確率的に問題が起きていたことの要因だろう。

 

そこで、確実にトークンの値を出し入れするために、以下のコードを書いて突っ込んだ。

 

$_SESSION['token'] = 'xxxxxxxx';
if ( PHP_SESSION_ACTIVE === session_status() ) {
	session_write_close(); // セッションの保存と終了
	session_start(); // セッション再開
}

 

とりあえず、コードレベルでセッションの即時反映を行うようにしてみた。

 

で、書いた後、発生していた状況と同じ条件で相当な回数をテスト。

ってか今回の問題はプログラムを直すよりも、ブラウザ弄っていた時間のほうが遥かに長い(´・ω・`)

 

結果としては、睨みを効かせた部分がまさに正解だったようで、甲斐あってその後、問題は発生しなくなった。

 

まぁ使い慣れているものでも、全部一人で、そして組み方を変えるだけで使い勝手ってだいぶ変わるもんだねぇ・・・。

カテゴリ

この記事のコメント

コメントはないです。

コメントを残す

メールアドレスが公開されることはありません。