Piece Frameworkを試した

Piece FrameworkはPHPフレームワークです。

セキュリティに強い、そして日本人が開発している、というのが特徴のようです。

【PHPウォッチ】第34回 セキュアでロバストなPHPフレームワーク「Piece Framework」:ITPro
Piece Framework - A stateful and secure web application framework for PHP

早速触ってみました。

概要

以下の3つの要素から構成されます。

  • Piece_Unity: 基本
  • Piece_Right: Validation
  • Piece_Flow: フロー制御

一番の売りは、フロー制御のようです。これを使うと、Webアプリ開発者が意識せずとも、CSRF脆弱性を持たないフォームを作れます。

デモサイトでフロー制御を体験

デモサイトが用意されています。

Piece Framework Questionnaire Example
PieceFrameworkによるシンプルなWeb開発 - Piece Framework Documentation - Trac(デモの解説)

デモでは、入力画面①、入力画面②、入力画面③、確認画面、完了画面という5つの画面で構成されたフォームを体験できます(画面遷移図はこちら)。

デモサイトの動き

基本方式として、高木浩光@自宅の日記 CSRF対策に「ワンタイムトークン」方式を推奨しない理由の図5を採用しているとのことです。

以下にデモサイトでの動作の概略を説明します。

まず、入力画面①にアクセスすると、セッションID Cookieが発行されます。画面には、氏名とEメールアドレスを入力するフォームが表示されます。このフォームのhiddenに「_flowExecutionTicket」という「フロー実行チケット」が埋め込まれています。

このフロー実行チケットが、高木さん方式の「winid」に相当します。

このチケットは、サーバ側でセッションIDとヒモ付けされています。チケットはPOST/GETで画面間で引き回されます。各画面でユーザが入力した値は、チケットにヒモ付けてサーバ側のセッション領域に保存されます。

また、サーバ側でチケットにヒモ付けて、どこの画面まで進んだのかのステータスを保持しています。ステータスをチェックすることにより、いきなり入力画面①から完了画面に突き進むような攻撃を防げます。

フロー実行チケットでできること

チケットを使うことで、以下のような効果が得られます。

  • フロー制御
    途中の画面の跳び越しなどを防ぐ。
  • hidden値の改竄防止
    ユーザがフォームで入力した値は、値の検証後にサーバ側で保持されるため、改竄できない。
  • CSRF防止
    チケット値は予測困難であるため、CSRF対策になる。
  • 二重送信防止
    チケットのステータス管理により、ワンタイム性を持たせられる。

複数ウィンドウの同時起動時の問題

ここでは、複数ウィンドウの同時起動時の挙動について書きます。

Piece Frameworkの方式は、複数ウィンドウ同時起動に対応しています。ただしそれは、複数のウィンドウが、それぞれ異なる値のチケットを抱えている場合だけです。

問題なのは、同一の値のチケットを持つ複数のウィンドウが起動された場合です。問題といっているのは、CSRFに脆弱というような類のものではなく、ユーザ操作によってはデータの整合性が維持できなくなるということです。

例えば、デモアプリでの確認画面は以下のようなものです。

この画面で、「Form1に戻る」リンクなどを「別ウィンドウで開く」場合、同じチケットのウィンドウが複数出来てしまいます。

ここで、新しいウィンドウ側で性別を「女性」から「男性」に修正する操作を行ない、確認画面まで来たとします。

この状態で、旧ウィンドウ側の「回答する」リンクを押下した場合、旧ウィンドウの確認画面に表示されている情報(女性)ではなく、新ウィンドウの情報(男性)がDBなどに登録されてしまいます。

これはユーザの意図に反した現象と言えます。お金を扱うようなフォームであれば、このような不整合は許容できないと思われます。

複数ウィンドウ問題の解決策

一つの問題は、上記の確認画面において、入力画面①〜③に戻るアクションをリンクで行なっていることです。リンクではなくボタンを使うと、IEFirefoxでは別ウィンドウを起動できなくなります*1

しかし、たとえボタンを使ったとしても、ユーザがやろうと思えば、同一のチケットを持つ複数のウィンドウを起動できてしまいます。特にOperaでは簡単にウィンドウのクローンが作れます。

つまり、この種の不整合問題には「リンクをボタンにする」とかではない、根本的な対処が必要だと思います。

いくつか考えられる対処方法を以下に書きます。

hiddenで全部の情報を引き回す

一番簡単なのは、フロー実行チケットだけではなく、ユーザがフォームに入力した情報も一緒にhiddenで引き回す方法です。

情報をDBなどに登録する最終段階で、サーバ側のセッション領域に保存されている情報と、hiddenから送られた情報が一致しない場合はエラーとします。

チケットの値を都度変更する

もう一つの解決策は、チケットにヒモ付いてセッション領域に保存される情報に、何らかの変更が加えられるタイミングで、フロー実行チケットの値も変更する方法です。

そうすると、新ウィンドウ側の操作で性別を変更した際に、新ウィンドウのチケットの値が、旧ウィンドウのそれとは異なる値に変化します。これにより、性別変更後は新・旧のウィンドウを区別できるようになります。

この方法では、チケットの値を変更する際に、変更前のチケットとそれにヒモ付く情報をサーバ側から消去する実装と、消去せず残す実装の2通りが考えられます。

消去する実装の場合、旧ウィンドウの「回答する」リンクが押下された際に、エラーとせざるを得ません。ブラウザから送信されるチケットは、サーバにとっては消去済み=見知らぬチケットであるためです。同じような理由で、ブラウザの戻る/リロードなどのボタンの機能に制約が生じます。

一方、消去しない実装では、上記のような問題は生じません。新・旧のウィンドウともに正常に機能させることができます。しかし、ユーザの操作毎にサーバ側のセッション領域内のデータ量が増えていく問題があります。

なおチケットの値を、第三者が予測困難な値に変更すると、セッションIDとフロー実行チケットを第三者が「固定」するタイプの攻撃への耐性ができます。

サブチケットを使う

チケットの値を変化させる方法の変種です。フロー実行チケットに加えて、「サブチケット」をhiddenに埋め込み、ユーザの操作毎にサブチケットの値を変化させます。

サブチケットは、サーバ側のセッション領域に保存されるフォーム入力の情報の「世代」を示す情報だと言えます。

最後に

HTTPの世界では、フォーム処理ひとつとっても簡単なようで難しいです。個々のアプリ開発者がいちいち共通的な処理を作るのは不効率です。そういう意味で、この手のフロー制御機能は、フレームワークが持っていて欲しいものだと思います。

最後に、Piece Frameworkについて何点か。

  • ドキュメントを充実
    サイトを見てもドキュメントが殆どありません。ソースコードのコメント量も少ないです。今後増やして欲しいなと思います。
  • 複数ウィンドウに対応
    上記した複数ウィンドウの制御が可能だとよいと思います。「フロー制御」を売りにするならば、徹底的にやって欲しいなと思いました。

なおフォームの処理については、拙文(よりセキュアなWebサイト構築の8章)にも書いています。興味のある方は是非ご一読を。

(2007/04/30 02:00, 14:00 加筆修正)

*1:リンクで遷移した場合、URLのクエリストリングにチケットが含まれるため、リファラーからチケットが漏れるという別の問題も生じえます。