当たり前?非常識?開発者必修のセキュリティ概念 Top 10

(Last Updated On: 2018年8月16日)

ITシステム開発者必修のセキュリティ概念 Top 10です。さっと考えたので変更するかも知れません。これらの考え方や概念を理解していないと、ITセキュリティ標準やガイドラインなどで要求されているセキュアプログラミング/セキュアコーディングを効率的かつ効果的に利用することはできないでしょう。

ここで紹介する概念はセキュリティ設計やセキュアコーディングを行う上で欠かせないモノばかりです。

順序はその基礎知識性、重要性、分かり易さで付けています。では開発者必修のセキュリティ概念 Top 10です。

参考:思いの外、長くなったのでショート版を作りました。

1. ゼロトラスト

ゼロトラスト(Zero Trust)とは何も信頼しないことです。

しかし、実際には何らかの信頼がなければ効率的にシステム/ソフトウェアを作ることはできません。信頼はそこにあるモノではなく、検証/保証するモノです。

言い換えると、検証/保証したモノ以外は信頼しない、です。

現実のシステム開発では十分に検証できないモノも使わざるを得ません。この場合、”リスクを許容”することになります。許容したリスクは放置するのではなく、定期的にリスクを評価し、リスクを削減/廃除可能である場合は削減/廃除します。

例えば、ソフトウェア開発の場合、自分のコードは勿論、フレームワークやライブラリに致るまで全て信頼しない状態から始めます。自分のコードはソースコード検査やレビュー、フレームワークやライブラリの機能は仕様書の確認とテストを行い、信頼できるものであることを検証/保証していきます。完全に検証/保証するのは困難です。この場合は”リスクを許容”し管理することになります。

仕様内で正しく動作することを確認するのは比較的簡単です。問題は”仕様外”、つまり不正な入力など、で正しく動作することの確認はかなり困難です。このため、後述する”ホワイトリスト”型の”入力バリデーション”で不正な入力をコードに与えないことが重要になります。

ゼロトラスト1とは私の造語です。「何も信頼しない」「信頼するには検証が必要」という概念はセキュリティの基本ですが端的な用語がないので適当に考えました。用語の意味は文字通りなので解りやすいと思います。

ポイント:

ゼロトラストの概念がないと、セキュアコーディングもセキュリティ設計も行なえません

 

2. 信頼境界線

信頼境界線(Trust Boundary)はITセキュリティに限らず、セキュリティの基本中の基本の概念です。どのようなセキュリティであっても、先ず信頼境界で可能な限りのセキュリティ対策を行います。”ゼロトラスト”で信頼できる範囲を広げても、様々な制約から必ず「ここが限界」というラインができます。

信頼境界線を書く場合、どのコンテクストに対する信頼境界なのか、区別しないと意味がありません。少なくとも次の4つのコンテクストは区別する必要があります。

  • 物理 – 場所やハードウェア、人
  • ネットワーク – 通信
  • ソフトウェア – プログラム
  • システム – 上記3つのコンテクストをまとめて”システム”単位で記述したもの。

初期状態は「ゼロトラスト」です。それぞれの信頼境界は開発者が検証/保証し作るモノです。この信頼境界を「検証/保証して作る」プロセスに問題があったり、場合によって全く無かったりして脆弱になるケースはよくあります。

ここではソフトウェアの信頼境界線の考え方の中で重要なルールと制限事項を紹介します。

  • 検証無しでは全てのコードは信頼できない。
    • 少なくとも自分が書いたコードは信頼できるように記述/検証する。
    • 自分が書いていないコード(ライブラリ/フレームワーク)を信頼したい場合は信頼できることを検証する。
  • ソフトウェアの信頼境界は同一プロセスまたはスレッドを越えることはできない

1番目も大切ですが、2番目の制限が重要な制限です。例えば、Webシステムの場合、自分がクライアント(ブラウザ)のJavaScriptを記述していても、サーバー側のプログラムはこのJavaScriptプログラムからの入力を一切信頼できません。

