マイクロサービスアーキテクチャーのSSRF問題とセキュアコーディング

(Last Updated On: )

Webシステムアーキテクチャーとしてマイクロサービスを利用しているケースは随分増えていると思います。従来から一般的に見られるWebアプリの作り方をすると、マイクロサービスではSSRF問題が簡単に発生します。

2017年版OWASP TOP 10ではA10 Insufficient Logging and Monitoringを新しく追加しました。一言でいうと、「未検証入力」を残してしまうアプリは脆弱なアプリである、とするのがA10脆弱性です。マイクロサービスで発生するSSRF問題の主な原因は「未検証入力」です。

「未検証入力」は既存の非マイクロサービスアーキテクチャーのWebアプリでも問題でしたが、マイクロサービスアーキテクチャーの登場によって、「未検証入力」がより重大な脅威になってきたことに対応する意味もあります。マイクロサービスアーキテクチャーで未検証入力を渡してしまうと簡単にSSRF問題が発生します。

SSRF – Server Side Request Forgery(サーバーによるリクエスト詐称)

SSRFとは?

SSRFは目新しい問題ではありません。少なくとも80年代の終り頃にはセキュリティ問題だと認識されていた問題です。

今のUNIX系OSには存在しませんが、古いUNIX系OSにはrsh(Remote Shell。 ややこしい事にsendmailで使われていたシェルにもrshがありこちらはRistricted Shellの事を指します)がありました。rshは”信頼できるホスト”からのコマンドをリモートで実行するシェルです。”信頼できるホスト”は設定ファイルで指定します。

rshでサーバーAからサーバーBにアクセスできる(信頼される)ように設定されている場合、

  1. 攻撃者がサーバーAにアクセスする
  2. 攻撃者がサーバーAでrshを使いコマンドを送信する(サーバーAはサーバーBへのアクセス権限を持つ)
  3. 攻撃者のコマンドがサーバーBで実行される

となります。本来、攻撃者はサーバーBにアクセスできないようになっていたとしても、サーバーBへのアクセス権限を持つサーバーAを経由しその権限を利用することにより、サーバーBにも攻撃できる、これがSSRFです。

XXE(XML eXternal Entity)攻撃もSSRFの一種です。Webサーバーが持つ他のサーバーへのアクセス権限を使って本来アクセスできないハズのサーバーに攻撃します。XXEはXMLのDTDタグ(URIデータでXML処理系はこのURIにアクセスできる)に攻撃用のURIを記載して攻撃します。

  1. 攻撃者が攻撃用XMLデータをWebサーバーに送信する
  2. Webサーバーが攻撃用XMLデータを処理し、DTDタグを処理する。(攻撃用URIには攻撃対象の内部サーバーとクエリー文字列を設定したURIが設定されている)
  3. Webサーバーが攻撃対象の内部Webサーバーが攻撃用のURI(不正命令を実行させるクエリ文字列付き)でアクセスする

攻撃対象のサーバーがWebサーバーを信頼している(アクセスできるようになっている)場合、攻撃用URIによって攻撃対象のサーバーが不正に操作されます。

rshによるSSRFもXXEも構造としては

  1. 攻撃者がアクセス権限を持っているサーバーAにアクセス
  2.  攻撃者がサーバーAで◯◯を使い何らかの命令をサーバーBに送信する(サーバーAはサーバーBへのアクセス権限を持つ)
  3.  攻撃者の命令がサーバーBで実行される

となっています。SSRFはサーバー側の権限を悪用する攻撃です。

サーバーをクライアント置き換え、クライアントが持つ権限を悪用するとCSRF(クロスサイト・リクエスト・フォージェリ – クライアントによるリクエスト詐称)になります。形は異りますが基本構造は同じです。

なぜマイクロサービスになるとSSRF問題が起きるのか?

マイクロサービスは複数の小さなWebサービスが連携して動作するアーキテクチャーです。従来は1つのアプリケーションの中にあった機能がWeb API化され処理されるようになります。

ざっくり言うとWebアプリをモジュール毎にWebアプリに変えて、それが1つのシステムとして動作するように作るのがマイクロサービスアーキテクチャーです。つまり、沢山のWebサーバーがあってそれらが連携して動くことになります。あるサーバーは別のサーバーにアクセス権限を持つ構造になります。この構造はSSRFが起きる構造です。

SSRFもCSRFもリクエストが真正(クライアント/サーバーが意図している事)であることをバリデーションすれば防止できます。マイクロサービスのSSRFの場合、CSRFのようにバリデーショントークンは必要ありません。入力パラメーターを全てバリデーション、出力パラメーターが妥当であることを保証すれば防止できます。

 要するにセキュアコーディング/セキュアプログラミング/ISO 27000で要求される入力バリデーションをしていれば対策できます。 ※ 全ての入力をバリデーションし、余計な入力もチェック&拒否、そしてビジネスロジックにバグが無いければSSRFはできない。

残念ながら一般的なWebアプリはURIのクエリ文字列などをマトモにバリデーションしていないアプリが大半です。バリデーションしていても使う変数だけチェックしているアプリが大半です。以下のようなことが起こっています。

  1. フロントエンドWebサーバーが攻撃者から”攻撃用クエリパラメーターを含むリクエスト”を受け入れる
  2. フロントエンドWebサーバーは”使うつもりの変数だけチェック”して、内部のWebサーバーに”攻撃用クエリパラメーターを含むリクエスト”を送信する
  3. 内部Webサーバーは”攻撃用クエリパラメーターを含むリクエスト”を処理してしまい攻撃される

