前書き
Webアプリケーションに脆弱性(セキュリティバグ)を埋め込まないための、基本中の基本的な知識についてまとめました。
この記事の内容だけでセキュリティを担保できるものでは全くありませんが、学習のファーストステップになれば幸いです。
HTTPやHTMLの基本的な知識があることを前提にしていますので、それが不足されている方には、以下の書籍をお勧めします。
Webを支える技術
記事の内容に誤りがありましたらコメント等で教えていただけると幸いです(掘り下げ不足は多々あると思いますが…)。
基礎知識
セッション管理
WWWサービスで使用されるHTTPプロトコルでは、クライアントからWebサーバーへのリクエストがあるたび、クライアントとサーバー間でコネクションが成立し、リクエストに対するレスポンスが行われ、コネクションは切断される。
そのため、Webサーバーに対する複数リクエストが同一クライアントから送信されたものであるかどうかを判断することは不可能である。一般的なWebブラウジングでは特に問題にはならないが、電子商取引サイトのようにWebサーバーにユーザがログインしてからログアウトするまで、ログイン情報を保持したままページを移管するには、このままでは問題がある。
そこで、クライアントとサーバー間でその情報を保持し、アクセス制御を一つの集合体として管理する仕組みが必要となる。この仕組みをセッション管理と呼んでいる。Webサーバー側で発行されたセッションIDをクライアントのCookieに持たせることで、特定ユーザの識別を行う。
引用元リンク:
https://www.ipa.go.jp/security/awareness/administrator/secure-web/chap6/6_session-1.html
アプリケーションでのセッションIDの扱いが適切でないと、脆弱性につながる恐れがあります。
同一オリジンポリシー
例えば以下のように、悪意のあるサイトから正規サイトの秘密情報を抜き取られるとヤバいことになります。
このため、ブラウザにはこのようなヤバいアクセスを制限する機能が備わっています。
オリジンは「スキーム + ホスト + ポート番号」の組み合わせをいいます。
2つのオリジンの「スキーム + ホスト + ポート番号」が一致している場合、両者は「同一のオリジンである」とみなされます。Same Origin Policy(同一オリジンポリシー、同一生成元ポリシー)は、あるオリジンで読み込まれたリソース(HTML, スクリプト, Cookieなど)が、異なるオリジン(クロスオリジンとも呼ばれる)のリソースとの通信・アクセスを制限する、Webブラウザが持つセキュリティ機能です。
引用元リンク:
https://yamory.io/blog/about-cors/
同一オリジンポリシーが適用される典型例は、上のようなJavaScriptによるクロスドメインのドキュメントアクセスと、JavaScriptでの非同期通信(いわゆるAjax)です。
ただし、同一オリジンポリシーが適用されない(クロスオリジンへのアクセスが許可される)ケースもあります。
代表的な物としては以下です。
-
<iframe>
のsrc属性指定 -
<img>
のsrc属性指定 -
<script>
のsrc属性指定 - CSS
-
<form>
のaction属性指定
Cross-Origin-Resource Sharing (CORS)
「同一オリジンポリシーの制限を超えて、データをやりとりしたい」というニーズがある場合があります。
例えば、同じ会社のサイトで、ドメインが異なるショッピングサービスと金融サービスを展開していて、相互にデータをやりとりしたい、など…
このようなニーズのために策定された仕様がCORSです。
読み方は「シーオーアールエス」または「コルス」。
シンプルなリクエスト
以下の条件すべてに合致する場合は、クロスドメインへのAjax通信によるリクエストが相手側の許可なしに可能です。
- リクエストのメソッドが下記のいずれかである
- GET
- POST
- HEAD
- Content-Typeヘッダーが下記のいずれかである
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- 一般的なヘッダのみで、特殊なヘッダが存在しない
プリフライトリクエスト
「シンプルなリクエスト」に合致しない場合、ブラウザが自動的に「プリフライトリクエスト」を行います。
サーバーサイドでは、
Access-Control-Allow-Origin
Access-Control-Allow-Methods
レスポンスヘッダーを付与することにより、オリジンを越えたアクセスを許可することをブラウザに知らせます。
「この要求元からのこのメソッドに許可します」という返答です。
さらに、Cookie送信やBasic認証を行いたい場合
CORSが適用されるリクエストにおいて、デフォルトではCookieやBasic認証に関わる情報は送信されません。
クロスオリジンのAjaxリクエストにおいて、Cookieの送信、Basic認証といったクレデンシャル情報(Creadentials)を付加したい場合、クライアントサイド(JavaScript)側でwithCredentialsと呼ばれるオプションをあらかじめJavaScript側で付与しておく必要があります。
引用元リンク:
https://yamory.io/blog/about-cors/
メジャーな脆弱性と基本的な対策
クロスサイト・スクリプティング
略してXSS、読み方はそのままエックスエスエス。
典型的な例
http://example.com/?gender=(フォームで入力した値)
上のようなGETリクエストにおいて、クエリパラメータgender
の値をサーバー側で検証なく処理しHTMLを生成してしまうと、
http://example.com/?gender=<script>(悪意のあるスクリプト)</script>
というようなリクエストによって、HTMLに悪意あるJavaScriptをインジェクション(注入)されてしまう恐れがあります。
スパムメールなどで、利用者にそのURLを「踏ませる」ことで攻撃が成立します。
GETのクエリパラメータだけでなく、POSTでの攻撃手法もあります。
JavaScriptだけではなく、不正なHTMLタグをインジェクションさせる攻撃手法もあります。
参考リンク:
Wikipedia - クロスサイト・スクリプティング
主な影響
- 偽のHTMLフォームが出力されてしまうことによる個人情報漏洩
- Cookieの内容を盗まれてしまうことによる成りすまし、個人情報漏洩 など
基本的な対策
- HTMLの文法上特別な意味を持つ記号(メタ文字)をエスケープする。つまり入力された記号をメタ文字ではなく単なる文字列として扱う。
- 入力された記号を正しく処理するために、レスポンスヘッダーにて文字エンコーディングを明示する。
SQLインジェクション
典型的な例
SELECT * FROM users WHERE name = '(入力値)';
上の'(入力値)'
のように、外部から受け取る文字列を元にSQLを生成している箇所があり、値を検証なく処理してしまうと、データベースに発行されるSQLは
SELECT * FROM users WHERE name = 't' OR 't' = 't';
となり、全レコードを取得できてしまう恐れがあります。
参考リンク:
Wikipedia - SQLインジェクション
主な影響
- ユーザーの一覧など、データベース内の情報漏洩
- データベースの内容の書き換え
- 認証回避(正規のIDとパスワードを用いずにログインされてしまう) など
基本的な対策
- SQLを文字列ベースで動的に組み立てずに、データベースの「バインド機構」(またはプレースホルダ、パラメタライズドクエリ)を利用する。
クロスサイト・リクエストフォージェリ
略してCSRF。読み方は「シーエスアールエフ」または「シーサーフ」。
典型的な例
以下のような「罠サイト」へのリンクが、フィッシング・メールで送られてきたと仮定します。
サンプルHTML(罠サイト):
<body onload="document.forms[0].submit()">
<form action="http://example.jp/password.php" method="POST">
<input type="hidden" name="pwd" value="cracked">
</form>
</body>
ソース引用:
安全なWebアプリケーションの作り方 第2版
前提として、Webには以下の特性があります。
(1) form要素のaction属性には同一オリジンポリシーが適用されず、どのドメインのURLでも指定できる
(2) Cookieに保管された情報、つまりセッションID等はブラウザによって対象サイトに自動的に送信される
すなわち、正規のサイトにログインしたままの状態で上記のような罠サイトのリンクを踏んだ瞬間、以下のようなリクエストが送信される恐れがあります。
- 正規のURLに対して、
- 有効なセッションIDをCookieに含み、
- 攻撃者が仕込んだ(下のサンプルHTMLでは
cracked
という)パスワードを含む リクエスト
このリクエストが不正であることを検知できないと、攻撃者によるパスワード書き換えが成立してしまいます。
主な影響
- 成りすましによる物品の購入
- 成りすましによるSNSへの書き込み
- パスワード変更 など
基本的な対策
- トークンを生成してhidden要素に埋め込み、トークン検証することでリクエストの真正性を検証する
- 重要な処理を受け付ける前にパスワードの再入力を挟む
- HTTPリクエストヘッダーのRefererフイールドによってリクエストの要求元を検証する
※どの機能を対象にするのか、要件定義や基本設計時に検討が必要
後書き
繰り返しになりますが、基本中の基本的なことしか書けていませんので、さらに広く・深く理解するために、以下の書籍をお勧めします。
安全なWebアプリケーションの作り方 第2版