CIでAWSキー等認証情報の漏洩を検知する

AWSキーのような認証情報を誤ってcommitしてしまうミスはやってしまいがちです。[「AWS 不正利用」でQiita検索](https://qiita.com/search?q=AWS+%E4%B8%8D%E6%AD%A3%E5%88%A9%E7%94%A8)するだけでも、いくつか事例が見つかります。

対策としては、[git-secrets](https://github.com/awslabs/git-secrets)が有名かと思います。これをインストールすると、git commitのタイミングで認証情報がコミットに含まれていないかチェックしてくれます。ただ個々人の環境にインストールすることを前提としたツールなので、インストール忘れや設定の共有に関してやや面倒さがあります。

この記事ではCI時にミスを検知する方法として、gitleaksというツールを使用する方法とCIサービスを使用する方法を紹介します。

# Gitleaksを使う方法

[gitleaks](https://github.com/zricethezav/gitleaks)は、レポジトリの全コミットを遡って認証情報が含まれていないかチェックしてくれるツールです。以下のように、`go get`で簡単にインストールできます。

```sh
go get -u github.com/zricethezav/gitleaks
```

また、[Windows/Mac/Linuxに対応したリリースビルド](https://github.com/zricethezav/gitleaks/releases)や[Dockerイメージ](https://github.com/zricethezav/gitleaks#run-me-with-docker)も提供されているため、割と簡単にCIに組み込めます。

試しに以下のようなAWSキー風の文字列を含むファイルを作成し、レポジトリにコミットしてみます。

```sh
% cat awskey.sh
#!/bin/sh

aws_key="AKIAAKIAAKIAAKIAAKIA"
echo "${aws_key}"

```

gitleaksを実行してみます。

```sh
% gitleaks -v
2018/05/21 11:28:28 fetching gitleaks-example from /Users/yagami/go/src/github.com/ks888/gitleaks-example ...
{
   "line": " aws_key=\"AKIAAKIAAKIAAKIAAKIA\"",
   "commit": "dbd504c74099290e458c076f3e9dbc2e189a524f",
   "string": "AKIAAKIAAKIAAKIAAKIA",
   "reason": "AWS",
   "commitMsg": "Add empty line",
   "time": "2018-05-21 11:28:26 +0900",
   "author": "Kishin Yagami",
   "file": "awskey.sh",
   "repoURL": ""
}
{
   "line": " aws_key=\"AKIAAKIAAKIAAKIAAKIA\"",
   "commit": "bd68ca87183f982649691447bdad69cddaf68eed",
   "string": "AKIAAKIAAKIAAKIAAKIA",
   "reason": "AWS",
   "commitMsg": "Add invalid api key to test gitleaks",
   "time": "2018-05-21 11:15:24 +0900",
   "author": "Kishin Yagami",
   "file": "awskey.sh",
   "repoURL": ""
}
```

想定通り、AWSキー風の文字列を検出してくれました。

gitleaksで面白いのは、false positiveを減らすためにエントロピーを利用しているところです。特定の文字列にマッチして、かつ、エントロピーがしきい値を超えた場合のみ警告を出すことができます。エントロピーってなんだっけ？という感じですが、「nビットのビット列の空間（情報源）から（一様ランダムとは限らない方法で）ランダムにビット列を選んだときのエントロピーは、n以下になる。エントロピーがnになる必要十分条件は、ビット列が一様ランダムに選ばれることである。」という性質（[wikipediaより](https://ja.wikipedia.org/wiki/%E6%83%85%E5%A0%B1%E9%87%8F#%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AD%E3%83%94%E3%83%BC%E3%81%AE%E5%9F%BA%E6%9C%AC%E7%9A%84%E6%80%A7%E8%B3%AA)）を使っているのかなと思いました。

この機能も試してみます。先程と同じく、AWSキー風の文字列を含むファイルを使います。よく見ると、aws_key変数の値が`AKIAAKIAAKIAAKIAAKIA`になっています。明らかに文字の選び方がランダムではないため、エントロピーは低くなりそうです。

```sh
% cat awskey.sh
#!/bin/sh

aws_key="AKIAAKIAAKIAAKIAAKIA"
echo "${aws_key}"

```

デフォルトではエントロピーによるチェックは無効になっているので、`--entropy`オプションを付けて実行します。

```sh
% gitleaks -v --entropy
2018/05/21 11:50:56 fetching gitleaks-example from /Users/yagami/go/src/github.com/ks888/gitleaks-example ...
2018/05/21 11:50:58 cannot fetch  from /Users/yagami/go/src/github.com/ks888/gitleaks-example
```

先程の問題が検出されなくなりました！テスト文字列の誤検出を防ぐのに役立ちそうです。False negativeの心配もありますが、しきい値はオプションで変えられるので調整できそうです。

# CIサービス (Inspecode) を使う方法

*Disclaimer: 僕はInspecodeの開発に関わっています。こういう使い方もあるかなーと思いついたので書いてみました。*

[Inspecode](https://rocro.com/inspecode)は、CodeClimateやSideCIのようなGitHub連携の静的コード解析サービスです。Golint、Checkstyle、ESLintなどの[各種静的解析ツールに対応](https://inspecode.rocro.com/help/tools.html)しているのですが、ひっそりとgrepにも対応しています。今回はこのgrepを使い、認証情報を示す正規表現パターンにマッチしたらCIビルドを失敗させるようにしてみます。サインインやレポジトリの登録といった初期作業は割愛しますので、必要であれば[ヘルプ](https://inspecode.rocro.com/help/)を参照して下さい。

今回は以下のような設定ファイル`rocro.yml`を作成しておきます。正規表現パターンとgrepオプションはgit-secretsが使用しているものを参考にしました。

```yaml
inspecode:
  grep:
  - options:
      --extended-regexp:
      -I:
      --regexp:
        - AKIA[A-Z0-9]{16}
        - ("|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)("|')?\s*(:|=>|=)\s*("|')?[A-Za-z0-9/\+=]{40}("|')?
      --word-regexp:
    thresholds:
      num-issues: 0
```

以下のような認証情報を含むファイルをcommit、pushして、CIジョブを実行します。

```sh
#!/bin/sh

aws_key="AKIAAKIAAKIAAKIAAKIA"
echo "${aws_key}"
aws_secret_key="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
echo "${aws_secret_key}"
```

以下がジョブ実行結果です。無事にAWSキーとシークレットキーを検出し、ジョブが失敗しました。

<img width="1044" alt="Screen Shot 2018-05-18 at 18.55.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/118939/0e0dd608-2467-5403-2f64-da0cded9789c.png">

# まとめ

CIの一環として認証情報の漏洩を検知する方法をいくつか紹介しました。他にも良い方法があればぜひ教えてください。