システムコンテクストの信頼境界線では「信頼している」と記述されていても、ソフトウェアコンテクストの信頼境界線の基本は「同一プロセスまたはスレッドを越えることはできない」です。セキュリティ専門家でも間違えている場合があるので、注意しましょう。

ポイント:

信頼境界線の概念がないと、セキュアコーディングもセキュリティ設計も行なえません

 

3. ホワイトリスト優先

セキュリティ対策において、何かを検証する時は必ずホワイトリスト型の検証を優先します。

  • ホワイトリスト ー 良いモノを定義し、良いモノだけを受け入れる
  • ブラックリスト ー 悪いモノを定義し、悪いモノを除いて受け入れる

「良いモノ」を定義し、「良いモノ」だけを受け入れても間違える時は間違えます。「悪いモノ」を定義し、「悪いモノを除く」場合、「全ての悪いモノを漏れ無く定義」することが必要です。

大抵の場合、「全ての悪いモノを漏れ無く定義」することは「良いモノを定義」するより困難です。「悪いモノを定義」しても全てではない場合がよくあります。つまり間違いを犯しやすいです。このためセキュリティ対策ではホワイトリスト型で検証するのがデフォルトです。ホワイトリスト優先はこれから紹介する入力バリデーションで重要です。

ホワイトリスト型の対策は、厳格なホワイトリストであれば、信頼できるモノであることが多いです。反対にブラックリスト型の対策は大抵の場合は信頼できない”補助的な対策”に過ぎないことが多いです。このため、ITセキュリティ対策ではホワイトリストを基本とし、ブラックリストは例外、使用した場合も”補助対策”として扱うべきです。

ポイント:

ホワイトリスト優先の概念がないと、まともなセキュアコーディングもセキュリティ設計も行なえません。

 

4. 入力バリデーション

入力バリデーションとは入力が正しく妥当であることを検証することです。セキュリティ対策で最も重要な入力バリデーションは信頼境界で行います。ロジック、MVCモデルいうモデル、で行う「入力バリデーション」と信頼境界で行う「入力バリデーション」は役割が異る(後述)を理解している必要があります。

仮に完璧に動作するコンピューターとソフトウェアであっても、受け入れ可能な正しい入力以外を与えると誤作動します。プログラム(コード)が受け入れ可能な正しい入力のみ処理(入力バリデーション)すると、セキュリティ維持が飛躍的に容易になります。

入力バリデーションを正しく行うには

  • ゼロトラスト
  • 信頼境界線
  • ホワイトリスト優先

の概念を理解している必要があります。

これらを理解していないと、入力バリデーションを行うべきモノと場所、方法を間違えてしまいます。セキュリティ対策用の入力バリデーションは「信頼境界」で行い、「最外周の信頼境界」が最も重要です。信頼境界の概念がないと正しく、セキュリティ対策用の入力バリデーションが行えなくなります。

これらの概念に加えて、以下の2つも重要です。

  • Fail Fast原則 – 失敗するモノはできる限り早く失敗させる
  • 単一責任原則 – 1つのモジュール/クラスは単一の責任を果す

Fail Fastはエラー処理で実践しているはずです。Fail Fast原則から「最外周の信頼境界」が最も重要になります。この時に「信頼境界線コンテクストの区別」が重要です。

例えば、ネットワークやシステムの信頼境界をソフトウェアの信頼境界と誤解すると、セキュリティが維持できません。ソフトウェアの信頼境界は同一プロセスまたはスレッドを越えることができないことを思い出してください。「信頼境界線コンテクストの区別」が出来ていないことが原因で問題となった例は数えきれません。注意してください。

単一責任原則はオブジェクト思考プログラミングの原則ですが、プログラムの基本構造である「入力 ⇒ 処理 ⇒ 出力」に当てはめることができます。それぞれ、機能に応じた責任を持った方が理解り易く、メンテナンスも行い易いモノになります。

