バグ修正や新機能の追加だけでなく、プロジェクトが長く存続していくためには、その有用性だけではなく、利用者から得られる信頼も重要です。この信頼を維持するためには、強固なセキュリティ対策が欠かせません。ここでは、プロジェクトのセキュリティを大きく向上させるために実践できる重要な取り組みを紹介します。
プロジェクトへの特権アクセスを持つすべてのコントリビューターが、多要素認証(MFA)を有効にしていることを確認する
プロジェクトの特権アクセスを持つコントリビューターになりすました悪意のある攻撃者によって、プロジェクトに深刻な被害がもたらされる可能性があります。
ひとたびこの攻撃者が特権アクセスを取得すると、コードを改変して意図しない動作(例:暗号資産のマイニング)を実行させたり、ユーザーのインフラストラクチャにマルウェアを配布したり、非公開のコードリポジトリへアクセスして、他のサービスへの認証情報を含む知的財産や機密データを窃取したりする可能性があります。
MFA(多要素認証)は、アカウント乗っ取りに対する追加のセキュリティ層を提供します。MFAを有効にすると、ユーザー名とパスワードによるログインに加えて、自分だけが知っている、またはアクセスできる別の認証情報を提供する必要があります。
開発ワークフローの一環としてコードを安全性を確保する
コードの脆弱性は、実際の利用環境で悪用されてから修正するよりも、開発プロセスの早い段階で発見・修正する方が、コストを抑えられます。
静的アプリケーション・セキュリティ・テスト(SAST)ツールを使用して、コード内のセキュリティ脆弱性を検出しましょう。これらのツールはコードレベルで動作し、実行環境を必要としないため、開発プロセスの早い段階で実行できます。また、開発中やコードレビューの段階など、通常の開発ワークフローにシームレスに統合することができます。
これは、熟練した専門家がコードリポジトリをレビューしてくれるようなもので、開発中には見落としがちな一般的なセキュリティ脆弱性の発見の手助けをします。
SASTツールの選び方は、まず ライセンスを確認する: 一部のツールは、オープンソースプロジェクト向けに無料で利用できます。例えば、GitHub CodeQLやSemgrepなどがあります。 そして対応している言語の範囲を確認する。
- すでに使用しているツールや既存のプロセスに簡単に統合できるものを選びましょう。例えば、脆弱性のアラートを確認するために別のツールへ移動するよりも、普段利用しているコードレビューのプロセスやツール内で確認できる方が望ましいです。
- False Positive(誤検知)に注意しましょう。理由もなく開発作業を遅らせるようなツールは避けたいものです。
- 機能を確認しましょう。一部のツールは非常に強力で、taint tracking(汚染追跡)を実行できます(例: GitHub CodeQL)。また、AIによる修正提案を提供するものや、カスタムクエリを簡単に作成できるものもあります(例: Semgrep)。
シークレットを共有しない
APIキー、トークン、パスワードなどの機密データは、誤ってリポジトリにコミットされてしまうことがあります。
このような状況を想像してみてください。あなたは、世界中の開発者から貢献を受けている人気のオープンソースプロジェクトのメンテナーです。ある日、あるコントリビューターが、第三者サービスのAPIキーを誤ってリポジトリにコミットしてしまいました。数日後、誰かがそのキーを発見し、不正にサービスへアクセスするために利用します。その結果、サービスが侵害され、あなたのプロジェクトの利用者はサービス停止の影響を受け、プロジェクトの評判にも悪影響が及びます。メンテナーであるあなたは、漏洩したシークレットの無効化、攻撃者がそのシークレットを利用して実行できた可能性のある悪意ある操作の調査、影響を受けたユーザーへの通知、そして修正対応の実施という困難な作業に直面することになります。
このような問題を防ぐために、コード内のシークレットを検出する「シークレットスキャン」ソリューションがあります。GitHub Secret ScanningやTruffle SecurityのTrufflehogのようなツールの中には、そもそもシークレットがリモートブランチへプッシュされることを防止できるものがあります。また、一部のツールはシークレットを自動的に無効化してくれる機能も提供しています。
依存関係を確認し、最新の状態に保つ
プロジェクトで利用している依存パッケージには、セキュリティ上のリスクとなる脆弱性が含まれている可能性があります。依存関係を手動で管理し、常に最新の状態に保つことは、多くの時間と手間がかかります。
あるプロジェクトが、広く利用されているライブラリに依存して構築されている状況を考えてみましょう。そのライブラリに重大なセキュリティ問題が発見されましたが、それを利用してアプリケーションを構築した開発者はその問題を把握していませんでした。攻撃者がこの脆弱性を悪用して侵入し、機密性の高いユーザーデータを取得したことで、重要な情報が危険にさらされることになります。これは単なる仮定の話ではありません。実際に2017年のEquifaxの事例で発生しました。重大な脆弱性が発見されたという通知を受けた後も、EquifaxはApache Strutsの依存関係を更新しませんでした。その結果、この脆弱性は悪用され、悪名高いEquifaxの情報漏洩事件によって1億4,400万人分のユーザーデータが影響を受けました。
このような状況を防ぐために、DependabotやRenovateなどのSoftware Composition Analysis(SCA)ツールは、NVDやGitHub Advisory Databaseなどの公開データベースに登録されている既知の脆弱性をもとに、依存関係を自動的にチェックします。そして、安全なバージョンへ更新するためのプルリクエストを作成します。最新の安全な依存バージョンを維持することで、プロジェクトを潜在的なリスクから保護できます。
オープンソースライセンスのリスクを理解し、管理する
オープンソースライセンスには利用条件があり、それらを無視すると法的リスクや評判へのリスクにつながる可能性があります。
オープンソースの依存関係を利用することで開発を加速できますが、各パッケージには、その使用、変更、または配布方法を定めるライセンスが含まれています。一部のライセンスは制約が少なく利用しやすいものですが、AGPLやSSPLのように、プロジェクトの目的や利用者の要件と合わない可能性のある制約を含むライセンスもあります。
このような状況を想像してみてください。あなたは、強力なライブラリをプロジェクトに導入しましたが、そのライブラリが厳しい制約を持つライセンス下で提供されている事に気付いていませんでした。その後、ある企業があなたのプロジェクトの利用を検討しましたが、ライセンスの遵守について懸念を示しました。その結果、採用には至らず、コードの大幅な修正が必要になり、プロジェクトの評判にも悪影響が及ぶことになります。
このような問題を避けるために、開発ワークフローの一環として自動ライセンスチェックシステムの導入を検討することをお勧めします。これらのチェックにより、互換性のないライセンスを早い段階で特定でき、問題のある依存関係がプロジェクトに導入されることを防ぐことができます。
もう一つの有効なアプローチは、Software Bill of Materials(SBOM)を生成することです。SBOMには、すべてのコンポーネントとそのメタデータ(ライセンス情報を含む)が標準化された形式で一覧化されています。これにより、ソフトウェアサプライチェーンを明確に把握でき、ライセンスに関するリスクを事前に発見するのに役立ちます。
セキュリティ脆弱性と同様に、ライセンスに関する問題も早期に発見するほど修正が容易になります。このプロセスを自動化することで、プロジェクトを健全で安全な状態に維持できます。
保護されたブランチで意図しない変更を防ぐ
重要なブランチへの制限のないアクセスは、誤操作や悪意のある変更を招き、セキュリティリスクやプロジェクトの安定性低下につながる可能性があります。
新しいコントリビューターにメインブランチへの書き込み権限が付与されたとします。そのコントリビューターが、十分にテストされていない変更を誤ってプッシュしてしまった場合、重大なセキュリティ問題につながる可能性があります。このような問題を防ぐために、ブランチ保護ルールを設定すると、重要なブランチへ変更をプッシュまたはマージする前に、レビューの実施や指定されたステータスチェックの通過を必須にする事ができます。この追加対策により、より安全な開発プロセスを実現し、プロジェクトの品質と安定性を維持する事ができます。
セキュリティ問題を簡単かつ安全に報告できるようにする
ユーザーがバグを報告しやすい環境を整えることは重要です。しかし、そのバグがセキュリティに関わる場合、重要なのは脆弱性の情報を攻撃者に知られることなく、安全に報告できる仕組みをどのように用意するかという点です。
このような状況を想像してみてください。あるセキュリティ研究者があなたのプロジェクトの脆弱性を発見しましたが、それを報告するための明確で安全な方法が用意されていませんでした。適切な報告プロセスがない場合、その研究者はイシューを作成したり、ソーシャルメディア上で問題について公に議論したりする可能性があります。たとえ善意で修正案を提供しようとしていたとしても、プルリクエストで対応した場合、マージされる前に他の人から内容が見えてしまいます。公開することによって、対応する機会を得る前に脆弱性が悪意のある攻撃者に知られてしまい、ゼロデイ攻撃につながる可能性があります。その結果、あなたのプロジェクトやユーザーが被害を受けることになります。
セキュリティポリシー
このような事態を防ぐために、セキュリティポリシーを公開しましょう。SECURITY.mdファイルで定義されるセキュリティポリシーには、セキュリティ上の懸念事項を報告する手順を記載します。これにより、脆弱性開示(Coordinated Disclosure)のための透明性のあるプロセスを整備し、報告された問題に対応するプロジェクトチームの責任を明確にできます。セキュリティポリシーは、「脆弱性の詳細は、一般公開されているイシューやプルリクエストには投稿しないでください。代わりに、security@example.com宛てにメールでご連絡ください。」といった簡潔な内容でも構いません。また、報告後どのくらいで返信を受けられるかなど、報告者にとって有益な情報を記載するとよいでしょう。脆弱性の開示プロセスを円滑かつ効率的に進めるために役立つ情報であれば、積極的に盛り込むことをおすすめします。
非公開での脆弱性報告
一部のプラットフォームでは、非公開のIssueを利用することで、脆弱性報告の受付から情報公開までの管理プロセスを、より効率的かつ安全に進めることができます。GitLabでは非公開版のイシューを利用できます。GitHubでは、この機能はPrivate Vulnerability Reporting(PVR)と呼ばれています。PVRを利用すると、メンテナーは GitHub上で脆弱性の報告を受け取り、そのまま対応を進めることができます。修正作業のためのプライベートフォークや、セキュリティアドバイザリーのドラフトもGitHubが自動的に作成します。脆弱性の公開および修正版のリリースを行うまで、報告内容や修正作業に関する情報はすべて非公開で保たれます。公開後はセキュリティアドバイザリが発行され、SCAツールを通じてユーザーへ通知されることで、利用者の保護にもつながります。
脅威モデルを定義し、ユーザーや研究者が報告対象を理解できるようにする
セキュリティ研究者が効果的に問題を報告するためには、どのようなリスクが対象範囲に含まれるのかを理解する必要があります。簡易的な脅威モデルを作成することで、プロジェクトの範囲、想定される動作、前提条件を明確にできます。
脅威モデルは、複雑である必要はありません。プロジェクトが何を行うものなのか、何を信頼しているのか、どのように悪用される可能性があるのかを整理した簡単なドキュメントでも、大きな効果があります。また、メンテナー自身が潜在的な問題点や、依存している外部ライブラリやパッケージから引き継ぐリスクについて検討する助けにもなります。
良い例として、Node.js の脅威モデルがあります。この脅威モデルでは、プロジェクトのコンテクストに沿って何が脆弱性と見なされ、何が該当しないのかを明確に定義しています。
脅威モデルの作成が初めての場合は、OWASP Threat Modeling Process が、脅威モデルを作成するための有用な入門用資料になります。
基本的な脅威モデルをセキュリティポリシーと併せて公開することで、ユーザーや研究者を含む関係者にとって、プロジェクトのセキュリティ方針がより明確になります。
簡易的なインシデント対応プロセスを準備する
基本的なインシデント対応計画を用意しておくことで、冷静に対応し、効率的に行動できるようになります。その結果、ユーザーや利用者の安全を守ることにつながります。
多くの脆弱性は、研究者によって発見され、非公開で報告されます。しかし、場合によっては、問題が発見される前に、すでに実際の利用環境で悪用されていることがあります。このような状況では、影響を受けるのはあなたのプロジェクトを利用しているユーザーです。そのため、軽量で明確に定義されたインシデント対応計画(セキュリティ問題が発生した際の対応手順)を用意しておくことが、被害を最小限に抑えるうえで大きな助けになります。
脆弱性が非公開で報告された場合でも、その後の対応が重要になります。脆弱性の報告を受け取った場合や、不審な活動を検知した場合、その後どのように対応するかをあらかじめ考えておく必要があります。
たとえ簡単なチェックリストのようなものであっても、基本的なインシデント対応計画を用意しておくことで、緊急時にも冷静に対応し、効率的に行動できます。また、ユーザーや研究者に対して、インシデントや報告を真摯に受け止めていることを示すことにもつながります。
対応プロセスは、複雑である必要はありません。最低限、以下の項目を定めておきましょう。
- 誰がセキュリティ報告やアラートを確認し、トリアージを行うのか
- 重大性をどのように評価し、緩和策の判断をどのように行うのか
- 修正し、脆弱性開示を進めるために、どのような手順を取るのか
- 影響を被るユーザー、コントリビューター、またはプロジェクトを利用しているユーザーや関連プロジェクトへどのように通知するのか
対応が適切に行われないインシデントは、ユーザーからプロジェクトへの信頼を損なう可能性があります。この対応プロセスをSECURITY.mdファイルに記載(またはリンク)しておくことで、ユーザーに対応方針や状況を明確に伝え、信頼関係の構築につなげることができます。
参考例として、Express.js Security WGでは、オープンソースプロジェクト向けのシンプルでありながら効果的なインシデント対応計画の例が公開されています。
この計画は、プロジェクトの成長に合わせて発展または変化させていくことができます。しかし、基本的な対応の枠組みをあらかじめ用意しておくことで、実際にインシデントが発生した際の時間を節約し、対応時のミスを減らすことができます。
セキュリティをチーム全体の取り組みと捉える
セキュリティは、個人だけが担う責任ではありません。プロジェクトのコミュニティ全体で共有することで、より効果的に機能します。
もちろんツールやポリシーは重要ですが、強固なセキュリティ体制は、チームやコントリビューターがどのように協力して取り組むかによって形成されます。責任を共有する文化を築くことで、プロジェクトは脆弱性をより迅速かつ効果的に発見し、トリアージを行い、対応できるようになります。
セキュリティをチーム全体の取り組みにするために、以下のような方法があります。
- 役割を明確にする: 脆弱性報告への対応担当者、依存関係の更新を確認する担当者、セキュリティ修正を承認する担当者を明確にしましょう。
- 必要最小限のアクセス権限を付与する: 書き込み権限や管理者権限は、本当に必要な人だけに付与し、権限を定期的に確認しましょう。
- 教育に投資する: コントリビューターが、安全なコーディング手法、一般的な脆弱性の種類、SASTやシークレットスキャンなどのツールの使い方を学べるよう促しましょう。
- 多様性と協調性を促進する: 多様な経験や視点を持つチームは、より幅広い脅威への認識や創造的な問題解決能力をもたらします。また、他の人が見落とす可能性のあるリスクを発見することにも役立ちます。
- 上流・下流プロジェクトと連携する: 依存している上流プロジェクトの問題はあなたのプロジェクトのセキュリティに影響を与える可能性があり、あなたのプロジェクトの問題も下流の利用者に影響を与えます。上流のメンテナーと協調的な脆弱性開示に取り組み、脆弱性修正時には下流の利用者へ情報を提供しましょう。
セキュリティは、一度設定すれば完了するものではなく、継続的に取り組むプロセスです。コミュニティと協力し、安全な開発手法を広め、互いに支え合うことで、より強く回復力のあるプロジェクトと、すべての人にとってより安全なエコシステムを築くことができます。
まとめ: あなたにとって小さな一歩でも、ユーザーにとっては大きな改善
ここで紹介した取り組みは、一見すると簡単なことや基本的なことに思えるかもしれません。しかし、こうした対策を積み重ねることで、よくあるセキュリティ問題からユーザーを守り、より安全なプロジェクトを築くことができます。
セキュリティは、一度整えれば終わりではありません。プロジェクトが成長すれば、責任も攻撃対象領域も変化します。定期的にプロセスを見直し、継続的に改善していきましょう。
コントリビューター
このガイドの作成にあたり、経験や知見を共有してくださったすべてのメンテナーの皆さんに心より感謝します!
このガイドは、@nanzggitsと@xcorailによって執筆され、以下の方々をはじめとする多くのコントリビューターの協力によって作成されました。
@JLLeitschuh、@intrigus-lgtm、@UlisesGasconほか、多くの皆さんにご協力いただきました。