アジャイル開発時代のアプリケーションをセキュアにする方法
目次[非表示]
- 1.脆弱性はバグの一種です
- 2.脆弱性のあるアプリケーション
- 3.Sneakerの挙動確認
- 4.Snykにログインする
- 5.Snyk CLIを使用可能にする
- 6.Snyk CLIで脆弱性をスキャンする
- 6.1.アプリケーションコードの脆弱性
- 6.2.OSSライブラリの脆弱性
- 6.3.Dockerイメージの脆弱性
- 7.CIを導入してみる
- 7.1.作業用ブランチを作成する
- 7.2.Visual Studio CodeにSnykのプラグインを入れる
- 7.2.1.Sneakerディレクトリをプロジェクトとして開く
- 7.2.2.拡張機能としてSnykを有効化する
- 7.3.OSSとアプリケーションコードの脆弱性を確認する
- 7.4.Github Actionsでワークフローを実行する
- 7.4.1.Secretをリポジトリへ登録する
- 7.4.2.リポジトリへプッシュしてワークフローを実行する
- 7.5.アプリケーションコードの脆弱性を修正する
- 7.6.OSSの脆弱性を修正する
- 7.6.1.OSSの継続監視
- 7.7.Dockerイメージの脆弱性は?
- 8.コンテナレジストリの脆弱性を継続的に検知する
- 9.まとめ
はじめに
こんにちは。DIVXでCTOを務めている田島です。
先日「エンジニア向けセキュリティ勉強会 〜アジャイル開発時代のアプリケーションをセキュアにする方法〜」と題した勉強会をSnykのToshiさんと行わせていただきました。結果として70名以上の方に参加頂けたようで嬉しかったです。皆さんありがとうございました。
今回の記事はその勉強会で行った内容をまとめたものです。勉強会に参加いただいた方もそうでない方も、よろしければご覧いただけると嬉しいです。
脆弱性はバグの一種です
バグはソフトウェアのリリースにブレーキをかけます。バグはテストを行う事で発見し修正する事ができますが、近年のアジャイルな開発現場ではそれらはCI/CDというリリースサイクルの中に組み込まれて自動化されている事が多いです。
バグの中でも情報漏えいを起こしたり、データを意図せず書き換えてしまうようなものを脆弱性と言います。脆弱性もバグの一種なのでCI/CDの中に組み込むのが理想ですが、今回はそんな理想を叶える為の方法をシェアいたします。
脆弱性のあるアプリケーション
今回はSneakerというコンテナで動くアプリケーション開発のCIに、脆弱性スキャンのフローを組み込んでみます。
Sneakerはサンプルのアプリケーションですが、Githubへのプッシュをイベントとして自動テストおよびコンテナイメージのビルド、コンテナレジストリへのプッシュまで行うので、皆さんが開発しているアプリケーションに組み込むとしたらどうなるのか?のイメージはつくと思っております。
Sneakerの挙動確認
SneakerはDockerコンテナが動く環境であれば簡単に動作します。ローカルPC内のお好きなディレクトリ内で、下記のリポジトリをクローンして下さい。CI実行のブランチがciブランチなので、ciブランチをデフォルトのブランチとして指定しています。
リポジトリをクローンした後は、READMEに書いてある手順でアプリが起動します。下記にコマンドだけ改めて記載します。
http://localhost:8080/にアクセスすると、下記のような簡素のUIのページが表示されます。
結論から先に申し上げると、Sneakerには少なくとも下記の脆弱性があります。これらは後述のSnyk CLIを使えば簡単に確認する事ができます。
- アプリケーションコード
- クロスサイトスクリプティング(XSS)
- SQLインジェクション
- ファイルインクルード
- OSコマンドインジェクション
- OSSライブラリ
- guzzlehttp/guzzle
- CVE-2022-29248
- CVE-2022-31043
- CVE-2016-5385
- guzzlehttp/guzzle
- Dockerコンテナ(一部抜粋)
- apache2
- curl
- dpkg
- expat
- glibc
- openssl
最終的にこれらの脆弱性をCIツールと連携する事で検出してみます。
なお、上記の中でアプリケーションコードの脆弱性だけは、ユーザーのコーディング依存で発生しているものですので下記の通り再現が容易です。興味のある方は試してみて下さい。
クロスサイトスクリプティング(XSS)
SQLインジェクション
OSコマンドインジェクション
ファイルインクルード
Snykにログインする
Snykを利用するにはSnykにログインする必要があります。ログインはGithubアカウントやGoogleアカウントがあれば簡単にログインできます。途中Githubとの連携を求められるので、Githubでログインしておいた方がスムーズかもしれません。
ログインすると、下記のようにダッシュボードへと遷移します。
Snyk CLIを使用可能にする
ダッシュボードにて、「Using Snyk in the command line」というパネルがあるので、snyk authで認証まで実施します。
認証が成功したら、下記コマンドでAPIキーを取得しておきましょう。APIキーはSnykをDocker Hub等の外部サービスと連携させる時に必要となるので、そのタイミングで改めて取得していただいても構いません。なお、取得した値は誰にも公開されないように取り扱う必要があります。
Snyk CLIで脆弱性をスキャンする
まずはじめに最も手軽に脆弱性をスキャンする方法として、Snyk CLIを使ってみます。
アプリケーションコードの脆弱性
リポジトリのディレクトリ直下で下記コマンドを実行します。
下記の通り4つの脆弱性が発見されました。
OSSライブラリの脆弱性
リポジトリのディレクトリ直下で下記コマンドを実行します。
各プログラミング言語に備わっているパッケージ管理システムのマニフェストファイルを対象にスキャンするので、それがあるディレクトリを指定しています。今回の場合はhtmlディレクトリ内に、composer.jsonおよびcomposer.lockが配置されているので、htmlを引数として指定しています。また、 --severity-threshold=highも指定して、重大度が高以上のものだけに絞っています。
Dockerイメージの脆弱性
Dockerイメージは使用しているマシン内でグローバルに管理されていますが、一応リポジトリのディレクトリ直下で下記コマンドを実行します。
ビルド済みのDockerイメージを対象にスキャンするので、今回ビルドしたsneaker:latestを指定しています。OSSと同様に --severity-threshold=highも指定して、重大度が高以上のものだけに絞っています。それでも、かなり膨大な数の脆弱性が発見できるはずです。
CIを導入してみる
Snyk CLIによって脆弱性が発見できるので、それを修正していけば脆弱性を解消する事ができますが、今回のテーマは「アジャイル開発時代のアプリケーションをセキュアにする方法」です。よって、CIツールの中でそれらを解消していこうと思います。
今回はCI/CDツールとしてGithub Actionsを使用します。CDまでは行いませんが、CIのイメージができれば、それをCDへ拡張できる事は自明でしょう。
作業用ブランチを作成する
もしカレントブランチがciではなかった場合はスウィッチします。
そこからpracticeブランチを作成しましょう。以後の修正はpracticeブランチ上で行っていきます。
Visual Studio CodeにSnykのプラグインを入れる
私個人は普段Vimを使用しているので、あまりVisual Studio Codeを使う機会はないのですが、SnykとVisual Studio Codeの組み合わせがとても便利なので今回はこちらを活用します。
Sneakerディレクトリをプロジェクトとして開く
ブランチが、先程変更したpracticeである事も確認しておきましょう。
拡張機能としてSnykを有効化する
拡張機能でsnykと検索するといくつか出てくるので、Previewではない方をインストールしましょう。
左メニューにSnykのアイコンが現れるのでクリックします。その後、赤枠をクリックしてブラウザ経由での認証を済ませます。
認証が成功すると下記のような画面になるので、「OPEN SOURCE SECURITY」の「Settings」をクリックします。
「Additional Parameters」の入力エリアにhtmlと入力します。これは、Snyk CLIで「OSSライブラリの脆弱性」のスキャンを実行した時に引数として渡したhtmlと同じ意味です。
このように一度Snyk CLIでシンプルな操作を体験しておくと、他の複雑なツールを扱う時も設定項目の意味が理解しやすいです。よって、Snyk CLIで操作をイメージしてから他のツールを使う事を個人的にはオススメしてます。
htmlを入力後、スキャンを実行するとOSSの脆弱性を検知できるようになります。
OSSとアプリケーションコードの脆弱性を確認する
Snykのメニューを開くと、OSSとアプリケーションコードの脆弱性がサイドバーに表示されています。これはさきほどSnyk CLIでスキャンした結果と一致しているので、Snyk CLIのアウトプットと見比べてみて下さい。
ここで、「CODE SECURITY」のどれか一つをクリックすると、該当のソースコードとその詳細情報が右のパネルに表示されるので、Snyk CLIで確認した時よりも見やすいです。
Github Actionsでワークフローを実行する
今の脆弱性がある状態で、Github Actionsのワークフローを実行してみます。現状のリポジトリのワークフローファイルの設定では、git pushによってワークフローが実行されるようになっています。皆さんがこのワークフロー実行を試す場合は、originをgithub.com:yuya-tajima/sneaker.gitから皆さんのアカウントのリポジトリに変更をお願いいたします。
Secretをリポジトリへ登録する
SNYK_TOKENとしてsnyk config get apiで取得しておいたAPIキーを設定します。DOCKERHUB_TOKENやDOCKERHUB_USERNAMEはDocker Hubにビルドしたコンテナイメージをプッシュする時に必要な情報です。
リポジトリへプッシュしてワークフローを実行する
下記のコマンドを実行し、ローカルで新規作成したブランチをリモートへプッシュします。
Githubの「Actions」タブをクリックすると、ワークフローが実行されている事が確認できます。
詳細を確認すると、Vulnerability Scanningというジョブが失敗しているのが確認できます。
更に詳細を確認をすると、失敗している箇所で4つのアプリケーションコードの脆弱性が検知されている事が確認できます。
これはVisual Studio Codeの下記赤枠と一致しています。よって、ここを修正すればワークフローの失敗箇所が成功しそうです。
アプリケーションコードの脆弱性を修正する
検出された4つの脆弱性を修正します。自力で修正頂いても良いですが、fixedディレクトリ内のindex.phpをコピーして頂ければ修正が完了します。
更新すると「CODE SECURITY」の脆弱性が消えました。
修正内容をコミット&プッシュして再度ワークフローを実行してみると、アプリケーションコードの脆弱性は解消されていましたが、次のステップのOSS脆弱性チェックに引っかかっていました。
OSSの脆弱性を修正する
Github Actionsのワークフローで検出されたOSSの脆弱性も、アプリケーションコードの脆弱性と同様にVisual Studio Code内の検出結果と一致しています。
composer.json内のバージョン表記を修正して、依存性の情報をアップデートします。こちらはfixedディレクトリ内のcomposer.jsonをコピーして、composer update --no-installを実行すれば修正が完了します。
修正内容をコミット&プッシュして再度ワークフローを実行してみると、ワークフローが成功していました。
OSSの継続監視
ちなみに、途中「Run Snyk to monitor the OSS vulnerabilities」内にてOSSライブラリを継続的にモニタリングするステップを組み込んであります。
URIをクリックすると、composer.lockがプロジェクトとしてSnykの監視対象に含まれた事がわかります。現在は全ての脆弱性を解消している状態なので何も検知も通知もされませんが、今後脆弱性が発覚したタイミングでユーザーにその旨が通知されます。
DockerイメージもOSSと同様にSnykの継続監視対象に含める事ができますが、それは最後にご紹介します。
Dockerイメージの脆弱性は?
実はVulnerability Scanningの中を細かくみると、Run Snyk to check Docker image for vulnerabilitiesというステップで、重大度が高以上の脆弱性が検出されている事がわかります。こちらはDockerイメージの中に残る脆弱性なのですが、今回はあえてパスさせています。
どんな脆弱性があるかは、「Security」タブの「Code Scanning」メニューで確認できるようにワークフローファイルで設定してあります。
今回Dockerイメージの脆弱性に関してあえてパスしているのは、使用しているDockerイメージから常に脆弱性を解消しつくすのは現実的に難しいケースがあるからです。もちろん、既知の脆弱性をゼロにする事が理想ではありますが、アプリケーションコードやOSSライブラリの脆弱性に比べて、Dockerイメージの脆弱性は影響範囲が限定的になるケースが多いです。その為、そこにかけるコストと危険性のバランスをみながら落とし所を探っていく事が多いかもしれません。
実際今回使用しているベースイメージであるphp:7.4.1-apacheを、2023年2月7日時点の最新版であるphp:8.2.2-apacheに変更しても依然としてCriticalな脆弱性が1つ残ってしまっていました。この状態をどうしても避けなければならない場合、別のベースイメージを使用するか、スクラッチで作成し直すかの意思決定が必要になるでしょう。この判断を促してくれるという意味でも、Snykを使う価値は大いにあると考えています。
コンテナレジストリの脆弱性を継続的に検知する
今回Github Actions経由して、Docker Hubにイメージがプッシュされました。仮にこのDockerイメージの脆弱性が解消されているとしても、継続的にイメージの安全性を担保する仕組みが必要です。なぜなら、脆弱性のスキャンはCIの中でしか実行されないからです。
Snykの管理コンソールの「Integrations」では、現在Docker HubがConfiguredになっています。これはDocker Hub上にあるDockerイメージが、脆弱性における監視対象として含められる状態である事を意味します。他にもAWSやGCPをはじめ様々な外部レジストリとの連携が可能です。
まとめ
簡単なものではありましたが、SnykをCIの中に組み込んで継続的に脆弱性をスキャンする仕組みを整えてみました。皆さんのサービスやプロダクトをスピーディかつセキュアに届ける為の一助となれば幸いです。