GitHub | Secret scanning - Push protection の運用を考える
こんにちは、Thinkings 技術研究チームの石井です。
Thinkings では、sonar ATS の開発に GitHub Enterprise を採用しています。更に今年から GitHub Advanced Security の利用も開始しました。
GitHub Advanced Security には様々な機能が含まれているのですが、今回はその中にある Secret scanning の Push protection にスポットを当ててみます。Push protection は強力な機能ですが、その機能がどういったものかという説明が多い代わりにどう運用しているのかについてはあまり触れられていません。
この記事ではそういったあまり触れられていない運用について、どのように設計、運用しているかについて説明します。
GitHub Advanced Security とは?
GitHub Advanced Security(GHAS) は、GitHub が提供するセキュリティに関連する機能の総称です。GitHub で管理しているソフトウェアのリポジトリに対して、各種セキュリティに関する機能を利用できます。セキュリティと一言に言ってもその範囲は膨大なので、いくつかの機能に分割されています。
例えば、次のような機能が存在します。
-
Code scanning
リポジトリ内から様々な脆弱性やエラーを検知します。 -
Secret scanning
リポジトリ内からシークレットを検知したり、シークレットを含むコミットをブロックします。 -
Dependency review
リポジトリで使用しているパッケージについて、ライセンスや依存関係に関する情報を収集します。
GHAS は、Enterprise や Organization、リポジトリ単位で有効 or 無効にすることができます。
Secret scanning とは?
API キーやパスワードなどの機密情報(=シークレット)がリポジトリに追加された際に検知する機能です。シークレットがリポジトリ内に検知された場合、Security タブにアラートが作成され Enterprise 管理者やリポジトリオーナーに通知されます。
シークレットの検知条件をカスタマイズしたり、Git で push される際にシークレットが含まれていればブロックする機能などが含まれます。
Push protection とは?
シークレットをリポジトリ内に追加させないための機能です。Secret scanning はリポジトリ内にシークレットが追加された後に検出しますが、Push protection はリポジトリにシークレットが追加される前に検出します。
具体的には、Git で push した際にコミット内にシークレットが含まれていると次のようなメッセージを出力して push が失敗します。
Remote: Resolving deltas: 0% (0/1)
Remote: Resolving deltas: 100% (1/1)
Remote: Resolving deltas: 100% (1/1), completed with 1 local object.
Remote: Error: GH013: Repository rule violations found for refs/heads/master.
Remote:
Remote: - GITHUB PUSH PROTECTION
Remote: —————————————————————————————————————————
Remote: Resolve the following violations before pushing again
Remote:
Remote: - Push cannot contain secrets
Remote:
Remote:
Remote: (?) Learn how to resolve a blocked push
Remote:
https://docs.github.com/code-security/secret-scanning/working-with-secret-scanning-and-push-protection/working-with-push-protection-from-the-command-line#resolving-a-blocked-push
Remote:
Remote:
Remote: —— XXXXXXXXXXXXXXXXXXXXXX ————————————————
Remote: locations:
Remote: - commit: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Remote: path: example/Program.cs:8
Remote:
Remote: (?) To push, remove secret from commit(s) or follow this URL to allow the secret.
Remote:
https://github.com/ORGANIZATION/REPOSITORY/security/secret-scanning/unblock-secret/xxxxxxxxxxxxxxxxxxxxx
Remote:
Remote:
Remote:
e.g. Push protection によってブロックされた場合のメッセージ
ブロックされるとコミット内にあるシークレットを取り除かなくてはなりません。取り除くための手段は次の2つです。
- Removing a secret introduced by the latest commit on your branch
- Removing a secret introduced by an earlier commit on your branch
Bypass する
Push protection で、シークレットが検出されてブロックされた場合に Bypass するという選択も取れます。ここで言うBypass
とは、ブロックされた操作を回避して push できる状態にすることを指します。誤検知やテストで使用する(無効化された)シークレットが含まれている場合、Bypass するという手段も取れます。
Bypass は「誰でもできる」or「特定のメンバー or チームが承認する」のどちらかを選択できます。特定のメンバー or チームが承認する場合、開発者が Bypass 要求申請を送信して誰かが承認すると push できるようになります。
Bypass 要求申請を行う場合、次のような画面が表示されます。ここでシークレットを許可したい理由を3つの選択肢から選び、コメントを書いて承認を依頼します。理由の選択肢は次の3つです。
-
They're used in tests
テストで使用する -
This is a false positive
偽陽性である -
This will be fixed later
後で修正する
Bypass 要求申請ページ
Bypass 要求申請を行うと、承認可能なメンバーに対してメールが届きます。メールのリンクをクリックすると次の画面が表示されます。ここには次のような情報が表示されます。
- どのリポジトリのどのコミットをブロックしているか
- どのようなシークレットが検知されたか
- 誰から Bypass 要求申請があったか
- 要求申請時に選択した理由とコメント
Bypass 要求に対する選択画面
この画面の下部にあるボタンをクリックして Approve か Deny を選択します。Approve を選択すると、開発者は push できるようになります。Deny を選択すると、開発者はコミットからシークレットを取り除かなければなりません。
Push protection を使った運用フローを考える
前提知識の説明が長くなりましたが、ここから運用フローについて考えた結果を説明します。GHAS は有効化しておき、Secret scanning と Push protection についても有効化しているものとします。
前提条件
まずは、考慮すべきことやボトルネックがどこにあるのかを定義します。弊社では次のような条件を設定しました。
- Push protection の Bypass は専用の GitHub チームを作って、そこから承認を得ること
- Bypass 要求申請を承認可能なメンバーは、各ユニットから1名ずつ選出する
Push protection によってブロックされた場合、誰かの目を通さないと Bypass できないようにしました。これは意図せず、あるいは認識違いによりリポジトリにシークレットが含まれないようにするための条件です。安全策として考えるのであれば、この条件は必須と考えました。
次に、Bypass 要求申請を承認可能なメンバーを考える必要があります。このメンバーは、Engineering Manager(EM) や テックリードのような仕事量の多い人には割り当てないようにしました。もし割り当ててしまうと、忙しい EM やテックリードが開発のボトルネックになる可能性があるめです。このメンバーもユニット単位で選出することで、承認に必要なコミュニケーションを内部に閉じて開発速度をできるだけ下げないようにしました。
なお、「ユニット」とは弊社内で開発チームの最小単位の呼び方です。1ユニットあたり数名で開発しています。
運用フロー
前提条件をもとに運用フローを可視化しました。Push protection によってブロックされた際に、コミットを修正するのであれば開発者だけで解消可能です。そのため、ここには Push protection で Bypass 要求申請が必要な場合のフローを定義しています。基本的に Push protection で Bypass 要求申請の承認を必要とするのであれば、このフローになるはずです。
設計した運用フローの全体
開発者はコードの修正を行うメンバーです。管理者はここで言う「Bypass 要求申請を承認できるチームのメンバー」です。
管理者の操作は「承認(Approve)」か「拒否(Deny)」のどちらかしかありません。そのため、このような簡単なフローになっています。しかし、実際には Bypass 要求を判断する部分についても考慮する必要があります。
承認するために必要なこと
管理者が Bypass 要求申請をどうやって判断するべきかという観点を設定しました。この観点には、まず次の条件を含めました。
- Bypass 理由の選択肢のうち、
This will be fixed later
は常に拒否とする
上記の「後で修正する」なのでリポジトリにシークレットが含まれないようにするための条件が満たせなくなります。修正するタイミングは後ではなく今なので、この条件を加えました。
他には次の条件を含めました。
- マネージドサービスでシークレットを管理できるかどうか
- シークレットの値が漏洩しても問題ないと判断できること
sonar ATS の開発では、Azure Key Vault でシークレットを管理しています。このようなサービスを使用してシークレットを管理できるかどうかを観点に含めることで、すでに存在する運用の考慮漏れを防ぐことが狙いです。
他にもテストで使用する値や誤検知のような場合は、漏洩しても問題ないと判断できれば承認しても良いことにしました。漏洩しても問題ないとは、例えば検知された値がシークレットではない(=偽陽性)やシークレットそのものが無効化されている場合などを指します。この「問題ない」という基準は曖昧にしていますが、コミュニケーションを取ることで管理者が判断するための手段として設定しました。
定期メンテナンスの一時的なアサイン
もうひとつ、例外的な運用パターンがあります。それが定期メンテナンスです。sonar ATS では、月に一度メンテナンスを実施しています。その際にもコードの修正作業が発生する可能性があります。
メンテナンス中に Push protection が検出され、更に Bypass 要求申請が必要になるケースはゼロではありません。そのため、Bypass 要求を承認するために少なくとも1人の管理者が参加している必要があります。しかし、メンテナンスに参加するメンバーは不定であるため、作業中に管理者がいるかどうかはわからないという問題がありました。
この対策方針は「一時的に定期メンテナンス参加者を管理者に加える」としました。ポイントは「一時的に」という部分です。権限を付与する場合、制限時間を設けてその時間を超えると削除します。
定期メンテナンス作業の中には特定のクラウドリソースなどへのアクセスが必要になるため、事前に権限申請を行います。この権限は Microsoft Entra ID のグループで行っています。このグループにメンバーが追加されると、GitHub の管理者チームに追加します。これで誰が定期メンテナンスに参加しても、必ず参加者は管理者の権限が付与されます。また、この作業は忘れないためにワークフローを自動化しています。
定期メンテナンス終了後、メンテナンスメンバーは管理者から除外します。これで、不用意に Bypass 要求申請が承認される可能性を下げられます。
運用フロー設計のためにやったこと
運用フローを設計するに当たり、とりあえずこれはやっておく必要があることをリストアップしました。基本的には動作の検証と情報共有だけです。
検証用のリポジトリを作って運用フローを試す
運用フローを設計する前に、そもそも Push protection がどういった振る舞いなのか検証しました。GitHub にもドキュメントはありますが、実際に動かさないとわからない部分も数多くありました。今回のケースでは Bypass 要求申請するため、承認 or 拒否したあとはどういった情報が画面に表示されるかや通知される情報にはどういったものがあるのかなどを確認しました。
Push protection で Bypass する場合、1人ではできないので他の人にお願いして操作してもらいました。実際にはフローの設計をしながら何度か検証作業に戻りました。
手間はかかりますが運用フローを設計するためには必要な作業です。
運用フローを建付けて資料に落とし込む
検証がある程度できたら、運用フローを設計します。運用フローを考える際の前提条件をざっくり決めながらチームメンバーと相談しつつ設計しました。
ある程度設計できたら資料に落とし込みます。ここで作成する資料は開発者全員が参照するため、できるだけ初学者でもわかるように解説を多めにしました。また、検索性を高めるためにできるだけ実際のメッセージを記述するようにしました。例えば、push をブロックした際のブロックメッセージは、検証で出力されたものをそのまま貼り付けています。これは困った時にメッセージで検索すれば資料にたどり着くための導線を設定するためです。
Bypass 申請要求の管理者に対して説明会の実施
ある程度運用フローの管制が見えたら管理者を決定します。前提条件の通り、各ユニットから担当者を選んでもらい説明会を実施しました。説明会ではなぜ GHAS が必要なのか?なぜ Secret scanning で Push protection を使うのか?といった目的から説明して、やってほしいことや Bypass 承認する際の観点などを共有しました。
すべての開発者に対して説明と資料の共有
説明会でフィードバックを受けてから資料の調整をしたら、すべての開発者に資料の共有をします。資料だけでは伝わりづらいので、簡単に説明も加えました。実際の運用で迷った際に私がフォローできることも伝えました。すべての開発者に対してフォローするのは大変ですが、Push protection については頻度が低いことが予想できたのでこのようにしました。
まとめ
強力な機能が豊富な GHAS ですが、ではこれを導入すればセキュリティが強化できるかといえばそうではありません。それぞれの機能に対して適切な知識の習得と運用設計が必要です。今回は Push protection にスポットを当てましたが、他の機能も同様に運用を考える必要があります。特に開発メンバーに対する情報共有は必要不可欠なので、多少時間がかかってもやっておきたいですね。なお、幸か不幸か今のところ Bypass 要求申請はありません。事前の準備をやったとはいえ、欲を言えば実際に開発中に対応する経験もあってほしいところです。
Thinkings の技術研究チームでは製品の技術的負債の解消を目的として、このように特定の技術にフォーカスした検証や運用設計などを継続的に実施しています。過去にも CI 実行時間の短縮や自動テストの実行時間の分析と最適化、スクラム開発で T シャツサイズ見積もりを取り入れる改善なども行ってきました。もしこのような活動に興味があれば以下の採用情報ページからお気軽にご応募ください!
Discussion