Fail Fast原則と単一責任原則を当てはめると

  • 入力 – 入力処理の責任を果す。(入力値の形式的妥当性を検証 = 入力値の形式/範囲の検証)
  • 処理 – ロジック処理の責任を果す。(入力値の論理的妥当性の検証 = 入力値の論理的/仕様的検証)
  • 出力 – 出力処理の責任を果す。(「出力無害化」を行う。これは後述の「多層防御」で行う)

となります。同じ検証でも、入力処理とロジック処理で行う検証は異るモノです。

「契約プログラミング/契約による設計」(DbC – Design by Contract)はセキュアコーディングを更に突きつめたようなプログラミング技法です。数学的にセキュリティ維持と性能維持を保証するDbCでは、DbCサポート機能以外の入力バリデーションが極めて重要です。DbC用の検証コードは運用時に実行されません。適切な入力バリデーションがないとセキュリティ維持ができません。

ポイント:

正しい入力バリデーションの概念がないと、まともなセキュアコーディングもセキュリティ構築も行なえません

 

5. 出力無害化

外部システム(プログラム内に組み込まれているライブラリ、正規表現やXMLなど、も含む)に出力する場合、全ての出力を完全に無害化すると安全性の確保が飛躍的に容易になります。

出力無害化を確実に行うには次の概念が欠かせません。

  • ゼロトラスト
  • 信頼境界線
  • Escape Last
  • 多層防御
  • ホワイトリスト優先

出力無害化は入力バリデーションと同じくゼロトラストで行うと安全性が飛躍的に向上します。今時のHTMLテンプレートシステムは「変数が整数型、既に検証済みであろうと無条件にHTMLエスケープ」します。これが出力におけるゼロトラストです。これがWebアプリの安全性を飛躍的に高めていることをWebアプリケーション開発者であれば知っていると思います。

出力無害化は信頼境界を正しく理解していないと機能しません。例えば、外部システムには正規表現を安全に利用するには、メタ文字(正規表現で意味を持つ文字)のエスケープが欠かせません。正規表現ライブラリが”信頼境界の外”であることを理解していないと、危険な処理をしてしまいます。

Escape Last(最後にエスケープ)は、文字通りエスケープ処理を”出来る限り最後”に行うことです。HTML部品などを作る場合、出力処理のビューではなく、ロジックにあたるモデルでHTML部品を作ることがあります。この場合、”出来る限り最後”はHTML部品を作る関数内になります。エスケープ処理を”出来る限り最後”に持ってくるとコードの見通しが良くなり、重複エスケープ(これもセキュリティ問題の原因)も起こりづらくなります。

多層防御の概念で出力対策を行います。ゼロトラストと重複する部分がありますが、出力時の安全対策は独立した対策として処理します。 厳格なセキュアコーディングでは入力やロジック処理のチェックで「大丈夫なはずだから」という理由で出力無害化を省略しません

お勧めしませんが、出力の無害化を選択的に行う場合はホワイトリスト優先です。オススメは、HTMLテンプレートシステムのように、ゼロトラストで全ての出力を無害化する方法です。どうしても、という時にはホワイトリストで出力対策をします。(不用意にこれを行うとセキュアコーディングの概念に反することになります)

前置きが長くなりました。出力の無害化は簡単です。次の3つの何れか、又はその組み合わせで対策します。

  • エスケープ(エンコーディング – 無害なデータに変換)
  • 安全なAPI(エスケープが必要ないAPI – 有害な動作を不可能にするAPI)
  • バリデーション(無害なデータであることを検証)

これら全ての方法で、コンテクストが重要です。出力先のコンテクストを理解し、それぞれのコンテクスト用に無害化処理を行わないと無害化できません

