メリークリスマス!エンジニアのみなさんはPCと向き合っていることかと思います。
最近様々なところでDevSecOpsが浸透してきました。概念としては理解できるものの、実際に例を見てみないことには納得できないところもあるかと思います。
というわけで今回は超入門編の記事を執筆しました。みなさまの腹落ちに繋がると幸いです。
この記事で紹介すること
- DevSecOpsのアーキテクチャ
- どういうメリットがあるのか
- ツールの使用例
DevSecOpsとは
DevSecOps(Development, Security, Operationsの組み合わせ)は、ソフトウェア開発プロセスの用語です。開発(Dev)、セキュリティ(Sec)、および運用(Ops)の3つの主要な側面を統合し、連携させることで、セキュリティの統合を強化し、ソフトウェア開発の早期段階からセキュリティを考慮することを目指しています。
アジャイル、スクラム、DevOpsといったシステム開発の手法の1つではなく、どのようにDevOps型の開発をするか、セキュリティを担保するかを検討する際のアーキテクチャの1つです。
DevSecOpsとはなんぞや
OWASP DevSecOps Guidelineでは以下のライフサイクルで表現されています。
https://owasp.org/www-project-devsecops-guideline/latest/00a-Overview
(上図左側)矢印で示されている箇所にそDevOps開発のレイヤーでセキュリティチェックを行う箇所が存在します。
重要なことですが、一般的にアプリ開発ではリリース後に脆弱性診断をすればセキュリティを網羅的に発見できます。 とはいえ、リリース後に初めてセキュリティチェックを行うのでは、出てきた脆弱性を後から修正するのはかなりの手戻りが必要になります。
それをアプリケーションの設計段階から計画しておき、テストや脆弱性診断後のトリアージ、開発の手戻りを少なくしようという試みです。
どこからスタートすればいいのか
前述の8の字の輪っかをどう既存のプロジェクトに組み込めばいいのか、という問題がありますので
一般的に、まずはソースコードを元としてGithubのプルリク等をトリガーに以下のステップでセキュリティを担保すると良いかと思います。
なお、OWASP DevSecOps Guidelineでは上記に加えて以下の要素が紹介されています。
- IaC Scanning (構成ツールに渡すファイルのスキャン)
- Infra Scanning
- コンプライアンスチェック
https://owasp.org/www-project-devsecops-guideline/
DevSecOpsのフロー(一例)
-
Secret Scanning
Githubリポジトリ内の機密情報、IAMのアクセスキーやJWT生成のSecret等をソースコードにベタ書きしていないか調査します。 -
SCA (Software Composition Analysis)
サードパーティやオープンソースなライブラリの既知の脆弱性等を確認します。
使用するもの: Github Dependbot、Snykなど -
SAST (Static Application Security Test)
ソースコードの静的解析です。問題のある関数の使い方等を調査します。
使用するもの: Snykなど -
Container Scanning
コンテナイメージ内のソフトウェアパッケージやライブラリの脆弱性を調査します。
使用するもの: Trivy, Snykなど -
DAST (Dynamic Application Security Test)
アプリケーションの静的解析です。脆弱性診断用のツールが使用されることもありますが、開発プロセスに含めるためには自動化をしておきたいです。
ツール: ZAP, Vexなど
それでは次の章から実際にアプリケーションに組み込む際の設定例を紹介していきます。また、こちらは一例であり、導入するプロダクトにより使える/使えない構成がありますのであくまで「こんなことをすると良いんだな」程度に捉えていただければ幸甚です。
1. Secret Scanning
使用するもの: Github Dependbot
Githubリポジトリ内の機密情報、IAMのアクセスキーやJWT生成のSecret等をソースコードにベタ書きしていないか調査します。
これはGithubの機能をそのまま使っていきましょう
リポジトリ > Setting > Code security and analysisから
2. SCA (Software Composition Analysis)
サードパーティやオープンソースなライブラリの既知の脆弱性等を確認します。
使用するもの: Github Dependbot、Snykなど
今回はGithub Dependbotを使用していきます。要件がなければGithubのソースをGithubに検証してもらうのが楽だと思います。
特に設定は必要Pull RequestをトリガーにPackage.json(npm)やComposer.json(PHP)等を走査して問題のあるパッケージを通知してくれます。
Github Dependbot設定
先ほどと同様にGithubを遷移します。
リポジトリ > Setting > Code security and analysisから
Dependbot alertsを有効にします。
問題があるライブラリがある場合、Securityタブから確認できます。また、メールでも通知してくれます。
このアラートからプルリクを出すこともできます。
※依存関係を解決する以下のようなLockファイルがリポジトリ内に存在しないと実行できません。
- npm: package-lock.json
- php: composer.lock
このフローで必要となるpackage-lock.jsonをgit管理に含めるかどうかはプロジェクトによると思いますが、コンテナやCIツールを実行する時にも必要であるため私は含めるべきと考えています。
lockファイルについてはこちらの記事が参考になります。本筋とは異なるため説明は割愛させてください。
https://zenn.dev/estra/articles/npm-lockfile-dependencies
3. SAST (Static Application Security Test)
ソースコードの静的解析です。問題のある関数の使い方等を調査します。
使用するもの: Snykなど
SASTツールの例としてSnykを紹介します。
SnykはGithub連携しておくとリポジトリを調査してCodeAnalyticsを行なってくれます。
連携後、リポジトリ選択画面からチェックを入れると解析の対象になります。いきなり全てが解析対象になるわけではありません。
スキャン後にSnykの画面からCodeAnalitycsを確認します。
引くほど脆弱性が検出されていますが、これは説明用にRailsのやられアプリに実施しているので問題ありません。普通のリポジトリでこれだけ検出されたらPLは怒られると思います。
以下はCodeAnaliticsの結果です。Nokogiriの危ない使い方によるXXEを検出してくれました。その他の脆弱性でも結構ロジックをちゃんと見ていて驚きました。脆弱性診断をする前にこういった物をピックアップしておいてくれるのは非常に助かりますね。
Snykは前述のGithubのdependbotと同じようにSCAとしても使用できます。
さらに、Snykの場合、脆弱性情報だけでなく、deprecatedなパッケージを使用しているパッケージも依存関係から明らかにします。
Snykの使用感を試してみたい方は他人様のリポジトリのスキャンもできます。
り
プロジェクトの追加から画面遷移をします。
こちらのURLで脆弱性があるリポジトリに対して試すと、Snykの使用感がわかると思います。ちなみに初回は時間がかかります。(5分くらい?)
上記のリポジトリはRailsで作成されたやられアプリとなっており、よく起きる脆弱性をソースコードから確認し、修正してDASTツールで確かめてみよう、というのがコンセプトになっているため、脆弱性検証では非常に使い勝手が良いです。
4. Container Scanning
コンテナイメージ内のソフトウェアパッケージやライブラリの脆弱性を調査します。
使用するもの: Trivyなど
イメージ内のライブラリの脆弱性を調査したい理由は、構成ファイルだけではわからない依存関係が存在するためです。
例えば、以下はとあるイメージでcurlをインストールした際のバージョンです。特に指定せずにパッケージ管理ツールを使うと、最新ではないバージョンがインストールされます。
ubuntu@ip-172-31-47-54:~$sudo apt -y curl
~省略~
ubuntu@ip-172-31-47-54:~$curl --version
curl 7.81.0 (x86_64-pc-linux-gnu) libcurl/7.81.0 ...
上記の場合、curl7.81.0となり、CVE-2023-38545(CVSS9.8)の深刻な脆弱性に対応できていません。(執筆時の最新はcurl 8.5.0)
今回はよく使われるtrivyを使用して調査していきます。
trivyはお手軽にCI/CDパイプラインとしてGithub Actionsで利用できます。そのため、Githubにプルリクを出した時点でイメージを作成し、コンテナスキャンができるというわけです。ドキュメント通りにリポジトリ内に.github/workflows/
に設定ファイルを置くだけで導入可能です。本筋とは異なるため説明は割愛させてください。
https://github.com/aquasecurity/trivy-action/blob/master/README.md
コンテナ使用されているlibcurlのバージョンと脆弱性も明らかにしてくれます。
(検出すると思っていたCVE-2023-38545が出なかった...汗)
5.DAST (Dynamic Application Security Test)
アプリケーションの動的解析です。脆弱性診断用のツールが使用されることもありますが、開発プロセスに含めるためには自動化をしておきたいです。
使用するもの: ZAP, Vexなど
動的解析は稼働しているアプリケーションに対してHTTPの通信を飛ばして調査が行われ、実際の攻撃者と同様に、HTTPの通信に攻撃ペイロードを送信します。
また、今回紹介するZAP(Zed Attack Proxy)は世界で最も使われているOSSのWebアプリスキャナーです。要は脆弱性診断ツールなのですが、最近はDASTという位置付けになっています。ちなみに以前はOWASPに所属するプロジェクトでした。(OWASP ZAP)
ZAPもCI/CDパイプラインとしてGithub Actionsに組み込むことが可能です。
前述のTrivy同様にドキュメント通りに、リポジトリの.github/workflows/
に以下のymlファイルを置くだけです。
name: Check run
on:
push:
pull_request
jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
# アプリケーションを立ち上げる何かしらの設定
- run: sudo apt-get update && sudo apt-get install -y nginx && sudo systemctl start nginx
- name: ZAP Scan
uses: zaproxy/action-full-scan@v0.3.0
with:
docker_name: owasp/zap2docker-stable # ZAPのdokcerイメージ
target: 'http://localhost'
cmd_options: '-n target.context' #ペイロードの設定
この状態でプルリクを出すと、Github Actionsでアプリが起動してActionsタブにZAPのレポートが出力されます。
スキャン結果から、実際に稼働しているアプリの調査をしていることがわかります。
と、いった具合にDASTをCI/CDパイプラインに組み込むことができました。
ただし、DASTを動かす点に関してはかなり面倒な点が多いです。 このコストを乗り越えてDevSecOpsを運用していくか検討の余地も大きいです。
設定にコストがかかる理由は以下です。
- ZAPに通信を覚えさせる
Webアプリをクローリングして、パラメータの通信を覚えさせる必要があります。ログインが必要ならその情報も覚えさせますし、アプリの動線が変更になった時はそれも取り込む必要があります。 - アプリが稼働してからでないとZAPを動かせない
当然、対象となるアプリが起動している必要があるわけですが、プルリクを出した時点ではまだコンテナすら立ち上がっていません。そのため、アプリケーションが立ち上がったよ、ということを何かしらのトリガーで通知する必要があります。 - スキャン時間かかりすぎ問題
リクエスト数とアプリの強度にもよりますが、10画面遷移程度のアプリケーションだとスキャンは5~15分かかります。ビルド時間 + スキャン時間でCI/CDの利用時間を超えない様に注意が必要です。 - リクエストのレート制限にひっかかる
スキャンのパケットを大量に投げるため、CI/CDツールのレート制限にかかる可能性があります。
https://qiita.com/ishii1648/items/ff662fa1a2d2e08ec494
ZAPのクローリングについて
ZAPは何でも自動でクローリングしてくれるわけではないので、初めは人間の目でリクエストを集めます。ZAPのデスクトップ版をインストールして巡回し、コンテキストをエキスポートしてGithub Actionsに渡します。
自動クローリング機能もありますが、精度は高くないので最初から手動で確認しながらの方が事故防止にもなります。
また、ZAPをDevSecOpsでCI/CDパイプラインに組み込むとなると、以下の様になります。
前述のcheck-run.yml
では「#アプリケーションを立ち上げる何かしらの設定」と一言で書いていますが、ここでDASTの診断するアプリケーションが立ち上がっていないとGithub Actionsはコケます。
ここまでの設定をすると
プルリクをトリガーにGithub Actionsに設定しておくと、まとまったソースコード変更の都度セキュリティが可視化されるため、セキュリティ面だけではなく急な脆弱性対応に追われることが減るため、品質向上にも役立ちます。
とはいえかなり理想論を書いているのも確かで、特にDASTツールは設定が煩雑なので自分の場合はビルド + ホスティングは他のホスティングサービス(HerokuやVercel)に任せてE2Eテストと動的な解析はホスティングサービスの方で実施しています。
※Vercelに診断パケットを飛ばす際はEenterpriseプラン契約が必要です。また、ホスティングサービスはDASTの様なツールを使うことを禁止しているところもあるため、規約を確認してください。
まとめ
DevSecOpsというアーキテクチャに焦点を当てて記事を書いてみました。
全てを取り入れる必要はありませんし、自社でできていないと思われる点だけを採用していただければいいと考えています。きちんと検出できているのかどうかを管理するコストも中々かかります。
くどいようですがあくまで「一例」になりますので
この記事が皆様の参考になると幸いです。メリークリスマス!
余談
今回初めてSASTとしてSnykを使ってみたのですが、かなり精度が高くて使い勝手が良かったです。
特にGithub Dependbotと比較するとSnykの方が多くパッケージの問題点を洗い出してくれました。
それと、記事を書いてから気がつきましたがSnykはContainer Scanningツールも存在するようなので記事の工程の4/5がSnykで実施できることになりますね....
https://dev.classmethod.jp/articles/check-the-runtime-version-in-the-images/