アプリケーションに何かしらのバグがあると様々な問題を起こす可能性があります。 そしてバグの中には悪用ができてしまうものが存在します。そうしたものは脆弱性と呼ばれます。
脆弱性が問題となる理由は下記の通りです。
- 経済的損失の恐れ
- 法的な要求の恐れ
- 利用者が回復不能なダメージを受ける恐れ
- 利用者に安全性に関して保証できなくなる恐れ
- 攻撃インフラ構築に加担してしまう恐れ
脆弱性の発生原因には以下の2種類に分類ができます。
- バグによるもの ・・・ セキュリティに無関係なところに発生しがちである
- チェック機能の不足によるもの ・・・ セキュリティチェックが乏しい場合に発生する
セキュリティ機能は、システムに安全性を強化する機能であり、セキュリティバグはそこから漏れ出した脆弱性(バグ)のことを示します。 セキュリティ機能はセキュリティ要件とされシステムの要件定義に組み込まれる場合があります。
Webを利用したアプリケーションやシステムの開発のセキュリティ設計において、いくつかの公的機関がガイドラインを公開しています。 これらの資料のいくつかは実際の開発の際に企業や組織で活用されいます。
資料名 | URL | 提供機関 |
---|---|---|
安全なウェブサイトの作り方 | https://www.ipa.go.jp/files/000017316.pdf | IPA |
安全なSQLの呼び出し方 | https://www.ipa.go.jp/files/000017320.pdf | IPA |
ウェブ健康診断仕様 | https://www.ipa.go.jp/files/000017319.pdf | IPA |
OWASP Top 10 | https://owasp.org/www-project-top-ten/ | OWASP |
Webアプリケーションの脆弱性には、Web特有の性質に由来するものがあります。 Webでは主にHTTPリクエストというWebサーバに対する命令と、HTTPレスポンスというWebサーバから返された内容の2つを使って情報のやり取りを行います。
HTTPリクエストはブラウザなどからWebサーバに送られた要求であり、構成は以下のようになります。
HTTPリクエストラインはWebサーバに対する命令に相当する部分であり、構成は以下のようになります。 例
GET / https://www.yahoo.co.jp HTTP/5.1
構造
メソッド / URL プロトコル/バージョン
HTTPのメソッドには以下のようなものがあります。
- GET
- POST
- HEAD etc...
HTTPレスポンスはwebサーバから返された内容であり、構成は以下のようになります。
ステータスラインは、リクエストメッセージの処理結果のステータスを返します。構成は以下の通りです。 例
HTTP/5.1 200 OK
構成
プロトコル/バージョン ステータスコード テキストフレーズ
ステータスコードは100の位に意味を持ち以下表のように分類されます。
ステータスコード | 概要 |
---|---|
1xx | 処理が継続している |
2xx | 正常終了 |
3xx | リダイレクト |
4xx | クライアントエラ |
5xx | サーバエラ |
代表的なヘッダに以下なようなものがあります。
- Content-Length ・・・ Bodyのバイト数を示す
- Content-tyle ・・・ MIMEタイプというリソース種類を指定する
MIME Type | 意味 |
---|---|
text/plain | テキスト |
text/html | HTML文書 |
application/xml | xml文書 |
text/css | css |
image/gif | GIF画像 |
image/jpeg | jpeg画像 |
image/png | png画像 |
application/pdf | pdf文書 |
「入力・確認・登録」形式の入力フォームのHTTPメッセージを確認します。
入力→確認の遷移におけるHTTPリクエストの内容の抜粋部分の構造例を以下に示します。
例
POST https://yamot.co.jp/31/31-003.php HTTP/5.1
Referer: https://yamot.co.jp/31/31-002.php
Content-Type: application/x-www-form-urlencoded
Host: yamot.co.jp
name=%5E%BE%E3%B8%B5%B8%A9%E4%A4&mail=test&40yamot.co.jp&gender=%5E%BE%E3%B8%B5%B8
ヘッダとボディは空行で区切られます。 HTTPリクエストのボディにはPOSTメソッドにより送信される値が入ります。
HTTPリクエストのヘッダに「Referer」がつくことがあります。 これはリンク元のURLを示すヘッダであり、form要素によるフォーム送信やa要素によるリンク、img要素による画像参照などでつけられます。 Refererがセキュリティ上の問題となるケースは、URLに秘密情報を含んでいる場合です。 多くの場合、URLにセッションIDが含まれている場合で、Referer経由で外部に漏洩しなりすましに悪用される場合があります。
GETメソッドとPOSTメソッドの使い分けのガイドラインとして以下のようなものが示されています。
- GETメソッドは参照(リソース取得)のみに用いる
- GETメソッドは副作用(リソース取得以外の作用)がないことが期待される
- 秘密情報の送信にはPOSTメソッドを用いること
副作用の例としては「サーバ側でのデータ追加、更新、削除がおきる作用」があります。これらのような更新系にはPOSTメソッドを用いる必要があります。 また、GETメソッドはURLにクエリ文字列の形でパラメータを渡しますが、ブラウザやサーバが処理できるURL長には上限があるため、データ量が多い場合はPOSTメソッドを使う方が安全である。 秘密情報をPOSTで送信すべき理由はGETの場合で送信すると以下のような場合があるためである。
- URL上に指定されたパラメータがReferer経由で漏洩する
- URL上に指定されたパラメータがアクセスログに残る
- URL上のパラメータがブラウザのアドレスバーに表示され他人に覗かれる
- パラメータつきのURLを利用者がSNSなどで共有してしまう
まとめると、これらが以下の項目で1つでも当てはまる場合はPOSTメソッドを用い、そうでない場合はGETメソッドを利用すべきであると言える。
- データ更新などの副作用を伴うリクエストの場合
- 秘密情報を送信する場合
- 送信するデータの総量が多い場合
2.6 hiddenパラメータの危険性
HTMLにおいてデータの受け渡しにhiddenパラメータは使われます。
<form>
<input type="hidden" name="name" value="value">
</form>
これはHTMLの表示として見えなくてもHTML文書上には記述されます。 HTTPはクライアントの状態を覚えておく設計になっていません(HTTPのステートレス性)。 そのためレスポンス内のhiddenパラメータに状態を記録します。
HTTPというレイヤー下においては、hiddenパラメータと同じように、テキストボックスやラジオボタンの選択肢も ブラウザで見る画面上では変更できない値は利用者が書き換え可能であると言えます。
hiddenパラメータのメリット
hiddenパラメータは利用者が書き換えできる特徴もあるが、情報漏洩や第3者の書き換えに対しては堅牢でもあります。 hiddepnパラメータや後述にあるセッション変数やクッキーのメリットとデメリットをまとめます。
種類 | メリット | デメリット |
---|---|---|
hiddenパラメータ | 情報漏洩や第3者の書き換えに対しては堅牢 | 利用者が書き換え可能 |
セッション変数 | 利用者自身の書き換えが難しい | セッションIDの固定化攻撃に弱い |
クッキー | セッションIDの固定化攻撃に弱い |
これらの特徴から、利用者により書き換えられて困る認証や認可に関する情報はセッション変数を用い、それ以外は基本的にhiddenパラメータを用いるとよい。 とくにログイン前の状態ではセッション変数の利用は避け、hiddenパラメータを用いることが情報漏洩に対して安全である。
HTTPには認証機能がサポートされており、HTTP認証と称されます。 種類にはBASIC認証、NTLM認証、Digest認証などがあります。 HTTPがステートレスであるように、HTTP認証もステートレスとなっています。
Basic認証は特定のディレクトリに設定をすることでIDとパスワードがなければそのディレクトリ以下のコンテンツを閲覧できなくすることができる機能です。 この仕組みはWebサーバの設定でも実現できる場合が多いが、各種フレームワークや言語の機能で実装できる場合もあります。
Basic認証において、ユーザ名やID、パスワードを入力し認証ボタンを押すとHTTPリクエストには以下のようなヘッダが付与されます。 例
Authorization: Basic dXNlcjE6cGFzcE=
Basic後ろの文字列はIDやパスワードをコロン「:」で区切りつなげ、Basic64エンコードしたものです。 一度Basic認証に成功すると、Basic認証を指定したディレクトリへのリクエストにはブラウザが自動的にAuthorizationヘッダを付与します。 そのため見かけ上では認証状態が保持されているように見えても、リクエスト毎にIdとパスワードが送信されており、認証状態自体はどこにも保存されていない。 そのためBasic認証はステートレスであり、ログアウトという概念も存在しません。 ちなみにBasic認証をログアウトのような状態にするにはBasic認証のキャッシュを削除します。 具体的にはURLがexample.comの場合「https://me@ exapmple.com」のようにすることでその認証をしたブラウザでログアウトのような状態にすることが可能です。
セッション管理とはクライアントとサーバー間でその情報を保持し、アクセス制御を一つの集合体として管理する仕組みである。 このセッション管理をHTTPで実現する目的でCookieという仕組みがある。Cookieはセッション管理という機能の実現に使われています。 具体的にCookieはサーバ側からブラウザに対して「名前=変数」の組を覚えておくように指示するものです。
session_startを組み込んであるWebページ(例:ログイン画面等)のレスポンスヘッダでは、以下にように記述されます。 例
HTTP/5.1 200 OK
Set-Cookie: PHPSESSID-gg5144evrhmdialeksjn45ndkjb53; path=/
Content-Length: 279
Content-Type: thxt/html; charset=UTF-8
<html>
このレスポンスヘッダの「Set-Cookie」により、Webサーバはブラウザに対してクッキー値を覚えるように指示します。 また、各入力値(ログインIDやパスワード)を入力しボタンを押すと、以下のようなHTTPリクエストがブラウザからサーバに送信されます。 例
POST /31/31-021.php HTTP/5.1
Referer: http://exapmle.com/31/31-020.php
Content-Type: application/x-www-form-urlencoded
Host: example.com
Content-Length: 18
Cookie: PHPSESSID=gg55427jsbhj232sjb2asjn
ID=user1&PWD=pass1
クッキー値を覚えたブラウザは、その後同一のサイト(例の場合:example.com)にリクエストを送信する際に 覚えたクッキー値(PHPSESSID=...)を送信します。なお、クッキーには有効期限が設定可能ですが、指定しない場合はブラウザを終了するまで有効です。 例の場合、PHPSESSIDのクッキー値はセッションIDと呼ばれます。セッション情報にアクセスするためのキー情報をとなります。
クッキーは少量のデータをブラウザで覚えておけるものですが、アプリケーションデータを保持する目的でクッキーそのものに値を言えっることはあまりしない。
- クッキーが保持できる値の個数や文字列長には制限がある
- クッキー値は利用者本人から参照・変更できるため、秘密情報の格納には不向き
このため、多くの場合クッキーには「整理番号」としてセッションIDを格納し、実際の値はサーバ側で管理する方法が用いられます。これはクッキーによるセッション管理と呼ばれます。 またセッションIDは連番の場合なりすましされる場合があるため、十分な桁の乱数を用います。 セッションIDに求められる要件には以下のようなものがあります。
- 第3者がセッションIDを推測できないこと
- 第3者からセッションIDが強制されないこと
- 第3者にセッションIDが漏洩しないこと
1の要件を満たすのは乱数の質が要求されます。乱数に規則性がある場合、セッションIDを収集することで他人のセッションIDが推測できる場合があります。その対策として、セッションIDは暗号論的疑似乱数生成器を用いて生成します。この高度な乱数生成器の例は電子政府推奨暗号リスト(http://www.cryptrec.go.jp/list.html )に一覧が記載されています。 実際の開発ではセッションIDを自作するのではなく、各種WEB開発ツール(PHP,Laravel,Rails,.Net....)で提供されるセッションIDが利用されます。 基本的にセッション管理機構は自作しないことが重要です。
この攻撃は、攻撃者が正規利用者に対してセッションIDを強制する攻撃です。
これを防ぐには認証されたタイミングでセッションIDを変更することで実現できます。
セッションIDが漏洩した場合、他の利用者になりすましができるため対策を講じる必要があります。 セッションIDが漏洩する原因には以下のようなものがあります。
- クッキー発行の際の属性に不備がある
- ネットワーク上でセッションIDが盗聴される
- クロスサイトスクリプティングなどのアプリの脆弱性により漏洩する
- 開発機能(PHP,Rails...)やブラウザなどのプラットホームの脆弱性により漏洩する
- セッションIDをURLに保持する場合に、Refererヘッダより流失する
セッションIDがネットワーク上で盗聴される場所は公的無線LAN(Free Wifi)などです。 セッションIDをネットワーク上の盗聴から保護するにはTLSによる暗号化が有効です。その際にクッキーを発行する属性指定に注意する必要があります。
クッキーを発行する際に、オプション属性を設定できます。主な属性は以下の通りです。 セキュリティ上重要な属性は、「Domain」「Secure」「HttpOnly」 の3つです。
属性 | 意味 |
---|---|
Domain | ブラウザがクッキー値を送信するサーバのドメイン |
Path | ブラウザがクッキー値を送信するURLのディレクトリ |
Expires | クッキー値の有効期限、指定なしの場合ブラウザ終了まで |
Secure | HTTPSの場合のみクッキーを送信 |
HttpOnly | JavaScriptでクッキーを取得できないようにする |
クッキーはデフォルトではクッキーをセットしたサーバのみに送信されます。 Domain属性は、「複数のサーバに送信されるクッキーを生成したい場合」に使用します。 Domain名を指定した場合、例えば「example.com」を指定したとするとサブドメインにもクッキーが送信されることとなります。 そのためDomain属性は原則指定しないのがルールとなります。
Secure属性はHTTPSの通信の場合のみサーバに送信されます。この属性はHTTPS送信を保証する目的で指定されます。
HttpOnly属性は、JavaScriptからアクセスできないクッキーを設定するものです。 クッキーとして格納されたセッションIDを盗み出す攻撃にはクロスサイトスクリプティングがあり、これはJavaScriptを悪用してクッキーを盗み出すものです。 HttpOnly属性のみでクロスサイトスクリプティングは防げませんが、攻撃を難しくできるため、セッションIDにHttpOnly属性をつけることが推奨されます。
最近のブラウザにはCSRF脆弱性対策としてSameSite属性が機能として追加されています。 sameSite属性には値としてStrict(厳しい),Lax(緩い),None(なし)が指定できます。 通常はSameSite=Laxを指定します。この結果として他サイトからPOSTメソッドで遷移した場合はクッキーは送信されなくなります。
Webを用いた攻撃手段には能動攻撃と受動攻撃に分類されます。
能動攻撃は攻撃者がWebサーバに直接攻撃することです。 代表例としてはSQLインジェクション攻撃があります。
受動攻撃は攻撃者がサーバを直接攻撃するのではなく、Webサイトの利用者にわなを仕掛けることにより、罠を閲覧したユーザを通しWebアプリを攻撃する手法です。
単純な受動攻撃例としては「罠サイトに利用者を誘導するパターン」があります。 このケースでは怪しいサイトを閲覧してマルウェアに感染する場合です。 ブラウザ本体や各種アプリケーションの脆弱性をついた攻撃となる場合がほとんどです。
このタイプの攻撃では以下のようなステップを踏みます。
- 攻撃者はあらかじめ正規サイトを攻撃しコンテンツに仕掛けを仕組む
- サイト利用者が仕掛けを含むコンテンツを取得し閲覧する
- サイト利用者の端末にマルウェアが感染する
この手法では下記の4種類が多いです。
- FTPなどのパスワードを不正入手しコンテンツを書き換える
- Webサーバの脆弱性をついた攻撃によりコンテンツを書き換える
- SQLインジェクション攻撃によりコンテンツを書き換える
- SNSなどの利用者が投稿可能なサイト機能のクロスサイトスクリプティング脆弱性を悪用する
このタイプの攻撃は罠サイトと正規サイトをまたがった攻撃です。 例としては以下のようなステップがあります。
- 利用者が罠サイトを閲覧する
- 罠サイトから仕掛けを含むHTMLをダウンロードする
- HTMLの仕掛けが発動し正規サイトに攻撃のレスポンスを送信する
- 正規サイトからJavaScriptなどの仕掛けを含むレスポンスが返送される
このタイプの攻撃は正規サイトにログインしている利用者のアカウントを悪用した攻撃です。 このタイプの攻撃例には以下のようなものがあります。
- CSRF(:項目3による攻撃のタイプ)
- XSS・HTTPヘッダインジェクション(:項目4の攻撃のタイプ)
受動攻撃に関してはブラウザやWebサイトそれぞれで対策を行う必要があります。
ブラウザ上では利用者の安全性を高めるための機能を提供しています。
- 利用者に配布元を確認させた上で許可した場合のみ実行する
- プログラムのできることを制限するサンドボックス環境を用意する
サンドボックスはJavaScriptやAdobe Flash playerなどで採用されている考え方であり、プログラムにできることを制限を付けることで悪意のあるプログラムから利用者を守るように配慮されています。
JavaScriptのサンドボックスでは以下のように機能が制限されます。
- ローカルファイルへのアクセス禁止
- プリンタなどの資源の利用禁止
- ネットワークアクセスの制限(同一オリジンポリシ)
同一オリジンポリシは、JavaScriptなどのクライアントスクリプトからサイトをまたがったアクセスを禁止するセキュリティ上の制限であり、ブラウザのサンドボックスに提供された機能の1つです。 簡単に言うと、「JavaScript で自由にやりとりできるところは、その JavaScript をとってきたところと同一の場所だけに制限する」 ということです。
同一オリジンを満たすのは下記条件の全てを満たす場合です。
- URLのホストが一致している
- スキーム(プロトコル)が一致している
- ポート番号が一致している
同一オリジンポリシによる保護対象にはiframe内の要素にとどまらず、Ajaxに使用されるXMLHttpRequestオブジェクトでアクセスできるURLなどにも及びます。XMLHttpRequestに関しては相手の許可があれば同一オリジンでなくても通信可能なCORSという規格が策定されました。
ブラウザは同一オリジンポリシにより受動攻撃を阻止していますが、アプリケーションに脆弱性があると受動攻撃を受ける場合があります。 その代表例に「クロスサイトスクリプティング(XSS)攻撃」があります。 XSSは同一オリジンポリシをかいくぐった攻撃手法であり、iframeの外側のスクリプトで内側をアクセスしようとした場合は同一オリジンポリシ違反でアクセス拒否されますが、何らかの手法によりiframeの内側にJavaScriptを送り込み実行させ同一オリジンポリシをかいくぐります。
JavaScript以外のブラウザ機能で、クロスドメインアクセスが許可されているものには以下のようなものがあります。
frame要素とiframe要素はクロスドメインアクセスができます。 ただしJavaScriptのよるクロスドメインのドキュメントにアクセスすることは禁止されています。
img要素のsrc属性はクロスドメインの指定が可能です。 またHTML5のcanvas要素を用いると、JavaScriptにより画像の中身にアクセスできますが、この場合はCORSと同一オリジンポリシの制約を受けます。 そのためクロスドメインの画像参照が問題になることは基本的にはない。
scriptの要素にsrc属性を指定すると他サイトからJavaScriptを読み込むことができます。
CSSはクロスドメインでの読み込みが可能な他、HTMLではlink要素で、CSSでは@importで、JavaScriptではaddImportメソッドで可能です。
form要素のaction属性でもクロスドメインの指定が可能です。 また、formのsubmitはJavaScriptから常に操作できます。このformの特性を悪用した攻撃手法にはクロスサイトリクエストフォージェリ(CSFR)攻撃があります。CRSF攻撃ではユーザの意図しないformを送信させられ、アプリケーションの機能が勝手に実行されます。
CORSは同一オリジンポリシーによって設けられた制限を緩めるものです。 具体的には、HTTP ヘッダーの転送で構成されるシステムであり、ブラウザーがオリジンをまたいだリクエストのレスポンスに、フロントエンドの JavaScriptがアクセスすることをブロックするかどうかを決めるものです。 CORS で定義された方法に従えば、たとえ同一オリジンでなくても、fetchやXMLHttpRequest (XHR)などJavaScript による自由なリソース取得が許可されます。 CORS ではリクエストの種類がシンプルリクエストとプリフライトリクエストの2種類に分けられています。 シンプルリクエストとプリフライトリクエストのレスポンスヘッダに共通して記述するものにAccsess Control Allow Originがあります。
Access-Control-Allow-Origin: https://example.com
シンプルリクエストといわれるのは下記メソッドです。
- GET
- POST
- HEAD
プリフライトリクエストは、リクエストの始めにOPTIONS メソッドで対象の異なるオリジンにリクエストを送り、実際のリクエストを送っても問題ないか確認します。該当するリクエストは以下になります。
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
プリフライトリクエストのレスポンスとして、アクセスを許可するメソッドをレスポンスヘッダーに含める必要があります。
Access-Control-Allow-Methods: POST, PUT, DELETE, PATCH
プリフライトリクエストでやり取りするヘッダには以下のようなものがあります。
要求の種類 | リクエスト | レスポンス |
---|---|---|
メソッドに対する許可 | Accsess-Control-Request-Method | Accsess-Control-Request-Methods |
ヘッダに対する許可 | Accsess-Control-Request-Headers | Accsess-Control-Request-Headers |
オリジンに対する許可 | Origin | Accsess-Control-Request-Origin |
通常ではHTTP認証やCookieも含めてリクエストを送りたいという場合、デフォルトでは異なる Origin に対して送信されません。 オリジンをまたいでCookieを送りたい場合はレスポンスヘッダに以下の記述を追加します。
Access-Control-Allow-Credentials: true
例としてXMLHttpRequestを使う場合とFetch APIを使う場合の記述例を示します。
●XMLHttpRequestの例
const xhr = new XMLHttpRequest();
xhr.withCredentials = true; // ここを追加。
●Fetch APIの例
fetch('https://exapmle.co.jp', {
mode: 'cors',
credentials: 'include' // ここを追加。
});