公開クラウドファンクションのリスクと堅牢化の方法

執筆: Corné de Jong


はじめに 

Mandiantのセキュリティ評価では、特定のビジネス要件を理由として、認証を欠いたまま公開されているサーバーレスアプリケーションが頻繁に確認されています。サーバーレスの展開では通常、サードパーティ製パッケージを組み込んだ独自開発コードが実行されるため、次のような幅広いアプリケーション層攻撃の標的となります。

  • ローカルファイルインクルージョン(LFI)およびリモートファイルインクルージョン(RFI)

  • コマンドインジェクション

これらの脆弱性が悪用されると、攻撃者は基盤となるコンテナインスタンスを完全に制御下に置く可能性があります。こうしたアクセスは足がかりとなり、最終的には被害者のクラウド環境全体の侵害につながることもあります。

本ブログ記事では、顧客対応で得られた知見をもとに、攻撃シナリオを解説するとともに、サーバーレス環境を保護するための実践的なガイダンスを提供します。今回の分析はGoogle Cloud Runのサービスおよびファンクションのうちパブリックにアクセスできる状態を維持する必要があるものの堅牢化戦略に焦点を当てていますが、これらの原則は公開されているあらゆるサーバーレス展開に共通して適用できます。

サーバーレスアプリケーションとは

サーバーレスアプリケーション(Function-as-a-Service、FaaSとも呼ばれます)は、基盤インフラを管理する必要なく、柔軟で疎結合、かつイベント駆動型のクラウドアーキテクチャの中で、個々のコードブロックをマイクロサービスとして展開できる仕組みです。これらのサービスにより、アプリケーションや自動化処理は運用負荷をかけることなく自動的にスケールし、即座に展開できます。サーバーレスサービスは、主要なEコマース、メディア、決済処理アプリケーション、そしてAI活用の基盤を支えています。 

生成AIの急速な普及は、サーバーレスアーキテクチャの利用拡大を後押しする大きな要因となっています。チャットボットとのやり取り、画像生成、「バイブコーディング」、マルチステップのAIエージェントなど、AIワークフローはユーザーのタスクを完了させるためにサーバーレスファンクションに依存しています。この成長により、サーバーレス環境の保護は企業のセキュリティチームにとってより差し迫った課題となっています。 

サーバーレスアプリケーション攻撃のリスク

公開されているサーバーレスワークロードは、脅威アクターにとって初期アクセスの足がかりとなり得ます。前述の通り、これらのサービスにはコード自体、インポートされたパッケージ、または基盤となるランタイム環境に脆弱性が存在する場合があります。

侵入経路が悪用されると、攻撃者は通常、権限昇格や横方向への移動を試みます。よく確認される手法には、次のようなものがあります。

  • アプリケーションコード内に直接保存されている秘密情報の抽出

  • アプリケーションのロジックや機密データを精査し、環境内のさらなる攻撃経路を特定する

  • リモートコード実行(RCE)の成功後、メタデータサーバーからサービスアカウントのベアラートークンを窃取する

これらの侵害済みの秘密情報やサービスアカウントを悪用することで、脅威アクターは隣接するシステムやワークロードへとピボットでき、適切な堅牢化戦略が講じられていない場合、環境全体の完全な乗っ取りにつながる可能性があります。

攻撃シナリオの例

以下の簡略化されたシナリオは、サーバーレスファンクションがどのように侵害され得るか、また攻撃者が初期コード実行を達成した後にどのようにピボットするかを示しています。

ローカルファイルインクルージョン(LFI) 

次のCloud Runの例では、Python/Flaskで実装されたファンクションが、適切な検証を行わないままユーザー制御可能な入力を受け取ってファイルを開いています。このパターンは、ローカルファイルインクルージョン(LFI)脆弱性の一例です。

import functions_framework
@functions_framework.http
def hello_http(request):
    request_json = request.get_json(silent=True)
    request_args = request.args
    if request_json and 'file' in request_json:
        file = request_json['file']
    elif request_args and 'file' in request_args:
        file = request_args['file']
# VULNERABILITY: The 'file' parameter is used directly in open() 
# without validation, allowing arbitrary file access
    with open(file, 'r') as resp:
          filedata = resp.read()
    return 'local file data {}!'.format(filedata)

