(Last Updated On: 2019年2月21日)

できれば全体をよく読む方が良いのですが、まとめとして対策を一覧できるようブログにしました。引用は2017年版 OWASP TOP 10からです。

A1 インジェクション

防止方法

インジェクションを防止するためにはコマンドとクエリからデータを常に分けておくことが必要です。
• 推奨される選択肢は安全なAPIを使用すること。インタープリタの使用を完全に避ける、パラメータ化されたインターフェースを利用する、または、オブジェクト・リレーショナル・マッピング・ツール(ORM)を使用するように移行すること。注意:パラメータ化されていたとしても、ストアドプロシージャでは、PL/SQLまたはT-SQLによってクエリとデータを連結したり、EXECUTE IMMEDIATEやexec()を利用して悪意のあるデータを実行することによって、SQLインジェクションを発生させることができる。
ポジティブに言い換えると「ホワイトリスト」によるサーバサイドの入力検証を用いる。特殊文字を必要とする多くのアプリケーション、たとえばモバイルアプリケーション用のテキストエリアやAPIなどにおいては完全な防御方法とはならない。
• 上記の対応が困難な動的クエリでは、そのインタープリタ固有のエスケープ構文を使用して特殊文字をエスケープする。注意:テーブル名やカラム名などのSQLストラクチャに対してはエスケープができない。そのため、ユーザ指定のストラクチャ名は危険である。これはレポート作成ソフトウェアに存在する一般的な問題である
• クエリ内でLIMIT句やその他のSQL制御を使用することで、SQLインジェクション攻撃が発生した場合のレコードの大量漏洩を防ぐ。

元々はSQLインジェクション対策用の項目だったので、SQLインジェクション対策のみのように見えますが、「A1 インジェクション」が指すインジェクション脆弱性はLDAPやXpathインジェクション、改行インジェクション、正規表現インジェクション、など全てのインジェクション脆弱性が対象です。

XPathやLDAPはエスケープできない場合があります。「テーブル名やカラム名などのSQLストラクチャに対してはエスケープができない」と記載されていますが、一般的なRDBMSはエスケープできますセキュアコーディング原則7(出力の無害化)に従い、識別子(カラム名やテーブル名)をエスケープできる場合はエスケープします。エスケープできない場合はバリデーションします。(ORMなどライブラリではエスケープしないと仕様上許可される識別子が使えなくなるので、エスケープするしかないです。アプリケーションならバリデーションの方がオススメです。PostgresSQLは識別子(上記文書では「ストラクチャ名」)のエスケープAPIを提供しています。)

識別子エスケープを仕様として持っているにも関わらず、エスケープAPIを持たないRDBMSもあります。この場合、文字エンコーディングがバリデーションされていることを保証した上でエスケープ仕様に応じ、文字列操作を行いエスケープします。

この防止方法に記載されている通り、ホワイトリスト型による入力バリデーション(入力検証)は基本的なインジェクション対策です。セキュアコーディング原則では入力バリデーションと出力無害化は独立したセキュリティ対策ですが、誤解が多かったので「特殊文字を必要とする多くのアプリケーション、たとえばモバイルアプリケーション用のテキストエリアやAPIなどにおいては完全な防御方法とはならない」と態々注意をしています。

