はじめに
「セキュリティはリリース直前にチェックすればいい」
「脆弱性が見つかったらパッチを当てればいい」
── 自分も以前はそんな感覚で開発していました。
セキュリティ関連の作業はインフラチームやセキュリティ専門チームの仕事であって、
アプリケーション開発者である自分の関心事ではない、と。
ただ、ソフトウェアが家電・自動車・医療機器・金融インフラから個人の健康情報まで遍在する時代になると、そのスタンスでは追いつかない場面が増えてきます。
設計段階で埋め込まれた弱点は後から直すコストが大きく、
リリース後に発覚すれば顧客データの流出やサービス停止といった取り返しのつかない事態にもつながります。
この記事では、セキュリティを「後付けのチェック」から「開発プロセスに組み込むエンジニアリング活動」へと捉え直すための考え方と手法を整理してみます。
攻撃者の思考を知ることから始めて、各フェーズの具体策、ライフサイクル全体の統合、
そして組織としての成熟度向上、という流れで進めていきます。
TL;DR
- セキュリティはリリース前のチェックではなく、
ライフサイクル全体に組み込むエンジニアリング活動 - 開発者個人の出発点は、
攻撃者視点の手法(悪用ケース・STRIDE)とアタックサーフェスの最小化 - 組織レベルでは、ライフサイクルモデル(SDL・タッチポイント) と
成熟度モデル(BSIMM・SAMM) が継続的改善の枠組みになる
攻撃者の目で自分のソフトウェアを見る
「リリース前にツールでチェックする」だけでは、
攻撃者が狙う設計レベルの弱点を網羅的に洗い出すのは難しい。
これは多くの開発現場で実感されている話です。
その根っこにあるのは
「攻撃者がどこを狙うかを開発者が知らないこと」だと感じています。
だからまず、攻撃者がどのようにソフトウェアを見ているかを体験するところから始めます。
このセクションでは2つの手法を取り上げます。
- 悪用ケース: 個別の攻撃シナリオを深掘りする
- STRIDE: システム全体に対する脅威を網羅的に洗い出す
「攻撃者なら何をするか」を考える ── 悪用ケースと攻撃パターン
通常のユースケースは「ユーザーがシステムを使って目的を達成するシナリオ」です。
これに対して悪用ケース(ミスユースケース)はその逆で、
「攻撃者がシステムを使って悪意ある目的を達成するシナリオ」を指します。
悪用ケースを書く目的は、
ソフトウェアが攻撃に対してどう反応すべきかを事前に決めておくことにあります。
実装してから「こんな攻撃は想定していなかった」と慌てるのではなく、要件・設計の段階で「この攻撃が来たらこう振る舞う」を決めておく、というアプローチです。
作り方のポイントとして、セキュリティ専門家とドメインの専門家がチームを組み、
ブレインストーミング形式で進める方法が知られています。
すべてのユーザーインターフェースを精査し、「開発者が起こりえないと思い込んでいるが、攻撃者は実際に引き起こせること」を洗い出します。
このとき問うべき問いは、たとえばこういうものです。
- 正当な入力と不正な入力をどう区別するか
- リクエストの送信元が正規アプリか不正アプリかを判別できるか
- 内部者がシステムを誤動作させることはできるか
具体例として、Log4Shell(CVE-2021-44228、2021年に公開された脆弱性)の悪用ケースを再構成してみます。
当時広く使われていたOSSのログ出力ライブラリの脆弱性で、
全世界のサービスが影響を受けた事例です。
| 項目 | 内容 |
|---|---|
| 攻撃者のゴール | 標的サーバー上で任意のコードを実行する |
| 前提条件 | 標的アプリがユーザー入力(HTTPヘッダ、フォーム入力など)をログに出力している。ログ出力に脆弱性のあるバージョンのライブラリを利用している |
| 攻撃手順 | (1) 攻撃者は外部に悪意あるJavaクラスを配置したサーバーを用意する。 (2) 特殊な書式(JNDIと呼ばれるJavaの名前解決の仕組みを誘発する文字列)を入力としてアプリに送信する。 (3) アプリがその文字列をログ出力する際、ログライブラリが文字列を評価し、外部サーバーに問い合わせる。 (4) 外部サーバーから悪意あるクラスをロードし、サーバー上で実行する |
| 結果 | サーバー上での任意コード実行。データ流出、サーバー乗っ取り、内部ネットワークへの侵入の足がかりに使われる |
この事例の興味深いところは、自分のコードに直接の脆弱性がなくても、
依存しているOSSライブラリの脆弱性を経由して攻撃が成立してしまう点です。
サプライチェーンの問題と、入力をログに通すというどこにでもある経路が攻撃の入口になっている問題が、一度に絡んでいます。
悪用ケースを考えるときの補助になるのが「攻撃パターン」です。
攻撃パターンは攻撃の「定型的な手順書」のようなものです。
たとえばインジェクション攻撃やバッファオーバーフロー攻撃には、
共通するステップ(タイミング、リソース、技法)があります。
攻撃パターンのカタログを参照することで、
ゼロから悪用ケースを考えるよりも体系的に攻撃シナリオを洗い出しやすくなります。
すべての悪用ケースに対して緩和策を実装できるとは限りません。
コストと便益のバランスを考えて優先順位をつけ、段階的に実装するのが現実的です。
発生確率が極めて低いものは除外する判断もあり得ます。
悪用ケースは「ユースケースの裏返し」と捉えると書きやすいです。正常系のユースケース図やシーケンス図を見ながら、「ここで悪意ある入力を入れたら?」「この通信を傍受されたら?」「ここで認証を回避されたら?」と問いを立てていく作業になります。
脅威を体系的に洗い出す ── STRIDEによる脅威モデリング
悪用ケースは「個別の攻撃シナリオ」を考える手法でした。
これに対して脅威モデリングは、システム全体を構成要素に分解して、
すべての構成要素に対する脅威を網羅的に洗い出す手法です。
粒度の違いで使い分けるイメージです。
脅威モデリング手法(Threat Modeling Method、TMM)は、次のような流れで進めるアプローチとして整理されています。
システムの抽象モデルを作成し、攻撃者の能力と目標を特定し、
対処すべき脅威のカタログを生成する、という流れです。
代表的な手法であるSTRIDEは、6つの脅威カテゴリの頭文字を並べたものです。
それぞれが「破られると損なわれるセキュリティ特性」と一対一で対応しています。
| 脅威カテゴリ | 訳語 | 損なわれるセキュリティ特性 | 身近な例 |
|---|---|---|---|
| Spoofing | なりすまし | 認証 | 他人のセッションIDを使ってログイン状態を装う |
| Tampering | 改ざん | 完全性 | 通信経路上でリクエストのパラメータを書き換える |
| Repudiation | 否認 | 否認防止 | 重要操作のログが残らず「やっていない」と主張できてしまう |
| Information Disclosure | 情報漏洩 | 機密性 | エラーメッセージにDB接続文字列が露出する |
| Denial of Service | サービス拒否 | 可用性 | 大量リクエストでサーバーを応答不能にする |
| Elevation of Privilege | 権限昇格 | 認可 | 一般ユーザーが管理者APIを叩けてしまう |
STRIDEの実践は、ざっくり次のステップで進みます。
- データフロー図(DFD)でシステムをモデル化する
- DFDの各要素を脅威カテゴリにマッピングする
- チェックリストや脅威ツリーを使って、要素ごとに具体的な脅威を特定する
- 特定した脅威に対する緩和策を作り、コストと価値で優先順位をつける
ステップ1で出てくるDFD(データフロー図)は、
システムをデータの流れで表現する古典的なモデリング記法です。
脅威モデリングの文脈では「攻撃者がどこから入り込めるか」を考えるための地図として使います。
DFDは次の4要素で構成されます。
+-------------------+ +-------------------+
| 外部エンティティ | | データストア |
| (人・他システム) | | (DB、ファイル等) |
+-------------------+ +-------------------+
| ^
| データフロー (関数呼び出し、 |
v RPC、HTTP等) | 読み書き
+----------------------+ |
| プロセス |----------------+
| (アプリ・Webサービス等) |
+----------------------+
ステップ2で行うDFD要素と脅威カテゴリのマッピングは、要素の種類ごとに「晒されやすい脅威の種類」が決まっているという観察に基づいています。
たとえば外部エンティティ(人や外部システム)はなりすましと否認のリスクには晒されますが、改ざんや情報漏洩の対象にはなりません。
外部エンティティ自体は通信路ではなくアクター扱いだからです。
| 要素 | Spoofing | Tampering | Repudiation | Information Disclosure | DoS | Elevation of Privilege |
|---|---|---|---|---|---|---|
| データフロー | X | X | X | |||
| データストア | X | X1 | X | X | ||
| プロセス | X | X | X | X | X | X |
| 外部エンティティ | X | X |
プロセスは6カテゴリすべてに晒されるので、
脅威モデリングではプロセス要素が特に注意の対象になります。
一方、データフロー(通信経路)は改ざん・盗聴・遮断という3つの観点で見ればよい、
というように、要素の性質に応じて確認すべき脅威が絞れます。
データストアについては、保管対象が監査ログかどうかなど、要素の文脈によって Repudiation が論点になることがある点も意識しておきたいです。
STRIDEは手作業でも実践できますが、Microsoftが無償提供している脅威モデリング用のツールもあり、DFDを描くと脅威候補を自動列挙してくれます。
緩和策の優先順位付けで意識したいのは、
実装コストだけでなく「実装しないコスト」も考慮することです。
リスクが顕在化したときのコストは金銭だけではなく、
信頼の喪失や、業界によっては人命にまで及びます。
悪用ケースとSTRIDEの使い分けは、感覚的には「個別深掘り」と「網羅チェック」の違いです。重要シナリオは悪用ケースで深く描き、抜け漏れチェックはSTRIDEでDFDから網羅的に洗う、と組み合わせるのが現実的だと思います。
リスクを見積もり、攻撃される面を減らす
攻撃者の思考を体験したところで、
次に問うべきは「すべての脅威に等しく対処することは現実的か?」という点です。
答えはもちろんNoで、限られたリソースで最大の効果を得るには、リスクの優先順位付けと「そもそも攻撃される面を小さくする」という戦略の両方が必要になります。
リスク分析のフレームワーク(組織レベル)の鳥瞰
セキュリティリスク評価には、組織レベルで実施する大きな枠組みと、
開発者が日々の設計・実装で意識する具体策があります。
ここでは前者の代表的なフレームワークを鳥瞰し、
後者は次のサブセクションで「アタックサーフェス」として扱います。
代表的なフレームワークとして、
SEI CERTのSERAやNIST RMF(Risk Management Framework)があります。
NIST RMFは特に広く使われているアプローチで、現行のNIST SP 800-37 Rev.2では次の7ステップで構成されています。
Prepare → Categorize → Select → Implement → Assess → Authorize → Monitor
↑ |
+------------------------------------------------------------------+
(継続的なサイクル)
Rev.1では先頭のPrepareがなく6ステップでしたが、組織的な準備の重要性が認識されてRev.2でPrepareが追加された、という経緯です。
細かいステップの暗記よりも、
組織として何を守るかを定義し、コントロールを選び、実装し、継続的に監視する。
このサイクルが回っていること自体が本質だと感じます。
2020年代の文脈では、組織レベルのリスク管理の現代的な拡張として、
次のような概念が浮上しています。
- ゼロトラストアーキテクチャ(NIST SP 800-207、2020年公開): 「ネットワーク内部だから信頼できる」という前提を捨てる
- サプライチェーンリスク管理: SBOM(ソフトウェアに含まれる部品の一覧表)の整備や、依存OSSの脆弱性監視を通じて、依存関係まで含めて脆弱性を追跡する
組織レベルの枠組みは大枠だけ押さえておき、
ここからは開発者が日常で扱う具体策に焦点を移します。
攻撃される面を最小化する ── アタックサーフェスの考え方
アタックサーフェスとは「攻撃者がシステムに侵入できるすべてのポイントと、データが流出しうるすべてのポイント」の総称です。
OWASPの定義に従うと、アプリケーションのアタックサーフェスは次の4つの構成要素から成ります。
+--------------------------------------+--------------------------------------+
| (1) 入出力のすべてのパス | (2) それらのパスを保護するコード |
| - HTTPエンドポイント | - 認証・認可 |
| - メッセージキューの入口/出口 | - ログ |
| - ファイル入出力 | - データバリデーション |
| - 外部APIへの送信 | - エンコーディング |
+--------------------------------------+--------------------------------------+
| (3) アプリで使われる価値あるデータ | (4) それらのデータを保護するコード |
| - 秘密鍵・APIトークン | - 暗号化 |
| - 知的財産 | - チェックサム |
| - 個人データ(PII: 個人特定情報) | - アクセス監査 |
| - 重要な業務データ | - データ整合性コントロール |
+--------------------------------------+--------------------------------------+
つまり、データやコマンドが「入る経路と出る経路」「それを守るコード」「守るべきデータそのもの」「データを守るコード」の4つを意識する、という整理です。
アタックサーフェス分析を行う目的は、おおむね次のように整理できます。
- アプリケーションのどの部分がセキュリティ脆弱性のレビュー・テストを必要とするかを特定する
- 攻撃に晒されている部分を、開発者とセキュリティ専門家の双方が認識できるようにする
- アタックサーフェスを最小化する方法を見つける
- アタックサーフェスの変化と、その変化が持つリスク上の意味を把握する
注意点として、アタックサーフェス分析は基本的に「外部からの攻撃に焦点を当てた分析」です。
ユーザーやオペレーターを直接狙うマルウェア注入やソーシャルエンジニアリングは主な対象外で、内部のアタックサーフェスは外部とは異なる構造を持つ可能性があります。
実践面では、デフォルト設定でアタックサーフェスを最小化することと、設計や変更のたびに再評価することがポイントになります。
「使われない機能はオフにする」「使われない管理APIはエンドポイントを開けない」
といった判断の積み重ねが、アタックサーフェスを小さく保ちます。
各フェーズに組み込むセキュリティの具体策
攻撃者の目を知り、リスクの見積もりとアタックサーフェスの最小化を理解しました。
ここからは「では具体的に、開発の各フェーズで何をすればよいのか」に答えていきます。
セキュリティ要件の洗い出しから、脆弱性を生まないコーディングまで、
開発者が日常の作業の中で実践できる手法を見ていきます。
セキュリティ要件を体系的に洗い出す ── SQUAREプロセス
実務において、セキュリティ要件はしばしば軽視されがちです。
要件定義書に書かれていたとしても、
汎用的なセキュリティ機能リストからのコピーであることが多く、
プロジェクト固有のリスクに紐づいていないケースが目立ちます。
通常の要件エンジニアリングはユーザーの望む機能に焦点を当てますが、
「システムがやってはならないこと」にはほとんど注意が払われない、
という指摘もあります。
ユーザーは「システムが安全であること」を当然のように期待していますが、その期待を明示的にセキュリティ要件に翻訳する作業は意識的にやらないと抜けがちです。
SQUARE(Security QUAlity Requirements Engineering) は代表的なプロセスモデルです。
セキュリティ要件の抽出・分類・優先順位付けに特化しており、
「セキュリティ要件専用の要件定義プロセス」と捉えると分かりやすいと思います。
[ 準備フェーズ ] [ 抽出フェーズ ] [ 整理フェーズ ]
Step 1: 用語定義の合意 → Step 4: リスク評価 → Step 7: 要件の分類
Step 2: 資産・目標の特定 Step 5: 抽出技法の選定 Step 8: 要件の優先順位付け
Step 3: 支援成果物の整備 Step 6: セキュリティ要件の抽出 Step 9: 要件のインスペクション
9ステップを「準備(1〜3)→ 抽出(4〜6)→ 整理(7〜9)」の3グループに分けて捉えると、流れの見通しがよくなります。
すでに開発プロセスがある組織の場合、
SQUAREの全ステップをそのまま導入する必要はありません。
セキュリティ用語の定義の追加、リスク分析の実施、緩和策の開発、セキュリティ要件の優先順位付けなど、部分的に取り込むだけでも効果があります。
特に重要だと感じるのは次の3ステップです。
- 資産とセキュリティ目標の特定(Step 2): 「何を守るか」の合意がないまま走り出すと、後段の作業がブレます。同じ会社でも、人事部門は人事記録の機密性、研究部門は研究情報の保護、というようにステークホルダーごとに関心資産と目標が違います
- リスク評価(Step 4): 前のセクションで扱ったリスク分析と接続するステップです。要件は組織のリスク評価結果に基づいて優先順位がつくので、ここを飛ばすと「全部Must」という扱いにくい要件リストができてしまいます
- 要件の優先順位付け(Step 8): コスト対便益だけでなく、人命・評判・消費者の信頼の喪失といった金銭以外のコストも考慮する必要があります
SQUAREは、要件エンジニアとセキュリティ専門家が、
支援的な経営層とステークホルダーとともに実施するのが最も効果的だとされています。
要件定義のフェーズだからこそ、組織横断で握っておきたいプロセスです。
脆弱性を生まないコードを書く ── セキュアコーディングの実践
ソフトウェアの脆弱性の多くは、避けることができるコーディングエラーに起因します。
セキュアコーディングは、その名の通り「脆弱性を生まないコーディングの実践」です。
代表的な例として、バッファオーバーフローを取り上げます。
これは、プログラムがバッファの容量を超えるデータを書き込もうとする、
または割り当てられたメモリ領域の外に書き込もうとする状態を指します。
データの破損、クラッシュ、悪意あるコードの実行を引き起こす、
最も古典的かつ現在も頻繁に悪用されるコーディングエラーの一つです。
セキュアコーディングのベストプラクティスとして、SEI/CERTがTop 10を公開しています。
全項目を逐次解説するのは記事の範囲を超えるので、まずは特に日常で意識しやすい次の3項目を掘り下げ、残りは後ほど一覧表で俯瞰します。
- 入力検証
- デフォルト拒否と最小権限
- 多層防御
入力検証(Validate input)
すべての信頼できないデータソースからの入力を検証する、というのが原則です。
ここで「信頼できない」の意味を広めに取るのがポイントです。
ユーザー入力だけでなく、外部APIからのレスポンス、ファイルから読み込んだデータ、
メッセージキューから取り出したデータも含みます。
インジェクション系の脆弱性(SQLインジェクション、コマンドインジェクション、XSSなど)の多くは、入力検証を適切に行うことで防げます。
# NG: 入力をそのまま信頼してSQLに連結する
def get_user_by_name(name):
query = f"SELECT * FROM users WHERE name = '{name}'"
return db.execute(query)
# OK: パラメータ化クエリで入力をクエリ構造から分離する
def get_user_by_name(name):
query = "SELECT * FROM users WHERE name = %s"
return db.execute(query, (name,))
検証の観点としては、「型」「長さ」「形式」「範囲」「許可リスト」のいずれかでフィルタリングするのが基本です。
「禁止リスト」での検証は抜けが出やすいので、可能な限り「許可リスト」で書くのがセオリーとされています。
デフォルト拒否(Default deny)と最小権限
アクセス決定は「許可されているものを通す」というロジックで書く、
というのがデフォルト拒否の原則です。
「禁止されているものを弾き、それ以外は通す」という書き方はリスクが高い、
という考え方です。
最小権限は、すべてのプロセス・コンポーネントを「必要最低限の権限」で実行する、
という原則です。
クラウド環境であれば、各サービスのIAM(ユーザーやサービスにどの操作を許可するかを管理する仕組み)ロールがこの原則の適用先になります。
付与する権限を「とりあえずAdministratorAccess」にせず、
本当に必要な操作だけに絞り込む、という実践に直結します。
# NG: 「禁止されたロールでなければ通す」(禁止リスト方式)
FORBIDDEN_ROLES = {"banned"}
def can_access(user):
return user.role not in FORBIDDEN_ROLES # 新しい未知のロールが追加された瞬間に素通り
# OK: 「許可されたロールのみ通す」(許可リスト方式 = デフォルト拒否)
ALLOWED_ROLES = {"admin", "editor", "viewer"}
def can_access(user):
return user.role in ALLOWED_ROLES
この考え方は、後で出てくるSDLの設計哲学「Secure by Default」と直結します。
多層防御(Defense in depth)
複数の防御戦略を重ねてリスクを管理する、というのが多層防御の考え方です。
単一の防御に依存しない設計、と言い換えてもよいです。
たとえばWebアプリケーションでSQLインジェクションを防ぐとき、
次のような層を重ねます。
- パラメータ化クエリで入力をクエリ構造から分離する
- 入力検証で不正な文字を弾く
- DBユーザーの権限を最小化する
- WAF(Webアプリケーションの前段に置く、不正なリクエストを遮断する仕組み)で既知の攻撃パターンを遮断する
- アクセスログを記録して異常を検知する
1層が破られても、次の層で止まる確率を上げる発想です。
Top 10の俯瞰
ここまで触れた3項目以外も含めて、Top 10を一覧表にまとめておきます。
| # | プラクティス | 一言説明 | 防げる代表的な脆弱性の例 |
|---|---|---|---|
| 1 | 入力検証 | 信頼できないデータソースからの入力を検証する | SQLインジェクション、XSS、コマンドインジェクション |
| 2 | コンパイラ警告に注意を払う | 最高警告レベルでコンパイルし、警告をコードで解消する | バッファオーバーフロー、未初期化変数の使用 |
| 3 | セキュリティポリシーに沿って設計する | アーキテクチャと設計でポリシーを実装・強制する | 認可漏れ、設計レベルの抜け |
| 4 | シンプルに保つ | 設計をできる限りシンプルかつ小さく保つ | 複雑性に紛れたロジックバグ |
| 5 | デフォルト拒否 | アクセス決定は許可リスト方式で行う | 認可バイパス |
| 6 | 最小権限の原則 | プロセスは必要最低限の権限で実行する | 権限昇格の被害拡大 |
| 7 | 他システムへ送るデータをサニタイズ | コマンドシェル・DB・既製ソフトウェアコンポーネントへ渡すデータをサニタイズ | コマンドインジェクション、二次攻撃 |
| 8 | 多層防御 | 複数の防御戦略でリスクを管理する | 単一防御失敗時の被害拡大 |
| 9 | 効果的な品質保証技法を使う | レビュー・テスト・静的解析を組み合わせる | コーディングエラー全般 |
| 10 | セキュアコーディング標準を採用する | チームで標準を定めて遵守する | 標準化されていれば防げた既知の問題 |
セキュアコーディング標準(SEI CERTなどが言語別に提供)に加えて、コードインスペクションと静的解析ツール(SAST)の併用が有効です。
これは通常のコードレビュープロセスへの自然な追加として実施できます。
2020年代の文脈では、
OWASP Top 10やCWE(脆弱性タイプの共通辞書)が参考になる重要なリソースです。
実務では「自プロジェクトのコードレビューチェックリストに、OWASP Top 10やCWEから数項目だけ取り込む」のが現実的な第一歩だと感じます。
加えて、CI/CDパイプラインへの自動スキャンの組み込みも一般的な現代的実践になっています。
- SAST: 静的解析
- DAST: 動的解析(稼働中アプリへの自動脆弱性スキャン)
- SCA: ソフトウェア構成解析(依存OSSの脆弱性スキャン)
これらはSDL/DevSecOps(開発・セキュリティ・運用を一体化させ、CI/CDパイプラインの中で継続的にセキュリティチェックを回すアプローチ)の文脈で後ほど改めて触れます。
ライフサイクル全体でセキュリティを統合する
ここまで個別の手法を見てきました ── 攻撃者の視点を持つ手法、リスクを見積もる手法、要件を洗い出す手法、安全にコーディングする手法。
ただ、これらをバラバラに実施するだけでは効果は限定的です。
開発ライフサイクル全体の中に体系的に位置づけて、「いつ」「誰が」「何を」するかを明確にして初めて、セキュリティが開発プロセスに組み込まれた状態になります。
Microsoft SDLとSD3+C ── ライフサイクル全体を貫くセキュリティモデル
Microsoft SDL(Security Development Lifecycle)は、
業界を代表するソフトウェアセキュリティプロセスの一つです。
2004年からMicrosoft全社で必須ポリシーとして運用されています。
SDL導入後、Microsoftの製品で発見される脆弱性は大幅に減少し、
必要なパッチが減って結果的に大きなコスト削減にもつながった、と報告されています。
「セキュリティに投資するとコストが下がる」という、
一見直感に反する事例として個人的にも印象的でした。
SDLの設計哲学を表すのが、SD3+Cという4原則です。
Secure by Design、Secure by Default、Secure in Deployment、Communicationsの頭文字を組み合わせた呼び方になっています。
+----------------------------------------------------------+
| SD3+C |
+----------------------------------------------------------+
| |
| [Secure by Design] 設計段階で安全性を組み込む |
| - セキュアなアーキテクチャ・設計・構造 |
| - 脅威モデリングと緩和策 |
| - 既知の脆弱性をレビュー・分析・テストで排除 |
| |
| [Secure by Default] デフォルト状態で安全 |
| - 最小権限で実行 |
| - 多層防御 |
| - 80%が使わない機能はデフォルトオフ |
| - OS・セキュリティ設定を弱体化させない |
| |
| [Secure in Deployment] デプロイ後も安全に運用できる |
| - デプロイガイドの提供 |
| - セキュリティ分析・管理ツールの提供 |
| - パッチ展開ツールの支援 |
| |
| [Communications] 脆弱性への迅速な対応 |
| - 脆弱性報告への迅速な対応 |
| - セキュリティ更新情報の積極的な共有 |
| |
+----------------------------------------------------------+
ここで注目したいのは、前のセクションで触れた「最小権限」「多層防御」の位置づけです。
これらはSD3+Cの「Secure by Default」の構成要素として再登場しています。
コードレベルの実践原則が、組織レベルの設計哲学にも組み込まれている、
という多層的な構造です。
SDLは、開発プロセスを7つのフェーズに分けて、
それぞれに対応するセキュリティ活動を埋め込みます。
| フェーズ | このフェーズのセキュリティ活動の例 |
|---|---|
| Training | 中核セキュリティトレーニング |
| Requirements | セキュリティ要件の確立、品質ゲート/バグバーの作成、セキュリティ&プライバシーリスク評価 |
| Design | 設計要件の確立、アタックサーフェス分析、脅威モデリング |
| Implementation | 承認ツールの使用、安全でない関数の非推奨化、静的解析 |
| Verification | 動的解析、ファジングテスト、アタックサーフェスレビュー |
| Release | インシデント対応計画、最終セキュリティレビュー、リリースアーカイブ |
| Response | インシデント対応計画の実行 |
表に出てくる用語を簡単に補足しておきます。
品質ゲート/バグバー はリリース前に「許容できないバグや脆弱性の基準」を定めて通過判定する仕組み、
ファジングテスト は不正・異常な入力を大量に投入して堅牢性を確認するテスト手法です。
これまで見てきた手法が、それぞれのフェーズに配置されているのが分かります。
たとえばDesignフェーズには脅威モデリングが入っていますし、
Implementationフェーズには静的解析が入っています。
SDLは「個別手法のメニュー表」というよりも、
「メニューを配膳する順序を決める枠組み」と捉えるのが分かりやすいです。
SDLは元々ウォーターフォール的なフェーズを前提に作られたものですが、
アジャイル開発との統合ガイダンス(SDL-Agile)も公開されています。
スプリントごとに実施するもの、リリースごとに実施するもの、年1回実施するもの、
というように頻度の異なる活動として再構成して使われています。
2020年代の文脈では、SDLの考え方は現代のDevSecOpsに継承されています。
たとえば次のような実践です。
- CI/CDパイプラインへのSAST/DAST/SCAの自動組み込み
- Pull Request時の自動セキュリティスキャン
- Infrastructure as Codeのスキャン
これらはSDLが目指した「ライフサイクル全体への組み込み」の現代的な実装形と捉えられます。
「セキュリティを開発の最後にチェックする」のではなく、
「常にパイプラインに流して常に検査する」というスタイルです。
タッチポイント ── ライフサイクルモデルに依存しないセキュリティ活動
SDLとは別の系譜として、ソフトウェアセキュリティの分野では「タッチポイント」という考え方も提唱されています。
タッチポイントの主張は「重要なのはモデル(プロセスの形)ではなく、アクティビティ(具体的な活動)である」というものです。
どのライフサイクルモデルにも組み込めるため、
──特定のプロセス(ウォーターフォール/アジャイル/カンバンなど)に依存しない
という性質を持ちます。
タッチポイントとして挙げられる代表的な活動には、次のようなものがあります。
- コードレビュー(特にセキュリティ観点でのレビュー)
- アーキテクチャリスク分析
- ペネトレーションテスト
- リスクベース・セキュリティテスト
- 悪用ケースの作成
- セキュリティ要件の定義
- 運用環境での監視
タッチポイントは後にBSIMM(後ほど触れる成熟度モデル)の基盤にもなっています。
一部の組織では、セキュアソフトウェア開発で実施すべき「最小限の活動セット」としてタッチポイントを位置づけているケースもあります。
SDLとタッチポイントの使い分けを整理すると、次のような捉え方ができます。
- SDL: 「フェーズ全体を一から設計する」場合に向いている
- タッチポイント: 「既存プロセスにセキュリティ活動だけプラグインする」場合に向いている
セキュリティの測定と成熟度を高める
ライフサイクルにセキュリティ活動を組み込む方法はわかりました。
ただ、次に問うべき点があります。
「それが実際に効果を上げているか」をどう判断するか、
そして「組織のセキュリティ能力」を継続的に向上させるには何が必要か、です。
測定と成熟度モデルが、この問いへの答えを提供します。
セキュリティを測定する ── 困難だが不可欠な取り組み
ソフトウェアセキュリティの「適切な測定」は、
率直に言って困難な問題です。複数の見解があります。
- プロセス側からのアプローチ: 開発プロセス自体を評価し、結果として安全なソフトウェアが生まれる可能性を推定する
- 成果物側からのアプローチ: 脆弱性の発見数や、成功した攻撃の件数を測定する
いずれのアプローチも「100%安全」を保証することはできません。
OSや外部連携システムを加味し始めると、測定はさらに難しくなります。
それでも、測定なくして改善なし、という原則は変わりません。
データを収集しなければ、パターンの分析も、改善の検証もできません。
ソフトウェア品質の測定指標は、セキュリティ測定にも活用できます。
脆弱性は必ず何らかのソフトウェア欠陥に起因するため、
欠陥数・脆弱性数のカウントは有用な出発点になります。
Microsoftはアタックサーフェス分析を測定指標の一つとして活用し、
アタックサーフェスの最小化を継続的に追跡している、と報告されています。
ライフサイクルフェーズごとの測定指標としては、次のような例が挙げられています。
| フェーズ | 測定指標の例 |
|---|---|
| 要件 | プロジェクトで選定されたセキュリティ原則が、要件固有のアクションに反映されている割合 |
| 要件 | セキュリティ要件のうち、リスク/実現可能性/コスト便益/パフォーマンストレードオフの分析を受けた割合 |
| 要件 | 攻撃パターン・悪用ケース・脅威モデリングでカバーされたセキュリティ要件の割合 |
| アーキテクチャ・設計 | アタックサーフェス分析とアーキテクチャリスク分析の対象となったコンポーネントの割合 |
| アーキテクチャ・設計 | セキュリティ設計パターンでカバーされた高価値セキュリティコントロールの割合 |
フェーズ別の指標とは別に、リスク観点と信頼できる依存関係観点で、
次のような指標も挙げられています。
- リスク: アクティブな脅威の数(カテゴリ別)、インシデント報告数(カテゴリ別)、各脅威カテゴリの発生確率と影響度見積もり
- 信頼できる依存関係: サプライチェーンの下請け階層の深さ、各レベルのサプライヤー数、検証済み信頼サプライヤーの数
最初から完璧な測定体系を作ろうとしないで、自プロジェクトで取りやすい指標から始めて段階的に拡張する、というのが現実的なアプローチだと思います。
組織の成熟度を高める ── BSIMMとSAMM
ソフトウェア開発全般の成熟度モデル(CMMIなど)と同様に、
セキュリティにも成熟度モデルがあります。
CMMI Instituteからは、サイバーセキュリティ成熟度に特化したプラットフォーム(Cyber Capability Maturity Management)も提供されています。
ソフトウェアセキュリティ固有の成熟度モデルとしては、SAMMとBSIMMが代表的です。
OWASP SAMM(Software Assurance Maturity Model)
組織が直面する固有のリスクに合わせたセキュリティ戦略の策定を支援するオープンフレームワークです。
2020年にv2へ刷新され、現行の主要バージョンとなっています。
次の4つの目的を持っています。
- 組織の既存のソフトウェアセキュリティ実践を評価する
- 明確に定義された反復で、バランスの取れたセキュリティ保証プログラムを構築する
- セキュリティ保証プログラムの具体的な改善を示す
- 組織全体のセキュリティ関連活動を定義し測定する
BSIMM(Building Security in Maturity Model)
おそらく最も有名なソフトウェアセキュリティ固有の成熟度モデルで、定期的にリリースされ、最新の評価結果はウェブサイトからダウンロードできます。
「ソフトウェアセキュリティイニシアチブの立案と実行を行う人」を主な対象読者として設計されています。
SAMMとBSIMMの違いを一言で表すなら、規範性と記述性の違いです。
- SAMM(規範的): 「組織が目指すべき活動の枠組み」を提供する。あるべき姿のチェックリストとして使う
- BSIMM(記述的): 「他社が実際にやっている活動の集合知」を提供する。同業他社のベンチマークとして使う
両者は対立するものではなく、補完関係にあります。
SAMMで「目指す姿」を定義し、BSIMMで「他社の実態」と比較して自組織の立ち位置を把握する、という使い方ができます。
これらのモデルの本質的な要素は無料で利用可能ですが、
外部の専門機関による評価を受けることもできます。
内部で自己評価を行って改善プログラムを定義することも可能ですが、
専用のリソースと労力は必要になります。
最後に正直なことを書くと、どの成熟度モデルも完璧ではありません。
良いプロセスに従ったからといって、
セキュアなソフトウェアが保証されるわけではないからです。
それでも、プロセスの成熟が統計的にはより安全なソフトウェアにつながる、
というエビデンスは蓄積されています。
「保証はできないが、確率は上がる」という整理で受け止めるのが健全だと感じます。
おわりに
整理してみる前は、セキュリティを「セキュリティチームの仕事」「自分はコードを安全に書くだけ」と捉えていました。
ところが書き終えてみると、セキュリティは「要件・設計・実装・運用のすべてに開発者が関わる横断的なエンジニアリング活動」として像を結びました。
攻撃者の視点を要件定義に持ち込み、脅威モデリングで設計を見直し、セキュアコーディングで日々のコードに織り込み、ライフサイクル全体で測定・改善を回す。
ひとつのフェーズだけで完結する話ではなかったのが、想像以上に印象的でした。
明日から始められる小さな一歩としては、たとえば次のような選択肢があると思います。
どれか一つ、引っかかったものから始めてみると、地に足のついた変化につながりやすいです。
- 自分が今書いているサービスのアタックサーフェス(入力経路、認証経路、保管しているデータ)を1枚の図にしてみる
- チームのコードレビューチェックリストに、OWASP Top 10やCWEから3項目だけ追加してみる
- 既存のCI/CDパイプラインに、静的解析(SAST)か依存関係スキャン(SCA)を1つだけ組み込んでみる
残された論点もあります。
特にAI/LLMをコード生成や運用判断に組み込むことで、新しいアタックサーフェスが生まれます。
プロンプトインジェクション、AI生成コードの脆弱性混入、モデルのデータ汚染などです。
これらはここまで見てきた古典的な脅威モデルだけでは捉えきれない領域です。
STRIDEやアタックサーフェスの考え方を、AIコンポーネントを含むシステムにどう拡張するかは、これからの論点になりそうです。
セキュリティに取り組まないことのコストは年々上がっています。
一方で、対抗するためのツールや手法は既にかなり整備されてきています。
完璧を目指す必要はなく、まず一歩踏み出すことに価値がある、というのが今のところの自分の結論です。
-
Microsoft の STRIDE-per-element の標準表ではデータストアにも Repudiation が含まれます。特にログを保管するデータストアでは、攻撃者がログを大量投入して埋もれさせる/改ざんするなどして「やっていない」を成立させようとする攻撃が考えられるためです。逆に、データストアに置かれたログは多くの場合 Repudiation 脅威に対する緩和策にもなります。 ↩