図1: 検証されていないユーザー入力を受け取ってファイルを開く、脆弱なPython/Flaskファンクション

この脆弱性により、攻撃者はcurlを使い、fileパラメータ経由でPOSTリクエストを送信することで、Cloud Runインスタンスから機密ファイルを要求できます。

curl -X POST https://cloudrun01-abc.europe-west3.run.app/ -H "Content-Type: application/json" -d '{"file": "main.py"}'

図2: fileパラメータを狙ったcurlのPOSTリクエスト

このレスポンスからは、main.pyの完全なソースコードが取得できます。攻撃者は、そのコードを分析して次のような情報を得ることができます。

  • APIキー、データベース認証情報、認証トークンなどのハードコードされた秘密情報

  • ビジネスロジックの欠陥や追加のインジェクションポイント

  • 内部サービスのエンドポイントやアーキテクチャの詳細

  • 技術スタックや潜在的なCVEの露出を明らかにするimport文

さらに、攻撃者は標準的な../のディレクトリトラバーサルシーケンスを利用して、機密性の高いシステムファイルを取得することもできます。

curl -X POST https://cloudrun01-abc.europe-west3.run.app/ -H "Content-Type: application/json" -d '{"file": "../../../etc/passwd"}'

図3: ディレクトリトラバーサルシーケンスを利用したcurlのPOSTリクエスト

LFI脆弱性があると、攻撃者はコンテナから直接さまざまなファイルを取得し、ファジングを行うことができます。主な例は次の通りです。

  • requirements.txt, package.json, go.mod: インストールされているパッケージやバージョンを特定し、既知の脆弱性を洗い出すために使用されます。

  • .envファイル: 機密性の高い環境変数やハードコードされた秘密情報が含まれていることが多い。

  • アプリケーション設定ファイル: 安全に管理されていない場合、データベース認証情報、APIキー、サービスエンドポイントが含まれる可能性があります。

  • /etc/passwd, /proc/self/environ: ユーザー情報や環境変数が含まれています。

  • アプリケーションログ: 認証トークンや個人情報(PII)が含まれる場合があります。

ベストプラクティス: 秘密情報や認証情報を、ソースコードやローカルのコンテナファイル内に保存しないでください。Secret Managerのような専用のシークレット管理ソリューションを利用してください。

コード実行・コマンドインジェクション

次のシナリオでは、Pythonファンクションがシェル実行メソッドをサニタイズされていないユーザー入力とともに使用しており、攻撃者が任意のコマンドを実行できる状態になっています。

import functions_framework
import subprocess
@functions_framework.http
def hello_http(request):
  request_json = request.get_json(silent=True)
  request_args = request.args
  if request_json and 'input' in request_json:
      input = request_json['input']
  elif request_args and 'input' in request_args:
      input = request_args['input']
  result = subprocess.run(input, shell=True,capture_output=True, text=True)
  return format(result)

図4: サニタイズされていないユーザー入力を伴うシェル実行を利用するPythonファンクション

これにより、攻撃者はGCPのメタデータサービスを狙った後続のcurlリクエストを実行し、サービスアカウントのベアラートークンを取得できます。 

次のリクエストは、1時間有効なサービスアカウントのOAuth 2.0ベアラートークンを抽出します。

curl -X POST https://cloudrun02-abc.europe-west3.run.app/ -H "Content-Type: application/json" -d "{\"input\": \"curl 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' -H 'Metadata-Flavor: Google'\"}"

図5:curlリクエストによるGCPサービスアカウントのベアラートークンの抽出

トークンを取得すると、攻撃者はそれを攻撃者が管理するシステム上で使用し、Google Cloud CLIコマンドを実行できます。例えば、CLOUDSDK_AUTH_ACCESS_TOKEN環境変数に、窃取したベアラートークンを設定することができます。

export CLOUDSDK_AUTH_ACCESS_TOKEN=”obtain bearer token”

図6: CLOUDSDK_AUTH_ACCESS_TOKEN環境変数の定義