このような形でマイクロサービスのシステムがSSRF攻撃を受けます。よく見るとXXE攻撃と同じ形で攻撃されると分かります。この例はクエリ文字列ですが、POSTを使った場合でも同様の問題が発生します。

「こんなのは当たり前だろ!」と思う開発者も少くないと思います。

しかし、マイクロサービス化に伴い、アプリ内部APIで検証済入力を受け取り処理するハズのそのままWeb API化してしまい、攻撃できてしまうシステムが山程できてしまったことが 2017年版OWASP TOP 10でA10 Insufficient Logging and Monitoring が追加された大きな理由の1つです。

そんなずさんな状態なので、

  • 内部のWebサーバーに”攻撃用クエリパラメーターを含むリクエスト”を送信する

となってしまい、SSRFに脆弱なシステムも量産されているようです。

セキュアコーディングを知っていればどうなったか?

 セキュアコーディングを知っていれば、フロントエンドWebサーバーで正しく入力バリデーションを行うはずです。セキュアコーディングの第一原則が「入力をバリデーションする」だからです。

そして、入力値を未検証のまま使ったりはしません。全てのクエリパラメーターをバリデーションするか、少なくとも検証済入力だけ内部Webサーバーに渡すはずです。SSRF問題は発生しないでしょう。

ISO 27000を知っている開発者ならより厳格なバリデーションをするはずです。ISO 27001:2012では「余計な入力」を含む不正入力は除外対象です。攻撃者の入力はフロントエンドWebサーバーで検出され、攻撃を阻止できます。

そもそも、攻撃用に余計な入力を設定してきた入力データ、である場合はたとえ「利用するデータ」がバリデーションして妥当であっても攻撃用の高リスクデータです。このような高リスクデータを内部Webサーバーに転送するのは危険な行為だと判ります。普通ならこのようなリクエストは排除し、攻撃用リクエストが在ったことを記録するでしょう。

「普通ならこのようなリクエストは排除し、攻撃用リクエストが在ったことを記録するでしょう。」と書きましたが、一般的なWebアプリはこのような動作になっていません、残念ながら。この為、A10 Insufficient Logging and Monitoring が追加されることとなりました。

A10は「ログ/モニタリングの脆弱性」ではありません。本当の目的は「入力バリデーションをしなさい、と言わずに徹底的に入力バリデーションをさせること」です。攻撃用ツールの入力を検出、ログ、対応するよう書かれていることから分かります。徹底した入力バリデーションがないと攻撃のログもモニタリングもできません。2017年版のOWASP TOP 10の策定に少しだけ関わり「いっそ、2004年版のOWASP TOP 10のように未検証入力を復活させたら?」とコメントしたところ「入力バリデーションをする”だけ”で十分と勘違いするユーザーが居るから危険でしょう」と返信がありました。2004年頃の当時からしばらく経っても、実際に勘違いしていた開発者も沢山居たようなので、今でもセキュアコーディングの基本概念が理解できていないなら勘違いするかも、です。

 セキュアコーディングの出力データ処理から考えてもSSRFを防止できます。セキュアコーディングには「出力を無害化する」原則もあるからです。

フロントエンドWebサーバーは内部Webサーバーに”無害なデータを送信する責任”があります。出力対策だけで無害であるリクエストを送信(出力)しようとすると、”リクエストを出力(送信)する部分では無害化は不可能”だと分かります。

  • もうこれからリクエストURIを作って内部サーバーにリクエストを送るぞ!

といった時点では、出力コードが持っているデータが無害であるかどうか?など判りません。なので入力処理とロジック処理でのバリデーションが必要だと分かります。

 セキュアコーディング原則を知って実践するだけでもかなり安全なアプリが作れるようになります。

  • 入力の安全性保証は基本的にアプリケーションの責任 (利用するライブラリなどの責任ではない。フェイルファースト原則。API/ライブラリに任せればよい、では安全にならない)
  • 出力の安全性保証は基本的にアプリケーションの責任 (出力するAPI/ライブラリや出力先の責任ではない。API/ライブラリを呼べばよい、出力先が”適当”に処理すべき、では安全にならない)

1つ1つのセキュアコーディング原則は簡単な概念ですが、最も重要な「入力をバリデーションする」を「セキュリティ対策ではない」などと勘違いしていると、脆弱なシステムを容易に作ってしまいます、このブログで紹介したSSRFに脆弱なマイクロサービスのように。

まとめ

セキュアコーディング/セキュアプログラミングは主なセキュリティ標準やガイドラインでは基礎的対策として要求されています。しかし、大半のソフトウェアが実装していない、大半の開発者が理解していない状態です。これはOSSのWebアプリケーションやフレームワークのほぼ全てがセキュアコーディング/セキュアプログラミングで要求する入力処理に対応していないことから判ります。

セキュアコーディング/セキュアプログラミングの基本(原則)は簡単なので理解さえすれば、直ぐに実践できます。 

ただし、プログラミングの一般基本原則であるフェイルファースト、リスク管理の一般基本原則であるゼロトラストを理解している必要があります。これらは基本的すぎて原則として言及されることはあまりないですが、原則であることは変わらず、とても重要です。理解&実践しないと必ずと言ってよいほど、おかしな事になります。

投稿者: yohgaki