重要: よく見落されていますが、入力バリデーションでデータが妥当であることが検証済みであること、が安全にSQLを実行する必須条件(CWE-20です。パラメーターのバリデーション漏れは危険ですが、識別子のバリデーション漏れも非常に危険です。何でもSELECTできる例:

pg_query_params("SELECT ".pg_escape_identifier($_GET['col'])." FROM ".pg_escape_identifier($_GET['table'])." ORDER BY ".pg_escape_identifier($_GET['sort_col']), []); 

pg_query_params("SELECT ".pg_escape_identifier($_GET['col'])." FROM ".pg_escape_identifier($_GET['table'])." ORDER BY ".pg_escape_identifier($_GET['sort_col']), []); 

のようにSQL識別子がユーザー入力になるユーザー指定の抽出カラム/ソートカラムを許可するアプリケーションは一般的に在ります。「ホワイトリスト」によるサーバサイドの入力検証を用いる、と記載されている通りインジェクション対策に入力バリデーションは絶対に欠かせない対策です。

入力バリデーションを行わない未検証のデータ(≒出鱈目なデータ)はどこで、どのような問題を発生させるか、開発者でも解りません。先ず第一に実施すべきセキュリティ対策です。

よく誤解されていることですが、セキュアな構造のアプリケーションでは入力対策と出力対策は独立した対策でそれぞれ可能な限りのリスクを排除します。(セキュアコーディング原則1と原則7)

入力対策だけ、出力対策だけ、どちらか一方だけではソフトウェアの構造的に安全性を十分に保証する構造になりません。

A2 認証の不備

防止方法


• 自動化された攻撃、アカウントリスト攻撃、総当たり攻撃、盗まれたユーザ名/パスワードを再利用した攻撃を防ぐために、できる限り多要素認証を実装する。
• 初期アカウント(特に管理者ユーザ)を残したまま出荷およびリリースしない。
• 新しいパスワードまたは変更後のパスワードがTop 10000 worst passwordsのリストにないか照合するようなパスワード検証を実装する。
• NIST 800-63 B’s guidelines in section 5.1.1 for Memorized Secretsや最近の調査に基づくパスワードの方針に、パスワードの長さ、複雑性、定期変更に関するポリシーを適合させる。
• アカウント列挙攻撃への対策としてユーザ登録、パスワード復旧、APIを強化するため、すべての結果表示において同じメッセージを用いる。
• パスワード入力の失敗に対して回数に制限するか、段階的に遅延察せる。すべてのログイン失敗を記録するとともに、アカウントリスト攻撃、総当たり攻撃、または他の攻撃を検知したときにアプリケーション管理者に通知する。
• サーバサイドで、セキュアな、ビルトインのセッション管理機構を使い、ログイン後には新たに高エントロピーのランダムなセッションIDを生成する。セッションIDはURLに含めるべきではなく、セキュアに保存する。また、ログアウト後や、アイドル状態、タイムアウトしたセッションを無効にする。

NIST 800-63 B’s guidelines in section 5.1.1 for Memorized Secrets ではパスワードは何文字以上、といった明確なガイドラインを提示していません。必要な安全性に応じて十分なパスワード長を要求します。

個人的には2要素認証を利用しない銀行口座アプリケーションなら最低でも16文字以上のパスワードを要求すべきだと思います。普通のWebサイトなら少なくとも12文字以上のパスワードを要求する方が良いです。

覚えないパスワードなら最低でも32文字以上、できればランダム文字列90文字程度が最も良いとユーザーに薦めると良いです。

覚えるパスワードの作り方も紹介すると良いです。

A3 機微な情報の露出

防止方法


最低限実施すべきことを以下に挙げます。そして、参考資料を検討してください:
• アプリケーションごとに処理するデータ、保存するデータ、送信するデータを分類する。そして、どのデータがプライバシー関連の法律・規則の要件に該当するか、またどのデータがビジネス上必要なデータか判定する。
• 前述の分類にもとにアクセス制御を実装する。
• 必要のない機微な情報を保存しない。できる限りすぐにそのような機微な情報を破棄するか、PCI DSSに準拠したトークナイゼーションまたはトランケーションを行う。データが残っていなければ盗まれない。
• 保存時にすべての機微な情報を暗号化しているか確認する。
• 最新の暗号強度の高い標準アルゴリズム、プロトコル、暗号鍵を実装しているか確認する。そして適切に暗号鍵を管理する。
• 前方秘匿性(PFS)を有効にしたTLS、サーバサイドによる暗号スイートの優先度決定、セキュアパラメータなどのセキュアなプロトコルで、通信経路上のすべてのデータを暗号化する。HTTP Strict Transport Security (HSTS)のようなディレクティブで暗号化を強制する。
• パスワードを保存する際、Argon2、scrypt、 bcrypt、PBKDF2のようなワークファクタ(遅延ファクタ)のある、強くかつ適応可能なレベルのソルト付きハッシュ関数を用いる。
• 設定とその設定値がそれぞれ独立して効果があるか検証する

「ワークファクタ」とはストレッチングのことを指しています。ストレッチングとはハッシュ関数を複数回適用させて、パスワードデータベースからパスワードを解析する操作を妨害する仕様を言います。例に挙げられているbcyrptやPBKDF2では「ラウンド回数」(ストレッチング用ハッシュ関数の適用回数)とも言われています。

A4 XML 外部エンティティ参照 (XXE)

防止方法


開発者のトレーニングは、XXEを特定し、軽減するために不可欠です。加えて、XXEを防ぐには以下のことが不可欠です:
• 可能な限り、JSONなどの複雑さの低いデータ形式を使用し、機微なデータのシリアライズを避ける。
• アプリケーションまたは基盤となるオペレーティングシステムで使用されているすべてのXMLプロセッサおよびライブラリにパッチをあてるか、アップグレードする。依存関係チェッカーを使用する。そして、SOAPはSOAP 1.2かそれ以降のものに更新する。
• OWASP Cheat Sheet ‘XXE Prevention’に従い、アプリケーション内のすべてのXMLパーサでXML外部エンティティとDTD処理を無効にする
ホワイトリスト方式によるサーバサイドの入力検証や、XMLドキュメント、ヘッダ、ノード内の悪意のあるデータのフィルタリング、またはサニタイズを実装する。
• XMLまたはXSLファイルのアップロード機能において、XSD検証などを使用して受信するXMLを検証していることを確認する。
• SASTツールはソースコード内のXXEを検出するのに役立つが、多くのインテグレーションを伴う大規模で複雑なアプリケーションでは、手動によるコードレビューが最善の選択肢である。
もしこうしたコントロールができない場合には、仮想パッチ、APIセキュリティゲートウェイ、あるいはWebアプリケーションファイアウォール(WAF)を使用して、XXE攻撃を検出、監視、およびブロックすることを検討してください。

XXEとはXML eXternal Entityの略です。XML文書はDTD(文書型定義)を外部ファイル(URI)として指定できます。XXEはDTDのURIが指定できる仕様を悪用し、本来アクセスできないハズの内部ネットワーク内のシステムを攻撃します。

基本的にはXML文書を処理する際に、DTDを処理させないことにより攻撃を防止できます。防止するだけでは不十分です。DTDのURIが不正でないかバリデーションし、問題がある場合は適切に処置(記録&レポート)する必要があります。

A5 アクセス制御の不備

防止方法


攻撃者がアクセス制御のチェックやメタデータを変更することができず、信頼できるサーバサイドのコードまたはサーバレスAPIで実施される場合にのみ、アクセス制御は機能します。
• 公開リソースへのアクセスを除いて、アクセスを原則として拒否する
• アクセス制御メカニズムをいったん実装すると、アプリケーション全体でそれを再利用する。CORSの使用などは最小限にすること。
• アクセス制御モデルは、ユーザがどのようなレコードでも作成、読取、更新、または削除できるようにするのではなく、レコードの所有権があることを前提としなければならない。
• アプリケーション独自のビジネス上の制約要求はドメインモデルに表現される必要がある。
• Webサーバのディレクトリリスティングを無効にし、ファイルのメタデータ(.gitなど)とバックアップファイルがウェブルートに存在しないことを確認する。
• アクセス制御の失敗をログに記録し、必要に応じて管理者に警告する(繰返して失敗しているなど)。
• レート制限するAPIとコントローラは自動攻撃ツールによる被害を最小限に抑えるための手段である。
• JWTトークンはログアウト後にはサーバー上で無効とされるべきである。
• 開発者とQAスタッフは、アクセス制御に関する機能面での単体及び結合テストを取り入れるべきである。

A6 不適切なセキュリティ設定

防止方法


安全にインストールするプロセスにおいて、以下のことを実施すべきです:
• 繰り返し強化するプロセスは、簡単にすぐ他の環境に展開され、正しくロックダウンすること。開発やQA、本番環境は完全に同じように設定し、それぞれの環境で別々の認証情報を使用すること。このプロセスを自動化し、新しい安全な環境をセットアップする際には、手間を最小限にすること。
• プラットフォームは最小限のものとし、必要のない機能やコンポーネント、ドキュメント、サンプルを除くこと。使用しない機能とフレームワークは、削除もしくはインストールしないこと
• レビューを実施して、セキュリティ関連の記録と更新の全てに加え、パッチを管理するプロセスの一環としてパッチの設定を適切に更新すること(A9:2017-既知の脆弱性のあるコンポーネントの使用 を参照)。クラウドストレージのパーミッションは、詳細にレビューすること (例えば、S3 バケットのパーミッション)。
• セグメント化したアプリケーションアーキテクチャは、セグメンテーションやコンテナリゼーション、クラウドのセキュリティグループ(ACL)をともなったコンポーネントやテナント間に、効果的で安全な仕切りをもたらす。
• セキュリティディレクティブをクライアントへ送ること。例えば セキュリティヘッダー
• プロセスを自動化して設定の有効性を検証し、環境すべてに適用すること。

「セグメント化したアプリケーションアーキテクチャ」とはリバースプロキシ、Webアプリサーバー、データベースを別コンテナにして分離したアーキテクチャなどを指しています。分離と保護(境界防御)はセキュリティ対策の基本です。

A7 クロスサイトスクリプティング (XSS)

防止方法


XSSを防止するには、信頼できないデータを動的なブラウザコンテンツから区別する必要があります。以下を実施します:
• 最新のRuby on RailsやReact JSなど、XSSに悪用されうるデータを自動的にエスケープするよう設計されたフレームワークを使用する。各フレームワークにおけるXSS対策の限界を確認し、対策の範囲外となるデータ使用については、適切な処理を行う
• ボディ、属性、JavaScript、CSSやURLなどHTML出力のコンテキストに基づいて、信頼出来ないHTTPリクエストデータをエスケープすることで、リフレクテッドおよびストアドXSS脆弱性を解消できる。
要求されるデータの詳細なエスケープ手法は OWASP Cheat Sheet ‘XSS Prevention‘ を参照のこと。
• クライアント側でのブラウザドキュメント改変時に、コンテキスト依存のエンコーディングを適用することで、DOMベースXSSへの対策となる。これが行えない場合には、 OWASP Cheat Sheet ‘DOM based XSS Prevention‘で説明されている、同様のコンテキスト依存のエスケープ手法をブラウザAPIに適用することもできる。
• XSSに対する多層防御措置の一環としてContent Security Policy (CSP)を有効に設定する。これは、ローカルファイルインクルードを介して悪意のあるコードを設置可能にする他の脆弱性(例:パストラバーサルを悪用したファイルの上書き、許可されたコンテンツ配信ネットワークから提供

フレームワークやAPIに任せておけばよい、では安全になりません。フレームワークやAPIは盲目的に信頼するのではなく、どう使えば安全か、どの部分にセキュリティ対策が行われていないか、などを検証してから安全に使う必要があります。

XSSもインジェクションの一種です。A1 インジェクションで記載されている対策が基本対策で、入力バリデーションの実施も必須事項です。妥当でないデータ(≒攻撃用のデータ)を許容するのはリスクを増やす最大の原因です。

A8 安全でないデシリアライゼーション

防止方法


安全なアーキテクチャを実現するには、シリアライズされたオブジェクトを信頼できないデータ供給元から受け入れないか、もしくはシリアライズ対象のデータをプリミティブなデータ型のみにします。上記の対策を取れない場合、以下の防止方法から一つ以上を検討してください:
• 悪意のあるオブジェクトの生成やデータの改ざんを防ぐために、シリアライズされたオブジェクトにデジタル署名などの整合性チェックを実装する
• コードは定義可能なクラスに基づくため、オブジェクトを生成する前に、デシリアライゼーションにおいて厳密な型制約を強制する。
ただし、この手法を回避する方法は実証済みなので、この手法頼みにすることはお勧め出来ない。
• 可能であればデシリアライズに関するコードは分離して、低い権限の環境下で実行する。
• 型の不整合やデシリアライズ時に生じた例外など、デシリアライゼーションで発生した失敗や例外はログに記録する。
• デシリアライズするコンテナやサーバからの、送受信に関するネットワーク接続は、制限もしくはモニタリングする。
• 特定のユーザが絶えずデシリアライズしていないか、デシリアライゼーションをモニタリングし、警告する。

A9 既知の脆弱性のあるコンポーネントの使用

防止方法


以下に示すパッチ管理プロセスが必要です:
未使用の依存関係、不要な機能、コンポーネント、ファイルや文書を取り除く
• Versions Maven Plugin, OWASP Dependency Check, Retire.jsなどのツールを使用して、クライアントおよびサーバの両方のコンポーネント(フレームワークやライブラリなど)とその依存関係の棚卸しを継続的に行う。
• コンポーネントの脆弱性についてCVEやNVD などの情報ソースを継続的にモニタリングする。ソフトウェア構成分析ツールを使用してプロセスを自動化する。使用しているコンポーネントに関するセキュリティ脆弱性の電子メールアラートに登録する。
• 安全なリンクを介し、公式ソースからのみコンポーネントを取得する。変更された悪意あるコンポーネントを取得する可能性を減らすため、署名付きのパッケージを選ぶようにする。
• メンテナンスされていない、もしくはセキュリティパッチが作られていない古いバージョンのライブラリとコンポーネントを監視する。
パッチ適用が不可能な場合は、発見された問題を監視、検知または保護するために、仮想パッチの適用を検討する。
いかなる組織も、アプリケーションまたはポートフォリオの存続期間は、モニタリングとトリアージを行い更新または設定変更を行う継続的な計画があることを確認する必要があります。

A10 不十分なロギングとモニタリング

防止方法


アプリケーションによって保存または処理されるデータのリスクに応じて対応する: 
• ログイン、アクセス制御の失敗、サーバサイドの入力検証の失敗を全てログとして記録するようにする。ログは、不審なアカウントや悪意のあるアカウントを特定するために十分なユーザコンテキストを持ち、後日、フォレンジック分析を行うのに十分な期間分保持するようにする。
• 統合ログ管理ソリューションで簡単に使用できる形式でログが生成されていることを確認する。
価値の高いトランザクションにおいて、監査証跡が取得されていること。その際、追記型データベースのテーブルなどのような、完全性を保つコントロールを用いて、改ざんや削除を防止する。
• 疑わしい活動がタイムリーに検知されて対応されるように、効果的なモニタリングとアラートを確立する
• NIST 800-61 rev 2(またはそれ以降)のような、インシデント対応および復旧計画を策定または採用する。
OWASP AppSensor、OWASP ModSecurity Core Rule Setを使用したModSecurityなどのWebアプリケーションファイアウォール、カスタムダッシュボードとアラートを使用したログ相関分析ソフトウェアなど、商用およびオープンソースのアプリケーション保護フレームワークがあります。

A10対策を実施するには入力バリデーション(CWE-20対策)が絶対に欠かせません。

サーバサイドの入力検証の失敗を全てログとして記録するようにする

この一文から明らかに理解るように、入力バリデーションの実施(CWE-20対策)が大前提です。

投稿者: yohgaki