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

セキュアコーディング/セキュアプログラミングはコンピューター動作の基礎的原理から構築されています。初めてプログラムが書かれた時から現在に至るまで、全てのプログラムは同じ基本構造を持っています。

基本原理/基本構造に合わないセキュリティ対策/構造では満足できるセキュリティ状態の達成は不可能です。残念ながら大半のWebアプリが原理に反する脆弱な構造を持っています。

IPAが出鱈目なセキュアプログラミングを啓蒙していた責任は大きいと言わざるを得ないでしょう。昨年、修正しましたが誤りを訂正すべく十分な啓蒙を行っているとは言えないように見えます。

はじめに

セキュアコーディング原則よりも更に基礎的な2つの原則があります。

  • ゼロトラスト – 一切信頼しない、安全性が検証されたモノ以外。
  • フェイルファースト – 失敗するモノはできる限り早く失敗させる。

この2つのプログラミングの基礎概念を理解していないと、セキュアコーディングの考え方を誤解してしまう可能性があります。確実に押さえておきましょう。

コードの基本「構造」

全てのプログラムは、アプリケーション/モジュール/関数・メソッドの粒度に関わらず

  • 入力 → 処理 → 出力

の構造を持っています。

セキュアなコードの基本「原理」

プログラムの心臓部と言える箇所は”処理”(ロジック)ですが、完全に正しい”処理”であっても”処理”が正しく動作する為には、”処理”が期待する”妥当な(≒正しい)入力データ”であることが絶対の必須条件です。

強いデータ型を持つ言語の方がセキュアであると言われる理由は、プログラムが正しく動作するために妥当な(正しい)データが必須であり、強いデータ型を持つ言語は少なくともデータ型が一致していることを言語が保証することが可能だからです。

”処理”後のデータを出力するする場合、出力先に対して無害であることを保証しなければなりません。例えば、SQL文をデータベースに出力する場合は、出力を行うプログラムの責任として、そのSQL文が完全に無害であることを保証することが出力データが正しく動作する為の絶対の必須条件です。

※ 正しく動作=誤作動なく動作=セキュリティ問題なく動作

よく誤解されていますが、セキュリティ対策は基本的には”呼び出すAPI”に任せる物ではありません。それが可能となるのは信頼できると保証できる場合のみです。これもよく誤解されていますが、”信頼できる範囲は限定されている”点です。APIには何を渡しても適当に処理する責任はありません。

正しいプログラム動作を保証する為に入力と出力を信頼境界線上での境界防御で守ります。

参考: エンジニアなら分かる文字エンコーディングバリデーションの必要性

セキュアなコードの基本「原則」

基本原則はセキュアコーディングプラクティスTOP10の原則です。境界防御となる原則は特に大切です。

  • 入力バリデーション
  • 出力の無害化

これらを漏れ無く実施します。”漏れ無く”を勘違いしているケースは少くありません。例えば、SQLクエリではプリペアードクエリだけを使っていればよい、では穴だらけです。

「入力バリデーションはセキュリティ対策ではない」といったプログラムの原理に合わない主張も耳にしますが、これは大きな誤りです。ホワイトリスト型の入力バリデーションにより多数のリスクを廃除/緩和できるのはもちろん、そもそもプログラムが正しく動作する為に絶対条件として必要な処理です。

Webアプリでは入力処理が脆弱すぎるアプリケーションばかりの未だに続いている状態です。入力処理が脆弱な場合、安全性の保証が困難になります。

セキュアなアプリケーションの基本構造

セキュアなアプリケーションでは境界防御を多層防御で守ります。

通常は全てのモジュール/関数・メソッドで完全な境界防御は行いません。完全な防御保証の責任はアプリケーションレベルの境界防御にあります。この為、アプリケーションレベルの境界防御が最も重要※です。

※ アプリケーション外との入出力の境界防御が重要。APIであっても複雑な物、正規表現やXML処理なども外部の物と同様に扱う。

今のプログラムに足りないモノ

