DevSecOps超入門

メリークリスマス!エンジニアのみなさんはPCと向き合っていることかと思います。
最近様々なところでDevSecOpsが浸透してきました。概念としては理解できるものの、実際に例を見てみないことには納得できないところもあるかと思います。
というわけで今回は超入門編の記事を執筆しました。みなさまの腹落ちに繋がると幸いです。
Eenterprise## この記事で紹介すること
* 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 vs DevSecOps.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/1820dd49-809a-2f7a-812f-33bb0c6507f6.png)
https://owasp.org/www-project-devsecops-guideline/latest/00a-Overview

(上図左側)矢印で示されている箇所にDevOps開発のレイヤーでセキュリティチェックを行う箇所が存在します。

重要なことですが、一般的にアプリ開発ではリリース後に**脆弱性診断をすればセキュリティを網羅的に発見できます。** とはいえ、リリース後に初めてセキュリティチェックを行うのでは、出てきた脆弱性を後から修正するのはかなりの手戻りが必要になります。

それをアプリケーションの設計段階から計画しておき、テストや脆弱性診断後のトリアージ、開発の手戻りを少なくしようという試みです。

## どこからスタートすればいいのか
前述の8の字の輪っかをどう既存のプロジェクトに組み込めばいいのか、という問題がありますので
一般的に、まずはソースコードを元としてGithubのプルリク等をトリガーに以下のステップでセキュリティを担保すると良いかと思います。
``` mermaid
flowchart LR
    id1[1.Secret Scan]
    id2[2.SCA]
    id3[3.SAST]
    id4[4.Container Scan]
    id5[5.DAST]
    id1 --> id2 --> id3 --> id4 --> id5 
```

``` mermaid
flowchart LR
    id1[1.IaCファイルのスキャン]
    id2[2.SAST\n（ソースコード検査）]
    id3[3.コンテナスキャン]
    id4[4.プラットフォームのスキャン]
    id5[5.依存関係チェック]
    id1 --> id2 --> id3 --> id4 --> id5 
```

なお、OWASP DevSecOps Guidelineでは上記に加えて以下の要素が紹介されています。
* IaC Scanning (構成ツールに渡すファイルのスキャン)
* Infra Scanning
* コンプライアンスチェック

![DevSecOps-pipeline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/40d94ca4-84a6-4e41-7af3-04bcae2f6cc6.png)
https://owasp.org/www-project-devsecops-guideline/

## DevSecOpsのフロー(一例)
1. **Secret Scanning**
Githubリポジトリ内の機密情報、IAMのアクセスキーやJWT生成のSecret等をソースコードにベタ書きしていないか調査します。


1. **SCA (Software Composition Analysis)**
サードパーティやオープンソースなライブラリの既知の脆弱性等を確認します。
使用するもの: Github Dependbot、Snykなど
1. **SAST (Static Application Security Test)**
ソースコードの静的解析です。問題のある関数の使い方等を調査します。
使用するもの: Snykなど
1. **Container Scanning**
   コンテナイメージ内のソフトウェアパッケージやライブラリの脆弱性を調査します。
使用するもの: Trivy, Snykなど
1. **DAST (Dynamic Application Security Test)**
    アプリケーションの静的解析です。脆弱性診断用のツールが使用されることもありますが、開発プロセスに含めるためには自動化をしておきたいです。
    ツール: ZAP, Vexなど

それでは次の章から実際にアプリケーションに組み込む際の設定例を紹介していきます。また、こちらは**一例であり**、導入するプロダクトにより使える/使えない構成がありますのであくまで「こんなことをすると良いんだな」程度に捉えていただければ幸甚です。


### 1. Secret Scanning
:::note info
使用するもの: Github Dependbot
:::
Githubリポジトリ内の機密情報、IAMのアクセスキーやJWT生成のSecret等をソースコードにベタ書きしていないか調査します。

これはGithubの機能をそのまま使っていきましょう
リポジトリ > Setting > Code security and analysisから
![スクリーンショット 2023-12-24 3.11.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/636f51bf-9908-4fed-f700-651638aaf5c8.png)

