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

「セキュリティの」と付けていますが、どの分野でも共通することだと思います。

何事でも論理的に何かのガイドライン/ルールを作る場合、まず変えることのできない

  • 原理 – 事物・事象が依拠する根本法則

を見つけ、その原理から導き出される

  • 原則 – 多くの場合に共通に適用される基本的なきまり・法則

を作り、さらに特定の条件下の具体的な事例として

を作ります。原理、原則、ベストプラクティスを理解していないと様々な問題がおきます。これらの基本的関係は

原理 > 原則 > ベストプラクティス

です。しかし、適用範囲の広さは

原理 < 原則 < ベストプラクティス

です。例えば、ベストプラクティスは”例外的”に原則に反した方法が”最善の方法”となる場合も少なくないからです。よく混乱や誤解の元になりますが、条件を絞った場合(例外的なケース)のベストプラクティスも在ります。

例外的なケース(一般的で無いモノ=例外的な物)をベストプラクティスにすることには異論もありますが、結構狭い条件でしかベストプラクティスにならないモノが”ベストプラクティス”と呼ばれていることは多いです。1

ベストプラクティスは信用できない

ベストプラクティスは「最善の方法」だから常にベストプラクティスを使ってさえいれば「最善」のモノが作れる訳ではありません。なぜ最善のモノがベストプラクティスから作ることができないのか?理由は難しくありません。

  • 原則自体が「多くの場合に共通に適用される基本的なきまり・法則」であり「多くの場合」に適用できるモノ。つまり、全ての場合に適用できるモノではない
  • ベストプラクティスは「最善の方法」だが、それは必ずしも原則に則ったモノである必要はない。”特定の条件”で例外的にベストプラクティスになるモノもある。
  • ”特定の条件”が限定的すぎて、一般化すると簡単にアンチプラクティスなるモノも多い
  • ベストプラクティスを適用しようとする対象が特殊な場合、一般的なベストプラクティスがベストプラクティスにならない
  • 本当の意味で最適化しようとする場合、別のベストプラクティスと一緒に実施しなければならないモノも多い
  • 最善のモノを寄せ集めても全体最適化ができていない場合、合成の誤謬、により全体として最善ではなくなる

このような現実があるため、ベストプラクティスを無条件に信頼すると無駄や無理が発生します。

最も分かりやすい例は開発環境のセキュリティです。

  • 開発環境のソフトウェア/ハードウェア/ネットワークには、一般に必要とされるセキュリティのベストプラクティスは必要ない/通用しない

「セキュリティのベストプラクティスは必要ない?!通用しない?!」と思うかも知れませんが、本当に必要も無いし、通用もしません。例えば、セキュリティのベストプラクティスには

  • OS/ライブラリ/アプリケーションなどは、脆弱性の修正されたバージョンのソフトウェアを利用する
  • セキュアなソフトウェアには、検証され、信頼できるソフトウェアのみを用いて構築する

があります。これが開発環境には通用しないことは、開発者であれば一目で解ると思います。

  • 脆弱性が修正されたバージョンのソフトウェア(OS/ライブラリ/アプリケーションなど)を使わない。納品先環境のテスト用などのために敢えて古いモノを使う
  • 作りかけ(開発版/α版/β版のソフトウェアなど)が普通に利用される

これが現実でしょう。

  • ベストプラクティスは「特定の条件下」でのベストプラクティス

であり、常に最善ではありません特定の条件下では、原則を曲げたベストプラクティスが最善であることも少なくありません

ベストプラクティスを盲信すると、開発環境に一般IT環境のセキュリティ対策を持ち込み、不必要に使いづらい環境を作ったり、リスクが高い開発環境と一般IT環境を分離しない、といったアンチプラクティスに陥ったりすることがあります。

条件さえ合っていれば、ベストプラクティスは最善の方法、です。無駄・無理なベストプラクティス破りを推奨しているのではないことに注意してください。

反対に条件に合わないベストプラクティスを最善の方法とするとアンチプラクティスになります。例えば、”プリペアードクエリ/プレイスホルダをだけを使う”は

  • SQL文に組み込まなければならない変数がサブコンテクストがない”SQLパラメーター”の時だけ

はベストプラクティスです。”変数がサブコンテクスト2がない”SQLパラメーター”の時だけ”のベストプラクティスを”識別子やSQL語句、パラメーターにサブコンテクスト”がある場合にも適用すると、致命的と言えるバッドプラクティスになります。

参考:完全なSQLインジェクション対策出力対策”のみ”のセキュリティはアンチプラクティス

原則も信頼できない

ベストプラクティスは常にベストではない、と解ったところで原則についても考えみます。

原則は「多くの場合に共通に適用される基本的なきまり・法則」です。「多くの場合に共通」とあるように、原則はそもそも全ての場合を想定していません。例外も存在します。3

原則に従っていれば、最善の方法/結果にたどり着ける、と考えるのも間違いの元です。

例えば、セキュアコーディング原則の第一は「入力をバリデーションする」ですが、開発初期の段階から入力バリデーションは必要ありません。必要ないというより無駄です。入力バリデーションはアプリケーション開発が落ち着き、仕様が固まってから実装しても、何も問題く、その法が無駄が少なく効率が良いです。

そもそも原則第一の入力バリデーションが必要ないケースは沢山あります。

  • 権限を持ったモノが実行することが前提の場合、入力バリデーションは省略でき、省略しないと無駄になる場合さえある