その後、攻撃者はCloud Run Computeサービスアカウントのセキュリティコンテキスト内でGoogle Cloud CLIを悪用できます。ベストプラクティスや慎重な構成管理を伴わずに展開されている場合、例えばCloud RunサービスがEditor権限を持つデフォルトのコンピュートサービスアカウントとして実行されている場合、これはGCPプロジェクト全体の完全な乗っ取りに等しく、攻撃者は次のことが可能になります。

  • ほとんどのGCPリソースの読み取り・書き込み・削除

  • 新規サービスの展開や既存構成の変更

  • 秘密情報や暗号鍵へのアクセス

  • アクセス可能なすべてのストレージシステムからのデータ窃取

  • 新規サービスアカウントやSSHキーを通じた永続的なバックドアの確立

堅牢化に関する推奨事項

Mandiantは、組織が効果的なサーバーレスセキュリティを実現するために、次のような複数のアプローチを並行して実施することを推奨しています。

  • セキュアなソフトウェア開発ライフサイクル(S-SDLC): 展開前にセキュリティスキャン、コードレビュー、最小権限のIAMをCI/CDパイプラインに組み込み、継続的なセキュリティテストを統合すること; 

  • バイブコーディング: Mandiantは、AI生成コード、いわゆる「バイブコーディング」に対して多層的なセキュリティ対策を講じることを推奨しています。組織はAIによる試行を専用のサンドボックス環境に隔離し、本番システムや社内データを保護するために厳格なデータ流出制御を実施すべきです。さらに、開発環境は、人間の関与を前提とした承認済みIDEに限定し、サプライチェーンの脆弱性を軽減するために最小権限で動作する検証済みプラグインのみを使用すべきです。最後に、組織はこのAI生成ソフトウェアがセキュアなソフトウェア開発ライフサイクル(S-SDLC)の管理体制に従うことを確実にするとともに、許可される用途に関する明確な社内ガイドラインを整備する必要があります。バイブコーディングにおける包括的なセキュリティの基本原則は、Wiz Vibe Coding Security Fundamentalsブログに詳しく記載されています。

  • 実行時の補完的制御: アプリケーションに脆弱性が存在する場合でも、侵害を制限・封じ込めるために、以下のような多層防御策を実施すること;

パブリックサービスの分離

信頼できない外部エンティティが利用するパブリック向けのCloud Runサービスは、専用の独立したGoogle Cloudプロジェクトでホストしてください。これにより、侵害が発生しても、重要な内部リソースへの即座の到達経路とはなりません。この「サービスプロジェクト」モデルの実装は本記事の範囲を超えますが、セキュアなサーバーレスアーキテクチャのブループリントに詳しく記載されています。

ID・アクセス管理(IAM)

Mandiantは、サービス認証にデフォルトのCompute Engineサービスアカウントを使用するのではなく、最小権限の原則に従ったカスタムサービスアカウントを使用することを推奨します。Cloud Runファンクションの動作に必要な特定の権限のみを付与してください。例えば次の通りです。

  • Cloud Storageバケットへのアクセス: サービスがCloud Storageバケット内のオブジェクトへの読み取りアクセスのみを必要とする場合は、該当バケットに限定してStorage Object Viewerroles/storage.objectViewer)ロールを付与してください。

  • Secret Managerへのアクセス:  サービスが秘密情報へのアクセスを必要とする場合は、必要な個々のシークレットにのみ Secret Manager Secret Accessorroles/secretmanager.secretAccessor)ロールを付与してください。Cloud Runからのシークレットアクセスの詳細については、シークレット構成に関するGCPドキュメントを参照してください。

レイヤー7アプリケーションロードバランサー(ALB)アーキテクチャ

サーバーレスファンクションへのイングレストラフィックは内部のみに制限し、インターネットへの露出は外部のレイヤー7 ALBで管理してください。これにより次のような利点が得られます。

  • トラフィックの一元管理: ヘッダーやSSLポリシーに対するきめ細かな制御。

  • Cloud Armorとの統合: ローカル/リモートファイルインクルージョン(LFI/RFI)やサーバーサイドリクエストフォージェリ(SSRF)などの脆弱性に対してアプリケーションを堅牢化する、ウェブアプリケーションファイアウォール(WAF)のサポート。

  • トラフィックシェーピング: 不正利用を防ぐためのレート制限やリクエスト制限の実装。

  • 可視性の向上: セキュリティ監視のための堅牢なロギングおよびログ転送機能。

  • Identity-Aware Proxy(IAP): 内部ユーザーに対して特定のID認証が必要なシナリオへの統合サポート。

