境界防御の幻想と、アイデンティティを唯一の防壁とする理由
「社内ネットワークにさえ入れれば、あとはフリーパス」──そんな甘い神話にしがみついている組織は、もはや現代のサイバー戦においては格好の獲物でしかない。ランサムウェアグループや国家背景のAPT組織は、巧妙なフィッシングやサプライチェーン攻撃を通じて、いとも簡単にペリメータ(境界)の内側へと侵入する。一度侵入されてしまえば、フラットな社内ネットワークを横移動(ラテラルムーブメント)し、ドメインコントローラーを掌握するまでの時間はわずか数時間だ。
我々セキュリティアーキテクトが直面している現実は、ネットワークの「内と外」という概念の完全な崩壊である。
このパラダイムシフトに対する唯一の解が、NIST SP 800-207で定義される「ゼロトラストアーキテクチャ(ZTA)」だ。だが、現場を見渡すと、ゼロトラストを単なる「クラウドプロキシの導入」や「多要素認証(MFA)の義務化」と同義に捉えているエンジニアがあまりにも多い。本質はそこではない。ZTAの本質は、あらゆるリクエストを「信頼せず、常に検証する(Never Trust, Always Verify)」原則に基づき、アイデンティティ(ID)を新たな境界線として再定義することにある。
今回は、NIST SP 800-207の中核をなす「ポリシー決定ポイント(PDP)」と「ポリシー実行ポイント(PEP)」の設計・実装に焦点を当て、単なる概念論ではなく、現場の泥臭いインフラにどう落とし込むべきか、その実践的な防衛ロジックを紐解いていく。
—
NIST SP 800-207が描くPDPとPEPの厳密な役割分担
ゼロトラストのアーキテクチャを語る上で、NISTが定めたコンポーネントの役割を正確に理解しておく必要がある。多くの実装ミスは、このPDP(Policy Decision Point)とPEP(Policy Enforcement Point)の境界線を曖昧にした結果として生じる。
[クライアント] ---> (リクエスト) ---> [ PEP (リバースプロキシ/APIゲートウェイ) ]
│
(コンテキスト検証)
▼
[ PDP (認可エンジン) ]
│
(IDP / 脅威intel)
1. ポリシー実行ポイント(PEP: Policy Enforcement Point)
PEPは、すべての通信の玄関口であり、いわば「城門の跳ね橋」だ。クライアントからのリクエストを最初に受け止め、PDPの許可が出るまでリクエストを背後のリソース(マイクロサービスやデータベース)に通さない。
インフラの文脈では、次のようなコンポーネントがPEPとして機能する。
- APIゲートウェイ(Kong, Envoy, AWS API Gatewayなど)
- 次世代ファイアウォール(NGFW)やリバースプロキシ
- サービスメッシュのサイドカープロキシ(Envoy等)
2. ポリシー決定ポイント(PDP: Policy Decision Point)
PDPは、頭脳である。PEPから送られてきたコンテキスト情報(誰が、どの端末から、どんな状態の、どのリソースに対してアクセスしようとしているか)を受け取り、組織のセキュリティポリシーに基づいて「許可(Allow)」「拒否(Deny)」「追加検証(Step-up Auth)」を判定する。
PDPは通常、以下の2つのサブコンポーネントに分かれる。
- ポリシーエンジン(PE): 実際にアクセスを許可するかどうかのアルゴリズムを実行する頭脳。
- ポリシーアドミニストレータ(PA): PEPに対して、セッションの確立や切断を指示する命令系統。
実務において重要なのは、PEPは決して自律的に「許可・拒否」の判断を下してはならないという点だ。PEPはあくまで「門番」であり、判断を下すのは常に「最高裁判所」であるPDPでなければならない。
—
現場で即座に使える:EnvoyとOPAによるPDP/PEPの実装設計
では、このアーキテクチャを現代のクラウドネイティブ環境にどう実装するか。もっとも堅牢かつ柔軟な組み合わせの一つが、エッジプロキシとしての Envoy(PEP)と、オープンソースの認可エンジンである Open Policy Agent (OPA)(PDP)の統合だ。
以下の設定例は、Envoyが受け取ったリクエストのコンテキストをOPAに問い合せ、ゼロトラストな認可判断を下すための構成を示す。
Envoyの設定ファイル(envoy.yaml)
PEPとして振る舞うEnvoyは、外部のPDP(今回はOPA)に対してHTTPベースの外部認可(ext_authz)リクエストを飛ばす。
static_resources:
listeners:
- name: ingress_listener
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: secure_backend
domains: ["api.internal.local"]
routes:
- match:
prefix: "/"
route:
cluster: backend_service
http_filters:
# PDP(OPA)へ認可を問い合わせる外部認可フィルター
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
http_service:
server_uri:
uri: http://opa.security-system.svc.cluster.local:8181
cluster: opa_cluster
timeout: 0.25s # レイテンシを抑えるため250msでタイムアウトさせる
authorization_request:
allowed_headers:
patterns:
- exact: "authorization"
- exact: "x-device-trust-score"
transport_api_version: V3
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: opa_cluster
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: opa_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: opa.security-system.svc.cluster.local
port_value: 8181
- name: backend_service
connect_timeout: 0.5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: backend_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: backend.internal.local
port_value: 80
OPA(Rego言語)によるポリシー決定ロジック
次に、PDP側(OPA)で動作するポリシーファイル(policy.rego)だ。ここでは単なる「トークンを持っているか」だけでなく、デバイスの健全性スコア(Device Trust Score)やアクセスのコンテキストを多角的に評価する。
package envoy.authz
import future.keywords.in
default allow = false
# HTTPリクエストの基本構造を定義
<strong>http_request</strong> := input.attributes.request.http
# メインの認可判定ロジック
allow {
# 1. 有効なJWTクレームの検証とロール確認
claims := parse_jwt_payload(http_request.headers.authorization)
claims.role in ["system_admin", "security_analyst"]
# 2. デバイスの健全性チェック(EDR等から連携された信頼スコアが80以上であること)
to_number(http_request.headers["x-device-trust-score"]) >= 80
# 3. 業務時間外の特権アクセスに対する追加制約(例として平日の営業時間内のみ許可するなど)
# ※本番ではタイムゾーンや動的コンテキストをここに組み込む
}
# JWTのペイロードをデコードするヘルパー関数(簡略化のため署名検証はAPI Gateway側やIdPで実施済みを前提)
parse_jwt_payload(auth_header) := payload {
startswith(auth_header, "Bearer ")
token := substring(auth_header, 7, -1)
[_, encoded_payload, _] := io.jwt.decode(token)
payload := encoded_payload
}
この実装における肝は、PEPであるEnvoyが「Authorization ヘッダー」と「x-device-trust-score」という生データを抽出し、PDPであるOPAが「このユーザーのロールは何か」「デバイスは安全か」というビジネスロジック・セキュリティポリシーに基づいて厳密な判定を下している点にある。ネットワーク経路のどこを切り取っても、動的な検証が挟まる仕組みだ。
—
攻撃者が狙う盲点:アイデンティティベース制御の限界と「セッションハイジャック」の脅威
ここまで完璧なPDP/PEPアーキテクチャを構築したとしても、それで攻撃を完全に防げるわけではないのがセキュリティの奥深いところだ。ホワイトハッカーの視点から、このアイデンティティベース制御における最大の盲点を指摘しておこう。
それは、「アイデンティティそのものが窃取・悪用された場合、システム側からは正当なユーザーと区別がつかない」という根本的な矛盾だ。
攻撃者は、フィッシングサイトやブラウザのインプラント(マルウェアによるセッションクッキーの直接窃取)を用いて、正当に発行されたJWTやセッションクッキー(Session Token)をそっくりそのまま盗み出す。一度有効なトークンを奪ってしまえば、攻撃者はどれほど強固なMFAを突破した過去を持っていようとも、PEPをいとも簡単にすり抜けてバックエンドに到達できる。
この盲点を潰すための「次世代の防衛レイヤ」
アイデンティティの信頼性を担保し続けるためには、単なる「ログイン時の認証」から「継続的認証(Continuous Access Evaluation: CAE)」への移行が不可欠である。
1. DPoP(Demonstrating Proof-of-Possession at the Application Layer)の導入:
トークンに暗号学的な縛りを入れ、クライアント側が持つ秘密鍵で署名されたリクエストでなければAPIを受け付けないようにする。これにより、クッキーやトークンが盗まれても、秘密鍵が盗まれていなければ他の端末から再利用できなくなる。
2. IP・TLSフィンガープリントのコンテキスト紐付け:
PDPの判定ロジックに、単なるユーザーIDだけでなく、TLSハンドシェイク時のJA3/JA4フィンガープリントや、セッション途中でのネットワークセグメントの急激な変化(地理的異常)をリアルタイムで統合し、異常検知時には瞬時にセッションを無効化するパイプラインを構築する。
—
結びにかえて:セキュリティは「状態」ではなく「プロセス」である
ゼロトラストアーキテクチャにおけるアイデンティティベースのアクセス制御は、決して「一度導入すれば安心の銀の弾丸」ではない。それは、侵入を前提とした終わりのないリスク管理のプロセスそのものだ。
PDPのポリシー定義を誤れば、正当なビジネススピードを殺すか、あるいはセキュリティホールを広げるかの二者択一になる。PEPの配置場所を間違えれば、ラテラルムーブメントを許す死角が生まれる。
コードを書き、インフラを構築し、ログを監視するテックリードやアーキテクトである我々は、常に「このアイデンティティは、本当に今この瞬間も信頼に値するのか?」という疑いの目をシステムに向け続けなければならない。それこそが、巧妙化するサイバー脅威に対抗するための、唯一にして最強の防衛策である。
コメント