例えば、開発ツールで基本的に何でもできるデバッガーに2GB以上にデータを送った場合にバッファーオーバーフローが起きても対した問題でもありません。

原則は基本として守るモノですが、常に守るモノではありません。とは言っても大抵の場合は原則を守った方が良いことを忘れてはなりません。

参考:セキュアコーディングの原則出力対策の三原則

原理さえ信頼できない

原理はあまり変わるモノではありませんが、変わらないモノでもありません。

ここで紹介したように、SQLインジェクション対策にエスケープが必要ないAPIだけを使う、は一般的に適用するには原理的にセキュアでないアンチプラクティスです。

プリペアードクエリは原理的に識別子とSQL語句、SQLパラメーターサブコンテクストの無害化ができないので「APIだけ使えばOK!」にはなりません。

しかし、プリペアードクエリで識別子の無害化ができるになれば(+他に必要なセキュリティ対策もサポートすれば)、原理が変わり「APIだけ使えばOK!」と言っても大丈夫になります。4

参考:原理に反する入り口ノーガード設計原理的に入力値を分類すると3種類しかない

まとめ

  • 原理は事実・仕組みから”一般的”なルールを導き出す
    これは基本的には変わることがない。稀に前提条件が変わり原理が変わる事もある。
  • 原則は原理から”一般的”なルールを導き出す
    一般的でないモノは例外に過ぎず、原則ではない。前提条件を正確に把握しないと間違えることも。
  • ベストプラクティスは原理・原則から”一般的”なルールを導き出す
    通常ベストプラクティスには前提条件がある。条件を満たさないのに”ベストプラクティス”を実行すると”バッドプラクティス”になる。

ベストプラクティスや原則は、基本採用すべきモノですが、信用ならないモノです。何も考えないで従うモノではありません。原則でさえ信用なりません。原則の前提となる条件が変わると原則さえ変わります。ベストプラクティスと言われているモノが実はアンチプラクティス、という場合も少なくありません。

セキュアなソフトウェアを作る場合に最も重要な基本的概念は「ゼロトラスト」だと思います。何も信用せず、作っているモノに最適な方法を採用する、ことが必要です。

普段、ベストプラクティスや原則に従って作るべき、という話をよくしています。しかし、それらは「常に正解」ではない、と知っておかないと、とんでもない無理や無駄をすることがあります!

昔から気になっていることは

条件を満たさないのに”ベストプラクティス”を実行すると”バッドプラクティス”になる

です。これはSQLインジェクション対策でよく見られる問題です。「SQLインジェクション対策にはプリペアードクエリ/プレイスホルダだけ使っていればOK!」は「SQL文の変数が”パラメーターだけ”の時」にだけ適用できるベストプラクティスです。5

「SQL文の変数が”パラメーターだけ”の時」ではない場合、「SQLインジェクション対策にはプリペアードクエリ/プレイスホルダだけ使っていればOK!」はSQLインジェクション脆弱性を容易に作ってしまうバッドプラクティスです。6

最後に、ベストプラクティスや原則は不必要に破るモノではないです!破った場合に「全体として最善・最適な状態」になる場合にのみ破るモノです。


  1. ベストプラクティスは一般的でるのが理想です。しかし、例外のベストプラクティスも必要です。もしあまり一般的でないものや例外をベストプラクティスとする場合、ベストプラクティスになる為の条件を明確にすべきです。条件が明確で無い場合、”ベストプラクティス”がアンチプラクティスになってしまいます良い例(というより悪い例)はSQLインジェクション対策にはプリペアードクエリ/プレイスホルダを使う出力対策だけでセキュリティ対策は可能、です。 
  2. SQLパラメーターにはサブコンテクストがあります。パラメーターの内容自体が意味がある場合、はその中に別のコンテクストがあります。例えば、今のRDBMSのLIKEクエリ、正規表現、XML、XPathクエリ、JSONらがサブコンテクストとして一般的です。これらのサブコンテクストに合った方法で無害化しておかないとインジェクション攻撃に脆弱になります。 
  3. ベストプラクティスとは異り、限定的な条件を設定した例外を”原則”とするのは止めた方が良いでしょう。とんでもない混乱の元になります。”例外”はあくまで例外として扱う方が解りやすいです。 
  4. APIが識別子、SQL語句の無害化をサポートしても、サブコンテクスト(LIKE、正規表現、XML、XPath、JSONなど)の無害化は原理的に不可能なので、これらがある限り「SQLの無害化はこのAPIだけ使えばOK!:という状態にはなりませんが、最低限のSQLクエリセキュリティ対策として不正なSQL実行だけは防止できます。 
  5. 一般にはバッドプラクティスと呼ぶ方が良いでしょう。識別子によるインジェクション脆弱性は多過ぎます。ベストプラクティスと言えないことも無いですが、条件が限定的すぎて(ソート/抽出カラムを指定するクエリは一般的)一般的なベストプラクティスとするには無理があり、実際に勘違いした開発者によりSQLインジェクション脆弱性が作られている現実があります。 
  6. 簡単にバッドプラクティスになる、広く一般化できない”ベストプラクティス”は、ベストプラクティスと呼ばない方が良いように思います。原則と同じく、広く一般化できるモノだけをベストプラクティスとし、例外的/限定的なモノはある条件下の”作法”程度の扱いの方が良いでしょう。例外的/限定的なベストプラクティスの誤用/誤解はとても多いように感じます。 

投稿者: yohgaki