『プロになるためのWeb技術入門』を読んで学んだこと
「Webアプリケーション」とは何か
デスクトップアプリケーション
- 主な処理は、手元のPC上で行われている。
- 画面は、OSの機能を利用して表示されている。
- アプリケーションをPCへインストールする必要がある。
Webアプリケーション
- 主な処理は、手元のPCではなくサーバ上で行われている。
- 画面は、HTMLで表現され、Webブラウザ上に表示されている。
- アプリケーションをPCへインストールする必要はない。
Internet Explorer や FirefoxなどのWebブラウザ自体はデスクトップアプリケーションである。
Webはどのように発展したか
ARPANET(アーパネット)と呼ばれる、アメリカ国防総省が研究・調査目的のため、1969年に構築したネットワークが現在のインターネットの原型となっている。
欧州原子核研究機構(CERN)という組織の中で、実験成果を全世界の研究者間で共有しよう、ということでCERN所属のティム・バーナーズ・リーによって1989年に提案・開発されたのがWWWであった。
WWW登場当初のWebブラウザは、Webページをテキスト中心で表現していたが、これを現在のようにテキストと画像を混在させて表示できるようにしたのが、マーク・アンドリーセンらによって1993年に開発されたNCSA Mosaic
というWebブラウザである。
1996年にHTTP/1.0が策定された。
HTTPを知る
- プロキシサーバは代理サーバという意味で、WebブラウザによるHTTPリクエストを代理として受け取り、本来の要求先Webサーバへ渡す役割をする。
HTTPリクエスト
- リクエストライン
- メソッド: リクエストの種類を指定する。
- URI: リソースを一意に指定する。
- HTTPバージョン: バージョンによって利用できるメソッドやヘッダの種類が変わる。
- メッセージヘッダ: リクエストの付加的な情報(HTTPヘッダ)を表す。
- メッセージボディ: クライアントからサーバへ送信するデータを表す。
HTTPレスポンス
- ステータスライン
- HTTPバージョン
- ステータスコード: 最初の1桁目の数字がカテゴリを表している。
- レスポンスフレーズ: 人間向けのステータスコード
- メッセージヘッダ: レスポンスの付加的な情報(HTTPヘッダ)を表す。
- メッセージボディ: サーバからクライアントへ送信するデータを表す。
グローバルIPとプライベートIP
- 通常のプライベートネットワークに接続されるコンピュータの数はあまり多くないため、一番小さなクラスCのアドレス(192.168.0.0 ~ 192.168.255.255)がよく使われる。
組織 | 個人 |
---|---|
通常、企業や大学などの組織では、ゲートウェイと呼ばれるその組織の出入り口となっているサーバを通じてWebサイトへアクセスするため、アクセス先のWebサーバにはゲートウェイのIPアドレスが通知される。この時点で、どこの組織からアクセスされたのかを簡単に特定できる。また、ゲートウェイでは内部のどのコンピュータからどこへアクセスしたのか記録することも可能なため、記録が残っていれば不正アクセスなどの犯罪行為の犯人を突き止めることができる。 | 個人がインターネットを利用する場合、ISP経由で接続することがほとんどで、ISPがゲートウェイの役割をするため、このIPアドレスがどのISPからアクセスしたのか(場合によっては地域レベルまで)を知ることができる。ISPを利用する場合は、接続するたびに違うIPアドレスが割り当てられるため、一般にはIPアドレスから個人を特定することは不可能だが、犯罪行為に備えて、ISP側でいつ・誰に・どのIPアドレスを割り当てたのか記録を残していることがほとんどである。 |
DNS
- インターネット上には、各階層に対応するDNSサーバが用意されており、それらのDNSサーバに問い合わせることで、下位のDNSサーバのアドレスを知ることができる。
- 最上位のドメイン(トップレベルドメイン, jpやcom, net, orgなど)のDNSサーバを管理するのがルートサーバであり、2010年時点で、世界中に13個存在している。ルートサーバのIPアドレスは滅多に変更されないため、各OSや全てのDNSサーバに登録され、いつでも参照できるようになっている。実際には、ルートサーバが13台しかないわけではなく、13個のIPアドレスと言った方が正しく、災害や故障に備えて1つのIPアドレスに複数台のDNSサーバが登録されている。
CGIからWebアプリケーションへ
プロセス
- PHPスクリプトを実行するプログラム(PHPエンジン)は、Apacheのモジュールとして組み込まれており、ApacheはHTTPリクエストを受け取ると、自身に登録されたモジュールにリクエストを処理させる。PHPモジュールが組み込まれていると、ディスクから読み込んだファイルの中を調べ、
<?php ~ ?>
で囲まれた部分をPHPスクリプトとして認識して実行する。その他の部分はそのままレスポンスとして返すようになっている。 - CGIによるプログラムの呼び出しとは違い、PHPエンジンがWebサーバと同じプロセスで動作するため、いちいちプロセスを起動する必要がなく、高速に動作する。
オーバーヘッド
- オーバーヘッドとは、本来の処理を行うために余計に必要となってしまう処理を指す。
- FTPの場合、最初に接続してから認証処理や転送方法の指定、データ転送用コネクションの確立などがオーバーヘッドにあたる。
- HTTPの場合、接続してからいきなりGETリクエストを送ることで、レスポンスとして必要なファイルをすぐに受け取ることができる。そのため、FTPに比べてオーバーヘッドが少ない。
クッキーとセッション
- cookieの制限
- 1つのドメイン(Webサーバ)に保存できるcookieは20個まで
- 1つのcookieは名前と値を組み合わせて4K(4096B)まで
- クライアントに保存できるcookieは300個まで
- Webサーバから送られたcookieはブラウザが読み取ってクライアントPC上に保管され、次に同じドメインへリクエストを送る際にそのcookieを送信するのだが、正確には、
Set-Cookie
ヘッダのpath
属性で指定された文字列とリクエスト先URLの先頭が一致した場合に送信される。path
属性が省略された場合は、Set-Cookie
ヘッダが含まれるリクエストのURLのパス部分が指定されたと見なされる。 - セッションは結局、cookieを利用して情報をやり取りするが、cookieの中に情報そのものを格納するか、セッションIDだけを格納するか、という違いがある。セッションIDだけを格納することで、cookieに格納できる情報量の制限を気にしなくて済む上に安全性も比較的高い。
- セッションIDを用いたやり取り
- クライアントからユーザ名・パスワード等の認証情報を送る。
- サーバはセッションIDを発行してセッション情報にログイン中ユーザを記録し、cookieにセッションIDを格納して返す。
- クライアントは次のリクエストからセッションIDを格納したcookieを渡す。
- サーバはcookieのセッションIDからメモリを参照してログイン中ユーザを識別する。
Webアプリケーションの構成要素
- ネットワークは様々な機器をケーブル(or無線)で繋ぎ、互いに通信している。これらを抽象化した時、接続される機器を
ノード
、それらを結ぶ線をリンク
と呼ぶ。実際のネットワークでは、コンピュータを始めとして、ハブ、ルータなどネットワークに接続される機器が全てノード
となる。 -
プロセス
は、コンピュータ上で動作しているソフトウェアを指す。例えば、PC上でWebブラウザとワードプロセッサの2つのアプリケーションを実行している時、「PC上では2つのプロセスが実行されている」と言う。WebサーバとDBサーバは異なるプロセスとして動作している。
WebサーバとDBサーバ
- WebブラウザとWebサーバは異なるプロセスであるため、HTTPを使って通信している。DBも同様で、SQLの発行や結果の取得にはやはりWebアプリとDBが通信している。ただ、ここで使用されるプロトコルはHTTPのように標準化されているのではなく、DB製品固有の通信プロトコルを使って通信している。しかし、大抵の場合はこの通信を担当してくれるDBアクセス用のライブラリが標準で提供されているため、アプリケーション開発者がこのプロトコルを意識する必要はない。
CGIとAPサーバの違い
- CGIはWebサーバにリクエストが届くたびに新しいプロセスが起動されては終了していく「使い捨てモデル」
- APサーバDBと同じように、常にプロセスが実行されており、Webサーバからのリクエストを受けてサーブレットやJSPを実行する「使い回しモデル」
- プロセスを常に実行しておくことを「常駐させる」と言う。また、特にUNIXの世界では常駐プログラムのことを
デーモン(Daemon)
と呼ぶ。
WebサーバとAPサーバ
- WebサーバとAPサーバの連携方法は標準化されているわけではないため、一般的にはAPサーバ側が主要なWebサーバごとに連携用のモジュールを用意し、そのモジュールをWebサーバへ組み込むことで互いの連携を可能にしている。
- ほとんどのAPサーバはWebサーバとしての機能も持っているため、わざわざApacheのようなWebサーバと連携させなくても単独でWebサーバとして動作することが可能。
- 一般的に、WebサーバとAPサーバが行うべき仕事の量を比べると、APサーバのほうが多くなる傾向にある。WebサーバはHTTPリクエストで指定されたリソースをWebブラウザに返せばよいだけなのに比べ、APサーバではWebアプリが様々な処理を行うためである。具体的にはDBから情報を取り出したり、その結果を人間が見やすいようにHTMLに整形したりといったことを行う。
DB接続の管理
- DBへの接続/切断処理は個々の処理の中で行うのではなく、システム全体で一括して管理することが常套手段となっている。APサーバではDB接続を管理する機能を提供し、Webアプリケーション開発者がDB接続の状態を気にしなくていいようにしており、
データソース
がAPサーバの管理する抽象化されたDB接続を表している。 - APサーバがDB接続を管理することで、SQL発行のたびに接続/切断を行うのではなく、最初にAPサーバが代表してDBへの接続を確保しておき、Webアプリの必要に応じて接続を使い回すことで、DBアクセスを高速化する
コネクション・プーリング(Connection Pooling)
と呼ばれる仕組みも提供している。
Webアプリケーションを効率よく開発するための仕組み
リダイレクトとフォワード
リダイレクト | フォワード |
---|---|
リダイレクトでは、サーバからクライアントに対して一度ステータスコード302 Found を返すことで、遷移先のURLをLocation ヘッダで伝え、クライアントからサーバへ改めてHTTPリクエストを発行してもらう。つまり、2回のHTTPリクエストが発生する。 |
フォワードでは、APサーバの中だけで遷移処理が行われる。1回のHTTPリクエストで遷移が可能なため、リダイレクトに比べて応答が速くなる。 |
リクエストパラメータとリクエストスコープ
リクエストパラメータ | リクエストスコープ |
---|---|
HTTPのGETメソッドやPOSTメソッドによってクライアントからWebアプリに文字列として渡されるもの。フォワード先からも参照可能。読み取りはできるが、受け取った値を変更することはできない。 | APサーバによって提供される、フォワード元とフォワード先の間で情報を共有するための仕組み。1回のHTTPリクエストを処理する間のみ有効なため、リクエスト処理が終了するとリクエストスコープに保持されていた情報は消える。そのため、リダイレクトを使った画面遷移では、情報の引き継ぎを行うことはできない。 |
リクエストスコープとセッションスコープ
リクエストスコープ | セッションスコープ |
---|---|
Webアプリではログアウトせずにブラウザを閉じられることも多く、ユーザにログアウトを強制することはできない。そのため、タイムアウトによって削除されるとはいえ、セッションは本質的にメモリを多く消費するものであるため、画面遷移にまつわる細かな情報のやり取りまでセッションを使用しているとメモリ不足に陥る危険性が高くなるため、セッションとは違う考え方のリクエストスコープが必要になった。リクエストスコープの情報はレスポンスを返すと必ず削除されるため、メモリを有効に使うことができる。リクエストを受けてからレスポンスを返すまで が有効範囲で、APサーバが制御しており、ページ遷移前後での情報保持が主な使い道である。 | 通常、セッションが保持する情報は、APサーバのメモリに保存されるため、セッションが増えてくるとメモリ不足に陥る。そこで、最終アクセスから一定時間が経過したセッション情報を自動的に削除するセッション・タイムアウト が行われる。ログインやログアウトがあるWebアプリでは、ログアウト時にそのユーザのセッション情報を削除するのが一般的である。セッションを開始してから終了するまで or タイムアウトするまで が有効範囲で、Webアプリが制御しており、ユーザ情報の保持が主な使い道である。 |
- アプリケーションスコープという、最も情報の生存期間が長いスコープも存在し、Webアプリが実行されている間はずっと情報を保持できる。主にWebアプリの設定情報などを保持すべきで、通常の処理にはリクエストスコープやセッションスコープを利用すべきである。
O/Rマッピングフレームワーク(O/Rマッパ)
- オブジェクト指向言語におけるオブジェクトによる表現とRDB上の表現の違いを
インピーダンス・ミスマッチ
と呼ぶ。インピーダンスは電気抵抗のことで、本来は電気工学の用語である。 -
O/R
はObject/Relational
のことを指し、O/Rマッピング
はプログラミング言語のオブジェクトとRDBの対応を取る、という意味である。DBとの接続を管理したり、情報をキャッシングしてDBアクセスを高速化したりといった機能も提供するが、これらは副次的なものと言える。 - 大抵のO/Rマッパでは、DBとオブジェクトとの対応を何らかの形で定義ファイルに記述している。
- O/Rマッパによっては、わざわざSQLを定義しなくてもマッピング定義を元にして自動的にSQLを組み立ててくれるものもある。この機能は便利だが、DBへどのようなSQLを発行するかを細かく指定できないというデメリットもある。
セキュリティを確保するための仕組み
SQLインジェクション(SQL Injection)
Webフォームなどの入力インタフェースを利用し、DBに発行されるSQLを開発者が意図しない形に変換することで、情報の不正取得や改ざんを行う。
対策
- 入力値のチェック:パスワードを半角英数字のみに限定したり、正規表現によるチェックを行ったりする必要がある。
- プリペアード・ステートメント:文字列連結でSQLを組み立てるのではなく、WHERE句など条件によって変化する部分をプレースホルダとして登録したSQLを事前に用意しておき、後からパラメータを割り当てる(バインドする)必要がある。
クロスサイトスクリプティング(Cross Site Scripting, XSS)
HTMLの中に悪意あるJavaScriptを埋め込むことで攻撃を行う。XSSに対する脆弱性のあるサイトは直接攻撃を受けるわけではないが、攻撃の踏み台にされてしまう。
対策
- サニタイジング:HTMLに埋め込むと元のHTMLの構造を破壊する文字列を削除したり、無害な文字列に変換したりする必要がある。
セッションハイジャック(Session Hijack)
クライアントとサーバ間でやり取りされているセッションIDを第三者が盗み取ることで、Webサイトの利用者になりすましてサービスを利用されてしまう。
対策
- XSSの対策:最もよく用いられるセッションハイジャックの手段がXSSであるため、XSSの対策がセッションハイジャックの対策にも繋がる。
- 通信経路の暗号化:通信経路をSSLによって暗号化する必要がある。
- セッション・タイムアウト値の変更:セッション情報が削除されるタイムアウト値が長すぎると、セッションIDが盗まれた場合に被害を受けやすくなるため、安全性と操作性のバランスを考慮して値を設定する必要がある。
- セッションIDのランダム化:セッションIDは十分大きな桁の数字の中からランダムに選択されたものである必要がある。
クロスサイトリクエストフォージェリ(Cross Site Request Forgery)
攻撃者が捏造したフォームから強制的に情報をサブミットすることで、掲示板に意図しない書き込みをされたり、強制的に買い物をさせられたりする。
対策
- ワンタイムトークンによる確認
- まず、通常どおり、WebブラウザからAPサーバに対してフォームを表示するためのリクエストが送られる。
- ここで、APサーバはワンタイムトークンを発行し、Webブラウザへ返すフォームの内部に
hidden
パラメータを使って埋め込んでおく。 - フォームを受け取ったブラウザでは、パラメータを入力してサブミットするが、この時に
hidden
パラメータに格納されたワンタイムトークンも同時にサーバへ送信する。 - リクエストを受け取ったサーバでは、トークンを取り出して、記憶しているトークンの一覧と照合し、自サーバで発行したトークンであることが確認できれば、サブミットを受け入れ、そのワンタイムトークンを使用済みとする。
強制ブラウズ(Forceful Browsing)
WebブラウザのアドレスバーにURLを直接入力することで、本来表示されるべきではない画面を表示させる。
対策
- 認証情報の確認:ログイン必須のページでは必ずログイン状態をチェックし、未ログインであればログイン画面にリダイレクトさせるといった対処をとる必要がある。
- インデックスページの無効化:Webサーバの設定で、ディレクトリのファイル一覧を表示する機能は無効化する。
ディレクトリトラバーサル(Directory Traversal)
リクエストで渡された文字列を使ってシステム内のファイルを表示するようなアプリケーションで、プライベートなファイルを閲覧されてしまう。¥0
という文字列の終端を表す文字列を用いたヌルバイト(Null byte)
と呼ばれる攻撃と併用されることもある。
対策
- サニタイジング:
/
や¥
などのファイル名には使用されない文字をサニタイジングで削除することが有効である。 - ファイルシステムのアクセス権を設定する。
- ユーザから入力されたパラメータをファイル名として使用する設計にしない。
「戻る」ボタン
「戻る」ボタンで前のページを表示する際は、Webサーバにリクエストを再発行するのではなく、キャッシュに保存されたデータを利用している。静的なページではこれで問題はないが、セッションで状態を管理している場合は誤動作の原因となる。
対策
- キャッシュの無効化:キャッシュを防ぐには、HTTPレスポンスの
Cache-Control
ヘッダ等でno-store
,no-cache
,must-revalidate
を指定する必要がある。 - 「戻る」ボタンの無効化:JavaScriptが無効化されていない場合に限り、「戻る」ボタンを無効化できる。
- ワンタイムトークンの利用:
CSRF
の対策と同じでワンタイムトークンの発行&確認で誤作動を防ぐことができる。
ダブルサブミット
フォームをサブミットした際にレスポンスが遅く、サブミットボタンを連打されてしまった場合に、サブミットが複数回行われてしまう。
対策
- JavaScriptによる対策:ボタンが押されたかどうかをisSubmittedなどの変数に格納し、変数の値に応じてサブミットできるかどうかを制御する。
- ワンタイムトークンの利用:他の対策と同じでトークンの発行、その状態の管理で対策が可能である。