出力先のコンテクストは”SQL”、”HTML”といった粗いレベルのコンテクストでは意味がありません。SQLには識別子、パラメーター、語句のコンテクストがあり、パラメーターには更にLIKEクエリ、正規表現、XML、JSONなどのコンテクストがあります。それぞれのコンテクストに対して適切な出力無害化を行わないと、インジェクション攻撃に脆弱になります。

「安全なAPI」を使っていれば安全とは限りません。APIを使う場合もコンテクストが重要です。例えば、プリペアードクエリは静的SQLのみ使っている時は安全ですが、それ以外(プリペア文に変数埋め込み=動的SQL)では安全ではありません。”コンテクスト”を意識していないと、思わぬ勘違いをしてしまいます。

ポイント:

出力を完全に無害化するとインジェクション攻撃は防止できます。無駄に思えても例外なく完全に無害化することにより、ついうっかり、知らなかった、などのリスクを最小化できます。

 

6. 多層防御

多層防御はネットワークを考えると解りやすいでしょう。インターネットと内部ネットワークの境界でファイアーウォールを置いて防御している場合でも、内部ネットワークのPCなどはそのデバイス上のファイアーウォールで保護されています。このように多層構造で防御するのが多層防御です。文字通りですね。

単一の防御より複数の防御の方がより安全になります。信頼境界線のコンテクストを理解すると、複数レイヤーでも防御が必須であることも分かります。例えば、信頼境界内のPCであっても、全てのPCの物理的信頼が保てない場合、それに対する防御が必要になります。

コード中でよくある習慣は「この変数は安全な変数だから、セキュリティ対策なし出力でもOK」です。人間は間違えるモノです。コードは変わっていくモノです。セキュリティが重要なら「この変数は安全な変数だから、セキュリティ対策なし出力でもOK」は無くして、「全ての変数を完全に無害化してから出力する」とするのがセキュリティ的にはオススメの多層防御です。

セキュアコーディングでは”出力対策は独立した対策”であることを求めています。セキュアコーディングを行う場合には、「全ての変数を完全に無害化してから出力する」が必要です。

適切な多層防御には異るコンテクストの信頼境界を理解する必要があります。1つのシステムとして信頼境界の中にあっても、それぞれ物理、ネットワーク、ソフトウェアの信頼境界があることが普通です。この場合、それぞれの信頼境界で適切な対策が必要になります。

ポイント:

信頼境界にコンテクストがあること理解していない場合、信頼境界の概念など無駄で役立たない、必要な多層防御を不必要だ、と誤った認識をしていしまいます。

 

7. 必要十分条件

これまで説明してきた概念の実装が、ソフトウェアやシステムとして十分であるのか?検証することが重要です。それには必要十分条件を満たしているのか?考える必要があります。

様々なケースで必要十分条件が満たされていることを確認しなければなりません。ここではソフトウェアが正しく動作するための条件を考えてみます。

あるプログラムが正しく動作することを保証するための必要条件と十分条件

  • プログラムが正しく動作する ⇒ 入力と出力が正しい(ロジックは正しいとする)

これは必要十分条件となります。なぜなら

  • 入力と出力が正しい(ロジックは正しいとする)⇒ プログラムが正しく動作する

が正しいからです。誤った仮定は以下です。

  • プログラムが正しく動作する ⇒ 出力が正しい(ロジックは正しいとする)

誤っている理由は

  • 出力が正しい(ロジックは正しいとする)⇒ プログラムが正しく動作する

は成り立たないからです。コンピュータは整数演算でさえ、限定された入力でしか正しく演算できません。(オーバー/アンダーフロー)SQLインジェクションできないとは言っても、英数字の商品コードに”<script>alert(‘XSS’)</script>”といった文字列が保存されるのは正しくないからです。

必要十分条件の確認には1つの反例があれば、成り立たないことを証明できます。ここで使っている「ロジックは正しいとする」などの条件が必要な場合が多くあります。事前に必要な条件がある場合、対策を行うか、リスクを受け入れる必要があります。