ウェブアプリケーションファイアウォール(WAF) Cloud Armor

Cloud Armorは、ロードバランサーと統合して悪意のあるトラフィックをフィルタリングできるWAF保護機能を提供します。以下の例では、これまでに説明した特定のローカルファイルインクルージョン、リモートコード実行、トラバーサル攻撃をブロックするためのCloud Armorセキュリティポリシーの設定方法を示します。

ローカルファイルインクルージョン

あらかじめ構成されたlfi-v33-stableWAFルールにより、一般的なローカルファイルインクルージョン攻撃をブロックできます(ローカルファイルインクルージョンのリファレンス)。

evaluatePreconfiguredWaf('lfi-v33-stable', {'sensitivity': 3})

図7: Cloud Armorのlfi-v33-stable WAFルール構成

パストラバーサルリクエスト../../../etc/passwdをブロックし、403 Forbiddenが返される様子:

curl -X POST https://exampleabc01.com -H "Content-Type: application/json" -d '{"file": "../../../etc/passwd}'
<!doctype html><meta charset="utf-8"><meta name=viewport content="width=device-width, initial-scale=1"><title>403</title>403 Forbidden

図8: Cloud Armorがパストラバーサルリクエストをブロックし、403 Forbiddenが返されることの検証

evaluatePreconfiguredWaf('rce-v33-stable', {'sensitivity': 3})

図9: Cloud Armorのrce-v33-stable WAFルール構成

前述の例のリモートコード実行リクエストをブロックし、403 Forbiddenが返される様子:

curl -X POST https://exampleabc01.com -H "Contencurl -X POST https://exampleabc01.com -H "Content-Type: application/json" -d "{\"input\": \"curl 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' -H 'Metadata-Flavor: Google'\"}"
<!doctype html><meta charset="utf-8"><meta name=viewport content="width=device-width, initial-scale=1"><title>403</title>403 Forbidden

図10: Cloud Armorがリモートコード実行をブロックし、403 Forbiddenが返されることの検証

サーバーレスアーキテクチャの制御

Cloud Runサービスの堅牢化は、セキュアなアーキテクチャの一部分に過ぎません。これらのサービスは他のGoogle Cloudリソースと接続することが多いため、1つの侵害が追加のサービスの露出につながる可能性があります。多層防御の実装が重要です。特に、直接VPCエグレスまたはVPC Accessコネクタを使用する場合は、VPC Service Controlsを用いて、きめ細かなアクセスポリシーによって横方向の移動やデータ流出を制限してください。

セキュアなソフトウェア開発ライフサイクル(S-SDLC)

これまでに説明した堅牢化戦略は重要ですが、理想的な水準は依然として、開発の初期段階で脆弱性を能動的に特定することにあります。「シフトレフト」セキュリティの詳細な考察は、既存コード内のリスク軽減に焦点を当てた本分析の範囲を超えます。しかし、セキュアなソフトウェア開発ライフサイクル(S-SDLC)は、依然として基本原則であり続けます。サーバーレスファンクションを外部公開する前に脅威を無害化するには、堅牢なコード検証と継続的なセキュリティテストが不可欠です。

Cloud Runの脅威検知

本記事で説明した堅牢化の推奨事項に加えて、Google Cloud Security Command Center(SCC)は、Cloud Runリソースを狙ったコントロールプレーン攻撃を検知する組み込みサービスを提供します。これには、認証情報へのアクセス、偵察活動、スクリプトやリバースシェルの実行を検知する検知器が含まれます。Cloud Run Threat Detectionサービスは、PremiumおよびEnterpriseの各ティアで利用可能です。

結論

サーバーレスアプリケーションは、俊敏性と迅速なビジネス価値の創出を後押しします。「バイブコーディング」によってコードの展開はかつてないほど容易になった一方で、この猛烈な速度の中でも、チームは開発ライフサイクルの早い段階からセキュリティを組み込み、デフォルト設定に頼ることをやめ、IDとアーキテクチャを中心とした多層防御戦略を優先することが求められています。 

謝辞

本分析は、Ischa Rijff氏、Phil Pearce氏、Juraj Sucik氏の協力なしには実現しませんでした。

投稿カテゴリ

翻訳元: https://cloud.google.com/blog/topics/threat-intelligence/exposed-cloud-functions-harden/

ソース: cloud.google.com