プログラムの基本構造、動作原理、それから導き出される原則が今のプログラムに実装されていません。なぜ構造と原理に必要なモノが実装されていないのでしょう?

最も大きな理由は「プログラム機能の抽象化」にあると思います。「セキュリティ対策もプログラム機能の中に抽象化できる」と勘違いしてしまったことが「プログラムの基本構造、動作原理、それから導き出される原則」を無視してしまった原因に思えます。

セキュリティ対策を「プログラム機能の抽象化」に抽象化することも必要です。しかし、その前に「プログラムの基本構造、動作原理、それから導き出される原則」が実装されていることがプログラムが正しく動作する為に必要な前提条件になります。

  1. まず「プログラムの基本構造、動作原理、それから導き出される原則」をセキュリティ対策として実装
  2. 次に、多層防御として「プログラム機能の抽象化」にセキュリティ機能を実装

この順序で考え設計しないと体系化したセキュリティ対策は困難です。

フェイルファースト原則の未適用と出力対策の役割の誤解も大きな原因です。アプリケーションはフェイルファースト原則に従ったコードであるべき、つまりセキュアコーディングの第一原則の入力バリデーションを先ず最初に実施すべきです。最初に検証しないとごく単純なプログラム以外、必要なセキュリティ管理が不可能になります。

しかし、現存するアプリケーションの多くが出力対策に偏った対策になっています。出力対策の半分はフェイルセーフ対策であることの認識不足が原因だと思われます。

まとめ

この基本構造と必要な対策はコンピューターサイエンスでは当たり前の構造とされており、プログラム(アルゴリズム)実行の正しさを証明を習う際の前提条件になっています。

  • セキュリティ問題は基本的にはプログラムの誤作動/想定外の動作

誤作動/想定外の動作を可能な限り防ぐには、可能な限り正しく動作することを保証するホワイトリスト型の対策で行います。脆弱性が発現した場所を一つ一つ修正する、といったブラックリスト型では脆弱な対策になります。これを実現するには、何を置いても先ず入力データを厳格にバリデーションする必要があります。

厳格な入力バリデーションはISO 27000 (ISO 17799)では2000年から要求されており、CWE/SANS/OWASPといったセキュリティ専門機関でも2000年代初めから最重要のセキュリティ対策としています。

原理/原則に反する構造のアプリケーションでは、脆弱性が原因で訴訟やGDPR違反となった場合に非常に不利になると考えられます。

このような脆弱な構造になった大きな原因は

  • プログラムの”機能構造”の中にセキュリティ対策を実装

しようとしたことにあると思います。セキュアコーディング/セキュアプログラミングの基本構造は

  • プログラムの”基本構造”の中にセキュリティ対策を実装

しなければならない事が理解されなかったと思われます。攻撃が可能になった”機能”に対策するのではなく、データのセキュリティ対策はプログラムの”基本構造”に合わせ、フェイルファースト原則※に従い実装する必要があります。

コンピューターサイエンティストはこの点で勘違いをしていなかったので、一貫してセキュアな構造を提唱していました。しかし、残念ながらセキュアでない開発習慣や一部の”セキュリティ専門家”によって脆弱な構造のセキュリティ対策が提唱されたため、基礎的な部分での齟齬が現在でも解消されていません。

2017年版OWASP TOP 10ではデータバリデーションするだけでなく、記録/対応しないアプリケーションは脆弱なアプリケーション(A10脆弱性)としています。ほとんどのWebアプリがA10脆弱性を持っています。

※ フェイルファースト原則: 失敗するモノはできる限り早く失敗させる原則。リスク管理の基礎的概念の1つです。システム開発でテストより実装、実装より設計、設計より要求仕様と早い段階でダメなモノを廃除することもフェイルファースト原則です。

参考: セキュアコーディングの原則/ガイドライン。以下の基本概念はISO 27000(ISMS)、NIST SP800-171、PCI DSSなど業界標準規格で必要となります。

投稿者: yohgaki