プログラムが正しく動作するには”入力バリデーション”と”出力無害化”(+正しいロジック)が欠かせません。ロジックの正しさを証明するのは非常に困難です。しかし、”入力バリデーション”と”出力無害化”の正しさ証明は容易です。

ポイント:

論理的に物事を考えて、論理的に誤りのない構造や仕組み、実装にするという概念が欠けているとセキュリティ設計レベルで誤りを起こしやすくなります。

 

8. ITセキュリティ対策の目的

ITセキュリティを考える場合、その目的は明確かつ論理的に正しいセキュリティを達成できる目的でなければなりません。そうでなければ混乱を生みます。

  • ITセキュリティ対策の目的: ITシステムを許容可能な範囲のリスクに抑えて利用する

ITシステムを利用するには「リスク許容」が欠かせません。車を利用することと同じです。車を利用するには「事故」のリスクを許容します。

リスクが大きすぎて許容できない場合は利用しない、となります。しかし、車と同じくITシステムを利用しない、という選択肢は現実的ではありません。

ポイント:

目的が明確でない場合、簡単に”手段”と”目的”の勘違いが生まれます。セキュリティ対策の目的はITシステムを利用することにあり、セキュリティ対策するという”手段”が目的になるのは誤りです。

 

9. ITセキュリティ対策の定義

ITセキュリティ対策は「リスクを変化させる全ての対策」です。これらを「リスク管理」します。

  • ITセキュリティ対策の定義:リスクを変化させるモノ全てに対応し、これらを管理すること

セキュリティ対策というと減る方向性のリスク対策ばかりに目が行きがちです。しかし、これは誤りです。リスクが増える方向性のリスク対策が必須です。ISO 27000のリスク対応の定義が参考になります。

例えば、2要素認証はリスクを減らすだけではありません。増えるリスクもあります。

  • 鍵を生成する為の元鍵の盗難
    • 鍵を生成するデバイスの紛失/盗難
    • 鍵を生成するデバイスの乗っ取り
    • 元鍵を保存しているサーバーからの漏洩
  • TOTPの場合、再生攻撃
  • ユーザーが安易なパスワード(第一の鍵)を設定

このように「何か」を導入したりすると、リスクが減るだけではなく、リスクが増えることが普通にあります。

「リスクを減らす対策」「リスクを根本的に廃除する対策」といったモノが「セキュリティ対策」と勘違いしているケースはよく見かけます。

標準セキュリティでは、増えたリスク(リスク許容)もセキュリティ対策の1つとして管理します。リスクを減らすモノだけがセキュリティ対策ではない、これを理解するだけでも大きな違いになります。

ポイント:

増減するリスクを管理するのがセキュリティ対策、と理解していないとマトモなセキュリティ管理の実現は困難です。

 

10. ISO 27000

ISO 27000はITセキュリティ対策の国際標準です。ISO 27000からは直接概念的な知識を得るのは難しいかも知れませんが、ITエンジニアであれば一度は目を通し、基本(特に概念)は理解しておきたいところです。特に用語の定義などは確実に押さえておきたい部分です。

ポイント:

ISO 27000はISMS認証の基盤です。国内だけでも5000以上の組織がISMS認証を取得している国際ITセキュリティ標準です。これに合致しないセキュリティ概念ではコミュニケーションが成り立ちません。

 

まとめ

このTop10にはセキュリティ専門家と長年議論してきたテーマも多く含まれています。セキュリティ専門家と言われている人であっても、これらの概念を理解できていないケースがあります。

これらはセキュリティ専門家としてのみでなく、エンジニアとしても欠かせない基礎概念で、セキュリティ設計や実装に欠かせないモノです。これらを理解し利用/適用するだけでITシステムの安全性が飛躍的に向上します。

最後の方は説明が足りないかも(途中のモノも誤解されることが多いので足りないかも)知れませんが、参考になれば幸いです。

 


  1. 同じようなことを考える人はいます。Zero Trust Architectureという仕組みを提唱している会社はあります。 

投稿者: yohgaki