RESTfulなAPIやWebアプリケーションを開発する際に、一つの疑問が生じる。
RESTでは「ステートレス」を重視して、サーバサイドでのセッション管理ではなく、クライアント側で認証情報や状態を保持して、サーバに都度送る方式を唱えている。これはHTTPで実装するなら、Cookieを使ったセッション管理ではなく、BasicやDigest認証など、HTTP認証を使うことになる。
しかし現実問題として、モダンなWebサイトでBasic/Digest認証を扱うことはなく、サーバサイドのCookieを使ったセッション管理を使うことになる。
RESTにおいて、ステートレスという特性と、現実のセッション管理をどうバランシングすれば良いのか?
うまく整理しきれていないが、結論を三行に濃縮してみる:
以下、自分なりにRESTについて調べ、上記観点についてまとめた内容となる。
RESTは "Architectual Style" であり、分散ネットワークシステムにおけるアーキテクチャのパターンの一つであるとされる。1990年代後半~2000年初頭にかけて、HTTPの策定やApache Webサーバの開発に関わった Roy T. Fielding 氏が提唱し、Paul Prescod 氏などにより広められ、その後のWeb開発で重要な考え方の一つになった。
see:
以下のような設計原則を提唱している。 ( see: http://ja.wikipedia.org/wiki/REST )
2015年を生きる今の自分たちから見ると、ステートレスを除いては「何を今更」と云いたくなるほど普通に見られる原則である。
今、普遍的に見られる原則が、過去、わざわざ提唱されたということは、当時は、そうでなかったことが推測される。
当時のWebがどんな世界だったのか、以下の記事にその一端を伺うことが出来る。(こういう古い記事のURLがちゃんと残ってて参照できるのはスバラスィ! ITmediaに感謝!)
ポイントとしては、WebブラウザやWebサイト主体の最初の黎明期~発展期だった点。まだ Web 2.0 が出てくる前という点も覚えておきたい。
当時は初期のWebアプリの開発ツールなどで、POSTだらけの遷移だったり、出来立てのSOAPだったりが乱立していたらしい。
そのため、ハイパーテキストの思想やURIの考え方が蔑ろにされ、ブラウザに表示されるURLを送っただけでは同じ情報が見れなかったり(POST後の遷移)、リンクでつながるのではなくボタンクリックによるPOST遷移で画面が切り替わるなど、今らすればずいぶんと使いづらい世界が広がっていたようだ。
そのような状況に対して、ネットワークベースの分散アプリケーションの設計思想の一つとして、提唱されたのががREST, Representational State Transfer になる。
RESTはネットワークベースのクライアント - サーバ分散システムを想定し、scalabiliry, reliability, cache などの制約を考慮した場合の設計思想として整理されている。
HTTPを前提としてはいないが、一番実装しやすい世界がHTTPのWebの世界となっている。
推測だが、HTTP/URIの策定に関わり、Apache Webサーバの開発にも関わっていた Roy T. Fielding 氏にとって、当時の状況に対して忸怩たる思いや、強い「コレジャナイ」「どうしてこうなった」感を抱いていたのではないだろうか。
RESTは当時のWeb開発者たちに広く受け入れられ、日本でも広がっていった。
URIの組み立て方や、HTTPで定義済みのVerb(GET, POST, PUT, DELETE)をリソースの操作にマッピングする考え方は、自分にとっても直観的で分かりやすく、納得できる。
レスポンスにハイパーリンクを含めることでリソースをリンクさせていく考え方も、WebページやAPIレスポンスの作り方として、自分も違和感なく受け入れられる。
だが「ステートレス」については、その前提や目的が軽視され、「ステートレスでなければならない」というお題目だけが広まってしまった印象を感じており、「ステート」の定義の曖昧さも感じているため、強い違和感を感じている。
自分の力量不足で、これについては全く整理できていない。つまりそれだけ、RESTが唱える「Stateless」の「State」と、Web開発で扱う「状態」の関係がよくわからない状態になっている。
RESTの「Stateless」の解説としてよく見かけるのが、その目的として「Scalability」があるという点。つまり、サーバサイドで状態を管理しているとサーバ台数を増やした場合の同期処理などのためパフォーマンスやスケーラビリティで問題が出てくる。またLoad Balancerでのsticky sessionの扱いも出てくる。これを解決するため、クライアントとサーバのコミュニケーションをStatelessにする、というのがRESTの考え方となるらしい。
see:
自分は、Web開発で扱う「状態」には以下の種類があると考えている。
以下、つらつらと思った内容。
結局 state というのが、各開発者が実現しようと考えている「セッション管理」のどの部分にあたるのか、REST主要メンバーの間でも議論中であり、機械的に判別できるような状況ではないことが分かる。
Railsではサーバサイドではなく、クライアントサイド=Cookieに、セッションIDではなく直接セッション情報を格納する方式を使えるらしい。もちろん暗号化や改ざん対策は可能。
ただ、他の言語やFWとか見てみて、この方式が広まっているとは思えない・・・。
セッションをサーバサイドで無効化することも可能という点で、結局、どこかしらサーバ側でクライアントとの間でやりとりするトークンIDなどの最低限度の値は管理できるようにしておかないと、セキュリティ面で色々、自力で考えなくてはならなくなる面が多そう。
普通に、これまでのサーバサイドのセッション管理機構(言語それ自体のものだったり、FWが提供してたり)をそのまま使えば、最小限の労力でセキュアなセッション管理を行える。
RESTを意識するあまり、セキュリティ面まで普段と違う作りにして、自力で色々実装したり、あるいはFW側の機能を検証する必要があるか?どうか?
「そういえばOWASPではRESTアーキテクチャスタイルについて何か推奨とか出してるかな?」と思い、探したら以下のページが見つかった。
ストレートに一言。
RESTful web services should use session-based authentication,
either by establishing a session token via a POST or by using an API key as a POST body argument or as a cookie.
Usernames, passwords, session tokens, and API keys should not appear in the URL,
as this can be captured in web server logs, which makes them intrinsically valuable.
ユーザ認証にはセッション管理を使え、とあり、さらに、セッションIDやAPIキー、ログインIDとパスワード、トークンなどはURLに含めるな、ともある。
他にもこのページにはRESTスタイルで開発するときのセキュリティ上の注意点が解説されている。英語になるが、内容的には普通のWebサイトと同じように入力チェック+出力時のJSON/XMLに合わせたエンコーディング、CSRF対策などをしてください、という流れ。
また以下のページは、逆にRESTなWeb APIを診断する、pentester向けのガイドとなっている。クロールできたURLだけでは、全部のREST APIの機能を見れたことにはならない可能性があるので、ソースコード診断(ホワイトボックステスト)も可能なら実施したい、などとあり、参考になる。
RESTは設計パターンであるが、「HTTPを使った分散C/Sシステムは "どうあるべきか?" 」という思想になってしまってるのが難しさの原因だと思う。
英語の博士論文を読み込み、実際の開発に落とし込んだ時のギャップとか曖昧さについて最先端のメンバーと英語で会話してディスカッションして「どうあるべきか?」に一つ一つ答えを出していくのは、非常に限定された少人数でしかできない。
大多数の開発者は、そのような哲学的な思想やHTTPの将来には興味がなく、迫り来るスケジュールの中、数年先位までのメンテナンス性とセキュリティと顧客要求を両立させるので大変で、設計思想の妥当性の検証や現場でのfit & gapを検証してディスカッションする役目は負っていない。
単に「より分かりやすい・扱いやすいWebシステムを作るためのテクニック」として道具に落とし込んで、「こういうURL構成にすると分かりやすい、便利」とか「HTTPのリクエストメソッドを実際のリソース操作にマッピングすると、スゴイ自然に見えて、メンテナンス性もあがる」とかにしてくれてれば、ユーザとして気軽に扱えたかもしれない。
設計思想として「かくあるべし」というものが出来上がってしまったので、開発者たちは自分の作ろうとしているのがそれに沿ったものなのか、不安にかられ、「正しいのか、間違っているのか」というお墨付きを得ようとする。
これはちょうど、OOPが流行りだした後、「こういうプログラミングはOOPに従っているのか?」と不安に思うのと似てる。
他人が作った思想に沿った作りになっているのか、いないのか、考えたり、不安に思って調べるのは、個人的にはもうたくさんだ。
Roy T. Fielding 氏はあなたのWeb開発を設計してくれはしない。
あなた自身を含めた、チームメンバーが納得できる設計方式でWeb開発するのが一番大事なのではないか?
その上で、REST設計スタイルで取り込みたい部分があれば取り込めば良いし、取り込みづらい部分があれば取り込まなくても良い。
仮に「これはRESTではない」という批判が上がったところで、我々はRESTのためにWeb開発をするのではない。
ユーザにとって利用価値があり、分かりやすく、メンテナンスを容易にするために、あれこれ頭をひねって設計して、Webを開発している。
RESTが我々を支配してはならない。我々がRESTを支配しなければならない。
RESTじゃないんじゃないか?とビクビクするより、そもそもこうした設計思想はそういう不安を駆り立てる構造を持ってるんだから、細かいこと気にせず作ればいいんじゃないかな?
英語の論文バリバリ読み込んだり記事書いたりしてる人をもって「RESTは難しい」と言わしめてる。そこまで「正しく」扱うのが難しくて勉強が必要な設計思想、現場の開発者たちが「正しく理解して、正しく扱う」ことに期待してもしょうがないし、期待されてもできないと思う。
とくにstatelessについては、以下の記事にあるように、無理してクライアント側Cookieに持たせるよりは、従来通りのサーバサイドのセッション管理にした方が良かった、というのがスゴイ生々しいので、あんまり気にする必要無いんじゃないか。
結局ここに帰ってくる。
最大の違和感は、本当に "stateless" にしないと scalability & reliability は確保できないのか?という点だ。
特にWebシステムであれば、言語/FW/ミドルウェア/サーバのどこかしらに、セッション情報のクラスタリングや共有・同期機構があるはずだ。
もちろんそれがパフォーマンス上の問題になったり、同期タイミングの問題だとか、実際の開発で検証すべき部分はある。
しかしサーバサイドのセッション管理自体はWeb開発においても枯れた方式であり、セキュアに開発するノウハウも蓄積されているし、大抵のWeb開発言語やFWにはサーバサイドでのセッション管理方式が実装されている。
それらを無視してまで、scalabilityとreliabilityを優先した "stateless" なクライアントサイドの状態管理を導入する必要があるのか?
よほどクリティカルなシステムでも無い限り、例えばサーバの片系が落ちて、数秒程度のセッション情報の同期失敗が発生しても、利用者はほとんど影響を受けないのではないか?
2015年現在ではIn Memoryかディスク書き込みかに関わらず、分散KVSのソリューションも出揃い、サーバサイドのセッション情報をそれらで管理することで冗長性とスケーラビリティをある程度確保することも可能だ。
そうした現実を踏まえた上で、なお、"stateless"にしないとscalabilityとreliabilityは確保できないのか?
また、状態管理が必要になる機能は、本当にURIで一意に識別してアクセスできなければならない、つまりRESTに従わなければならないのか?
ショッピングカートでアイテムをバスケットに入れる一連の遷移は、本当に各遷移でRESTに従わなければならないのか?
3画面以上に分割された複雑なユーザ登録の画面遷移の途中、一画面ごとに、一意なURLでRESTにしたがってアクセスできなければならないのか?
そもそも状態遷移の管理が必要な機能は、URLで一意にアクセスできる必要は無いのではないか?厳密にRESTに従う必要は無いのではないか?
RESTは誰のためのものか?
RESTはWeb開発者が従わなければならない法律なのか?教義なのか?
従っていないと罰せられるのか?
そんな訳は無い。
scalabilityについてどう対処するか、reliabilityについてどこまで担保するか、そんなものは我々開発者がそれぞれのアプリ要件に応じて吟味するものであり、「RESTだからscalabilityを確保するために○○しなければならない」という思考はナンセンスだ。
・・・だが、色々ググってると、どうも皆そうしたメンタリティに陥ってるような気がするんだよなー。自分もそうなんですが。
なので、結論として、RESTだからscalability確保のために"stateless"にしなければならない、サーバサイドのセッション管理は使ってはならない、ということは無いんです。
多分、2000年初頭のWebの状況に対して、 "stateless" の部分が若干攻撃的に主張され、それがやたら強調されてるだけの気がします。
だって、色々ぐぐっても、「scalabilityやreliabilityを加味して、無理して "stateless" にしなくてもいいんじゃね?」っていう論調、見かけないし・・・。
だから "stateless" という主張だけがひとり歩きして、お題目化して、一周遅れでRESTを取り込んだJavaEE6-7辺りで、今頃になって「JerseyはRESTfulなAPI作るためのFWだから、セッション管理使えないの?」みたいな疑問が出てくるんじゃないかなーと。
でもソフトウェア開発の設計思想とか、開発思想って大なり小なりそういう、当初のバックグラウンドの文脈がロストされたため、数年後、変にお題目だけがひとり歩きしてややこしい状況になるって普通にあると思うんですよ。製品開発でのマーケティングとかに巻き込まれて、本来とは違う主旨でバズワード化することもあるでしょうし。
なので、なので、なので、「そのAPIってRESTじゃないよね?」とか言ってくる輩にはこの画像を返せば良いと思います。
(from http://matome.naver.jp/odai/2137161402956130101/2137208074866804503)