Certificate Transparencyの紹介

この記事は[Kyash Advent Calendar 2019](https://adventar.org/calendars/4359)の22日目の記事です。
最近自分が調べたCertificate Transparencyという証明書に関連する仕様について、業務でも使えそうだなーと思ったので、簡単な仕組みと活用方法の仕組みを紹介します。
([2019/11/14に開催した弊社のMeetup](https://kyash.connpass.com/event/152097/)でも少し紹介しました)

## Certificate Transparencyとは?

簡単にいうと、世界中で日々発行される証明書を誰でも確認できるようにするための仕様です。
皆さんが証明書を発行する際、ほとんどの場合発行を認証局(CA)に依頼するかと思いますが、このCAがCertificate Transparencyに準拠している場合、CAは**ログサーバー**と呼ばれるサーバーに事前証明書を登録します。
そしてこのログサーバーには誰でもアクセスすることができ、どのような事前証明書が登録されているかを調べることができるというのが、このCertificate Transparencyです。

## どう使う？

実は最近、[弊社Kyashを騙るフィッシングサイトが開設されている事例](https://news.kyash.co/post/189365456782/20191129)が確認されました。
これはアイデアの1つですが、Certificate Transparencyを活用すれば不審なドメインに対する証明書の発行を検知して、このようなフィッシングサイトの開設の早期検知に使えそうだなーというのを最近は考えたりしています。

## 具体的にどうやる?

折角なので、ここからは具体的にどのようにしてログサーバーにアクセスし、そこから提供されるデータを活用するのかというのを簡単に紹介しておこうと思います。
かなりざっくりとした説明になりますが、雰囲気だけでも伝われば幸いです。
ちなみにCertificate Transparencyの仕様は[RFC 6962](https://tools.ietf.org/html/rfc6962)としてまとめられており、以降に紹介するログサーバーへのアクセス方法もSection 4にまとめられています。
興味が湧いた方はぜひそちらも参照してみてください。

### ログサーバーはどこにあるの?

ログサーバーは単一ではなく複数存在しているんですが、そのうちの1つに`Google 'Argon2019' log`というログサーバーがあります。URLは`https://ct.googleapis.com/logs/argon2019/`で、このパスにさらに仕様で定められたパスを追加することでログサーバーの各種データにアクセスすることができます。

ちなみに、ログサーバーについては以下のページである程度まとまった情報が手に入ります。

 * http://www.certificate-transparency.org/known-logs

### 登録済みの事前証明書の数を確認する

というわけで、まずはログサーバーからそこに登録されている事前証明書の総数を取得してみましょう。
先ほど紹介したログサーバーのURLのパスに`/ct/v1/get-sth`というパスを追加した、 `https://ct.googleapis.com/logs/argon2019/ct/v1/get-sth`というURLにアクセスすることで事前証明書の総数を取得することができます。curlで叩いてみましょう。

```bash
$ curl https://ct.googleapis.com/logs/argon2019/ct/v1/get-sth | jq .
{
  "tree_size": 857115415,
  "timestamp": 1577009638601,
  "sha256_root_hash": "aq5lNyne6AlIFhzTMgARIQKn5MTY5fbqv1CYgCx4N6g=",
  "tree_head_signature": "BAMARzBFAiEAl8/BOCxX/i/1AHMdzVrz3FOXjoY0xaCVfcQ/uFA+VWsCIEscHFz9bfkF1SVVYL+uTLc2r58SpLVoiGFt41kfqJE8"
}
```

`tree_size`キーに対応する値がログサーバーに登録されている事前証明書の総数です。
例えば定期的にこのURLにアクセスし、その総数の増加量をチェックすることで、一定期間内にどれだけの証明書が発行されたのかというのを検知することができます。

### 事前証明書を取得する

単に事前証明書の総数を取得するだけではあまり有用なアイデアを思いつかないので、
具体的な事前証明書を取得する方法も見てみましょう。

ログサーバーに登録される全ての事前証明書には`0`を始点としたインデックスが割り当てられています。例えば一番最初に登録された事前証明書にはインデックス`0`、先ほどcurlで総数を取得した時点の最新の事前証明書のインデックスは`857115414`、という感じです。
そしてログサーバーは、このインデックスによって指定された事前証明書の内容を返却する機能も提供することになっています。

事前証明書の内容を取得するためのURLは、ログサーバーのURLのパスに`/ct/v1/get-entries`を追加した `https://ct.googleapis.com/logs/argon2019/ct/v1/get-entries`というURLになり、インデックスはクエリ文字列で範囲指定することになっています。
クエリ文字列での指定は、単に取得対象の事前証明書の始点となるインデックスを`start`として、終点となるインデックスを`end`として与えます。
つまり、先ほどcurlで事前証明書を取得した時点での最新の事前証明書3つは `https://ct.googleapis.com/logs/argon2019//ct/v1/get-entries?start=857115412&end=857115414`のようなURLでアクセスできます。curlで取得してみましょう。

```bash
$ curl "https://ct.googleapis.com/logs/argon2019/ct/v1/get-entries?start=857115412&end=857115414" | jq .
{
  "entries": [
    {
      "leaf_input": "AAAAAAFvLRk+cQABYZP...",
      "extra_data": "AAT/MIIE+zCCA+OgAwI..."
    },
    {
      "leaf_input": "AAAAAAFvLRlR+gABYZP...",
      "extra_data": "AAT/MIIE+zCCA+OgAwI..."
    },
    {
      "leaf_input": "AAAAAAFvLRll4QABYZP...",
      "extra_data": "AAT/MIIE+zCCA+OgAwI..."
    }
  ]
}
```

3つある`leaf_input`キーに対応する文字列が、事前証明書を表すデータになります。
ちなみにこの事前証明書を表すデータのデコードを全て自前で実装しするのは結構骨が折れます。
この辺りは、例えばGoであればこの辺りの面倒を見てくれるパッケージが公開されています。

 * https://github.com/google/certificate-transparency-go

あまり詳しく調べていませんが、PythonやJavaのための実装ものもあるようです。

 * https://github.com/google/certificate-transparency

ここで得られたデータを各種処理系で扱いやすい形にデコードすることができれば、
例えば事前証明書がどのドメインに対して発行されたものかを調べたりすることができるようになります。
この記事の最初の方に挙げた、「フィッシングサイトの開設の早期検知に使えるかも」というのもできそうな気がしてきますよね。

## さいごに

以上、簡単なCertificate Transparencyの解説と、実際にどのようなことができそうかをイメージしてもらうためのデータ等の紹介でした。
ここで紹介した内容はかなり細部を端折って紹介しているため、興味が湧いた方はぜひご自身で細部を調べてみてくださいね(と言っても詳細な解説等はRFCくらいな印象ですが...)