Secret scanningを有効にするだけです。
![スクリーンショット 2023-12-24 3.47.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/2b7f2808-196f-4f95-ef82-ec0a3cf6e08f.png)
### 2. SCA (Software Composition Analysis)
サードパーティやオープンソースなライブラリの既知の脆弱性等を確認します。

:::note info
使用するもの: 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を有効にします。
![スクリーンショット 2023-12-24 3.50.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/f3d00a79-ddc7-b515-64bb-904f77205bf5.png)

問題があるライブラリがある場合、Securityタブから確認できます。また、メールでも通知してくれます。
![スクリーンショット 2023-12-24 3.52.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/e0e1f003-fce3-00c1-a33f-d0dfbaa3c072.png)
このアラートからプルリクを出すこともできます。

:::note warn
依存関係を解決する以下のような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)
ソースコードの静的解析です。問題のある関数の使い方等を調査します。

:::note info
使用するもの: Snykなど
:::


SASTツールの例として[Snyk](https://snyk.io/jp/)を紹介します。
SnykはGithub連携しておくとリポジトリを調査してCodeAnalyticsを行なってくれます。

``` mermaid
flowchart LR
    id1[エンジニア]
    id2[Github]
    id3[Snyk]
    id1 --> |プルリク|id2 -->|Actions| id3 
```

連携後、リポジトリ選択画面からチェックを入れると解析の対象になります。いきなり全てが解析対象になるわけではありません。
![スクリーンショット 2023-12-24 4.24.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/ac9de215-a5ff-3f9b-aeec-1a76b2fea2ea.png)

スキャン後にSnykの画面からCodeAnalitycsを確認します。
![スクリーンショット 2023-12-24 4.29.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/fe294154-7869-102f-0594-795a9cdf5d0f.png)

引くほど脆弱性が検出されていますが、これは説明用に[Railsのやられアプリ](https://github.com/halkichi0308/broken_rails_app)に実施しているので問題ありません。普通のリポジトリでこれだけ検出されたらPLは怒られると思います。


以下はCodeAnaliticsの結果です。Rubyのパッケージ「Nokogiri」の危ない使い方※1によるXXEを検出してくれました。その他の脆弱性でも結構ロジックをちゃんと見ていて驚きました。脆弱性診断をする前にこういった物をピックアップしておいてくれるのは非常に助かりますね。

![スクリーンショット 2023-12-24 4.53.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/8a27f554-92ee-613e-2dd5-5a4a065e4ab2.png)


Snykは前述のGithubのdependbotと同じようにSCAとしても使用できます。
さらに、Snykの場合、脆弱性情報だけでなく、**deprecatedなパッケージを使用しているパッケージも依存関係から明らかにします。** 

Snykの使用感を試してみたい方は他人様のリポジトリのスキャンもできます。
り
プロジェクトの追加から画面遷移をします。
![スクリーンショット 2023-12-24 4.47.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/0c5bd2dc-5838-31ec-c8d5-dd3a71d7d0f2.png)

こちらのURLで脆弱性があるリポジトリに対して試すと、Snykの使用感がわかると思います。ちなみに初回は時間がかかります。(5分くらい?)
![スクリーンショット 2023-12-24 4.48.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/f8b5700b-4bbf-03f5-03db-9b239ad166d0.png)
上記のリポジトリはRailsで作成されたやられアプリとなっており、よく起きる脆弱性をソースコードから確認し、修正してDASTツールで確かめてみよう、というのがコンセプトになっているため、脆弱性検証では非常に使い勝手が良いです。

### 4. Container Scanning
コンテナイメージ内のソフトウェアパッケージやライブラリの脆弱性を調査します。
   
:::note info
使用するもの: Trivyなど
:::
``` mermaid
flowchart LR
    id1[エンジニア]
    id2[Github]
    id3[SAST]
    id4[Container Scanning]
    id1 --> |プルリク|id2 -->|Actions| id3 
    id2 --> |Actions| id4 
```
イメージ内のライブラリの脆弱性を調査したい理由は、構成ファイルだけではわからない依存関係が存在するためです。
例えば、以下はとあるイメージでcurlをインストールした際のバージョンです。特に指定せずにパッケージ管理ツールを使うと、**最新ではないバージョンがインストールされます。**
``` bash
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を使用して調査していきます。

https://github.com/aquasecurity/trivy

trivyはお手軽にCI/CDパイプラインとしてGithub Actionsで利用できます。そのため、Githubにプルリクを出した時点でイメージを作成し、コンテナスキャンができるというわけです。ドキュメント通りにリポジトリ内に`.github/workflows/` に設定ファイルを置くだけで導入可能です。本筋とは異なるため説明は割愛させてください。
https://github.com/aquasecurity/trivy-action/blob/master/README.md

以下はtrivyの使用感です。
![スクリーンショット 2023-12-24 5.25.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/97f2cfdc-d8c6-8a88-19bd-b63ecd8c94a6.png)

コンテナ使用されているlibcurlのバージョンと脆弱性も明らかにしてくれます。
![スクリーンショット 2023-12-24 5.37.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/5f298c2e-7bda-4119-4e02-7ba54b5248c2.png)
(検出すると思っていたCVE-2023-38545が出なかった...汗)

### 5.DAST (Dynamic Application Security Test)
アプリケーションの動的解析です。脆弱性診断用のツールが使用されることもありますが、開発プロセスに含めるためには自動化をしておきたいです。

:::note info
使用するもの: ZAP, Vexなど
:::
``` mermaid
flowchart LR
    id1[エンジニア]
    id2[Github]
    id3[SAST]
    id4[Container Scanning]
    id5[DAST]
    id1 --> |プルリク|id2 -->|Actions| id3 
    id2 --> |Actions| id4 
    id4 --> id5
```

動的解析は稼働しているアプリケーションに対してHTTPの通信を飛ばして調査が行われ、実際の攻撃者と同様に、HTTPの通信に攻撃ペイロードを送信します。
``` mermaid
flowchart LR
    id2[ZAP]
    id3[稼働しているアプリ]
id2 --> |HTTP通信|id3
```

また、今回紹介するZAP(Zed Attack Proxy)は世界で最も使われているOSSのWebアプリスキャナーです。要は脆弱性診断ツールなのですが、最近はDASTという位置付けになっています。ちなみに以前はOWASPに所属するプロジェクトでした。(OWASP ZAP)


ZAPもCI/CDパイプラインとしてGithub Actionsに組み込むことが可能です。
前述のTrivy同様にドキュメント通りに、リポジトリの`.github/workflows/` に以下のymlファイルを置くだけです。
``` yml:check-run.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のレポートが出力されます。
![スクリーンショット 2023-12-24 7.55.33のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/38cf7d4b-f6c7-671c-dca6-52baabcf8613.png)

スキャン結果から、実際に稼働しているアプリの調査をしていることがわかります。
![スクリーンショット 2023-12-24 8.00.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/8dac5d9b-7e3d-741c-5afd-9a26f7a08a25.png)


と、いった具合にDASTをCI/CDパイプラインに組み込むことができました。
ただし、**DASTを動かす点に関してはかなり面倒な点が多いです。** このコストを乗り越えてDevSecOpsを運用していくか検討の余地も大きいです。

設定にコストがかかる理由は以下です。
1. ZAPに通信を覚えさせる
   Webアプリをクローリングして、パラメータの通信を覚えさせる必要があります。ログインが必要ならその情報も覚えさせますし、アプリの動線が変更になった時はそれも取り込む必要があります。
1. アプリが稼働してからでないとZAPを動かせない
   当然、対象となるアプリが起動している必要があるわけですが、プルリクを出した時点ではまだコンテナすら立ち上がっていません。そのため、アプリケーションが立ち上がったよ、ということを何かしらのトリガーで通知する必要があります。
1. スキャン時間かかりすぎ問題
   リクエスト数とアプリの強度にもよりますが、10画面遷移程度のアプリケーションだとスキャンは5~15分かかります。ビルド時間 + スキャン時間でCI/CDの利用時間を超えない様に注意が必要です。
1. リクエストのレート制限にひっかかる
   スキャンのパケットを大量に投げるため、CI/CDツールのレート制限にかかる可能性があります。
   https://qiita.com/ishii1648/items/ff662fa1a2d2e08ec494

#### ZAPのクローリングについて
ZAPは何でも自動でクローリングしてくれるわけではないので、初めは人間の目でリクエストを集めます。ZAPのデスクトップ版をインストールして巡回し、コンテキストをエキスポートしてGithub Actionsに渡します。
![スクリーンショット 2023-12-24 7.21.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/f1be5dff-3c09-b675-c462-8061aa2c385c.png)
自動クローリング機能もありますが、精度は高くないので最初から手動で確認しながらの方が事故防止にもなります。


また、ZAPをDevSecOpsでCI/CDパイプラインに組み込むとなると、以下の様になります。
``` mermaid
flowchart LR
    id1[1.Github プルリク]
    id2[2.ZAP Scan]
    id3[稼働しているアプリ]
    id1 -->|CI/CD| id2 --> |HTTP通信|id3
```

前述の`check-run.yml`では「#アプリケーションを立ち上げる何かしらの設定」と一言で書いていますが、ここでDASTの診断するアプリケーションが立ち上がっていないとGithub Actionsはコケます。　

### ここまでの設定をすると
プルリクをトリガーにGithub Actionsに設定しておくと、まとまったソースコード変更の都度セキュリティが可視化されるため、セキュリティ面だけではなく急な脆弱性対応に追われることが減るため、**品質向上にも役立ちます。**

とはいえかなり理想論を書いているのも確かで、特にDASTツールは設定が煩雑なので自分の場合はビルド + ホスティングは他のホスティングサービス(HerokuやVercel)に任せてE2Eテストと動的な解析はホスティングサービスの方で実施しています。
![スクリーンショット 2023-12-24 8.26.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235988/3da93e0d-a7f2-66da-d713-c8a5505bb2e0.png)

:::note warn
Vercelに診断パケットを飛ばす際はEenterpriseプラン契約が必要です。また、ホスティングサービスはDASTの様なツールを使うことを禁止しているところもあるため、規約を確認してください。
:::

## 注釈
※1... Rubyのパッケージ「Nokogiri」に問題があるわけではなく、無害化をせずに使用すると脆弱性が顕在化するパターンです。


## まとめ
DevSecOpsというアーキテクチャに焦点を当てて記事を書いてみました。
全てを取り入れる必要はありませんし、自社でできていないと思われる点だけを採用していただければいいと考えています。きちんと検出できているのかどうかを管理するコストも中々かかります。
くどいようですがあくまで「一例」になりますので
この記事が皆様の参考になると幸いです。メリークリスマス!


## 余談
今回初めてSASTとしてSnykを使ってみたのですが、かなり精度が高くて使い勝手が良かったです。
特にGithub Dependbotと比較するとSnykの方が多くパッケージの問題点を洗い出してくれました。

それと、記事を書いてから気がつきましたがSnykはContainer Scanningツールも存在するようなので記事の工程の4/5がSnykで実施できることになりますね....
https://dev.classmethod.jp/articles/check-the-runtime-version-in-the-images/


## 参考文献
* [徳丸先生の記事 脆弱性対応入門~古典的脆弱性診断からモダンなスキャンまで~](https://vuls-jp.connpass.com/event/290311/presentation/)
* [OWASP ZAPとGitHub Actionsで自動化する脆弱性診断 ](https://amzn.asia/d/9dLetTs)
* [trivy-actionを使ってDocker imageの脆弱性診断をやってみた](https://qiita.com/asmg07/items/4fd623c4804d5f68f36e)