fluent-plugin-secure-forwardの脆弱性と匿名TLSの話

先日fluentdのサードパーティプラグインfluent-plugin-secure-forward言及した件について、更新v0.3.2が公開されています。

何かの間違いでfluent-plugin-secure-forwardを実運用に供してしまった方は、なるべく早く更新することをお勧めします。

脆弱性の内容と経緯

以下は、v0.3.2時点では修正済みとされている、v0.2.6*1当時、私が把握していた脆弱性について記載しています。

TLSの証明書検証不備

README.mdでの記述に反して、実際のコードはTLSクライアント側にリモート証明書検証コードが実装されていませんでした。

TLSに対するよく知られた攻撃手法として、証明書検証の不備を突いた中間者攻撃( 中間者攻撃 - Wikipedia) がありますが、このプラグインはそれを許容する前提で設計され、事実上TLSを認証のない匿名TLSとしてのみ利用していました。

このプラグインには匿名TLSの上に独自の認証機構をもち、shared_keyと呼ばれる共有鍵のハッシュを用いてクライントとサーバーを相互に認証することになっていましたが、ログ(通信するメッセージ)は全く暗号化されていませんでした。

前述のようにTLSの中間者攻撃が可能なため、shared_keyを鍵にしてログを(TLSの上でさらに)暗号化しないと、中間者はログを取得・改竄することができてしまします。

独自の認証手法の問題

独自に実装された認証手法自体にも問題がありました。

認証の中で、クライアントはサーバーへshared_key_saltと呼ばれているnonceを送りますが、このnonceは双方向の認証で使い回されておりnonceとして機能しておらず、かつ認証レスポンスを正しく検証していなかったため、偽のサーバーは正規のサーバー無しに(中間者攻撃なしに)クライアントからログを収集できました。.

また、サーバーはデフォルトではクライアントへnonceを送らないため、クライアントの認証はリプレイ攻撃が可能でした。 これは過去に一度中間者攻撃に成功した攻撃者に、サーバーへの恒久的なアクセス、偽造ログの挿入を許してしまいます。

セキュリティ設計の問題

平文のTCP上でTLSが実現しているように、極論すれば匿名のTLS上でセキュアな通信は不可能ではありません。

実際、このプラグインは匿名のTLS上で、TLSに頼らないセキュアな通信プロトコルを実現しようとしていたものと思われます。

しかし、事前共有鍵一つでセキュアな通信を実現するには安全な事前共有鍵認証と安全な鍵共有を独自実装し、メッセージを暗号化することが必須で、その実装は簡単とは言えません。

一方で、これらは既にTLSにPre-Shared-Key方式(TLS_PSK_*)として定義され実装済みのものです。

$ openssl ciphers -v aPSK
PSK-AES256-CBC-SHA      SSLv3 Kx=PSK      Au=PSK  Enc=AES(256)  Mac=SHA1
PSK-AES128-CBC-SHA      SSLv3 Kx=PSK      Au=PSK  Enc=AES(128)  Mac=SHA1
PSK-3DES-EDE-CBC-SHA    SSLv3 Kx=PSK      Au=PSK  Enc=3DES(168) Mac=SHA1
PSK-RC4-SHA             SSLv3 Kx=PSK      Au=PSK  Enc=RC4(128)  Mac=SHA1

報告と改修の経緯

まず、私はコードにコミットしておらず、改修は全て開発者の@tagomris氏(と助言されたおそらくTreasure dataの中の人)の尽力による功績です。

v0.2.6当時、最初私は中間者攻撃可能な証明書検証不備について、開発者の方に問い合わせましたが、証明書の有効性を検証をしていない実装は意図的なものであり、変更は難しい、とのことでした。

セキュリティに対して前提条件や要求される安全性には様々なバリエーションがあります。しかし、中間者攻撃に対して漏洩・改竄を許容するメンテナのセキュリティポリシーは、残念ながら私の必要とは合致せず、修正コードをコミットすることもありませんでした。

このプラグインはAPLでライセンスされており、ユーザーは無償で利用できる一方で開発者に文句を言う筋合いはありません。またコミットもせず横槍を入れるような指摘も失礼だったかと思います。

しかし、このプラグインはfluentdの公式サイトから、半ば推奨される形でリンクされてしまっていたため、いくらか実運用した利用者がいたものと思われます。(公開以前は100kダウンロード程度でした) fluentdはログ配送サーバーであり、万が一secure_forwardが利用されていたら、無関係のユーザーの情報が危険に晒される事になります。そのため、このエントリの公開を準備していました。

その後、v0.3.0でクライアント側にリモート証明書の検証機構が追加され、v0.3.2でリプレイ対策のサーバーnonceが導入されたようです。 少なくとも開発者の周囲の方、おそらくTreasure Data社の中の方に、証明書検証不備が脆弱性であると認識している方がいる、という点は安心材料かもしれません。

これでひとまず、『使うな』から『注意して使う』にできるものと思います。 

さいごに

TLSのリモート証明書検証不備・匿名TLSの利用は、少なくともWebの領域では明白かつ重大な脆弱性と見なされています。

一方で、独自プロトコルTLSで保護しているように見せながら、実際には適切な証明書検証をしていないアプリケーションが数多く存在するというレポートもあります。

TLSは(ニア)ゼロコンフィグレーションで安全性を確保してくれる魔法ではありません。 TLSを安全に使うには最低限、

  • 公開鍵基盤発行の証明書と有効範囲(ドメイン名)検査
  • プライベート認証局による証明書の自己管理

が必要です。このエントリが、TLSの誤用を少しでも減らすことになればと願います。