GitLabを使って秒で始めるDevSecOps

# DevSecOpsの定義
[Wikipedia](https://en.wikipedia.org/wiki/DevOps#DevSecOps,_Shifting_Security_Left)によると、「自動化されたDevOpsでセキュリティのベストプラクティス...」というバズワードをバズワードで説明されていて、具体的ではなかったので、個人的な独断と偏見で狭義のDevSecOpsを定義しちゃいます。

**DevSecOpsとは、フィーチャーブランチでセキュリティスキャンを自動化または半自動化し、ターゲットブランチにマージする前にアプリケーションセキュリティ（AppSec）の問題が解消した状態を、開発チームとして目指すことです。**

DevOpsのプロセスに組み込むという概念もかなり重要な要素ですが、どのタイミングで実施するかを具体的に明示すると、チームとしての方向性も共有しやすいと考えます。
この定義に当てはめて、以下のそれぞれの状態をDevSecOpsかどうか勝手に判定します。

| 状態 | DevSecOpsか否か |
|:-|:-:|
| SCA系の脆弱性に関する有名なニュースが出てから社内で一斉捜査が始まる | N  |
| リリース前にセキュリティチームによる診断のみを実施している | N  |
| マージ後のブランチのみでSASTを実行している  | N  |
| フィーチャーブランチでSCAをかけているが、ニュースになっていないような脆弱性は報告するとやぶ蛇なので見て見ぬ振りをしている  | N |
| フィーチャーブランチでSCAをかけて可能な限り解消し、マージ後も定期的にSCAを実行している  | Y |
| もうSASTが何を指摘してくるか完全に理解したので、そもそも誤検出すら出ないようなコードを基本的にコミットしている  | Y |
| なるべく同梱されているモジュールの少ないかつ最新のDockerイメージをベースイメージとして採用している  | Y |
| 言語ごと、セキュリティテストごとの指摘解消のパターン集をチーム内で共有している | Y |

# まずは秒で始めてみる
DevSecOpsを組み込む方法は多種多様ですが、最も手軽に早く開始でき、現実的なプロセスを回せるプラットフォームとして、GitLab（特に[GitLab.com](https://gitlab.com/)）を使います。たった数行のCI定義の追記で各種セキュリティテストを開始できます。

## 前提条件
以下のいずれかの方法で、DevSecOps機能がフルに利用できる状態のGitLab.comまたはSelf-managedのGitLab Enterprise Editionを用意します。
1. GitLabのUltimateを購入する。
2. [Ultimateの無料トライアル](https://about.gitlab.com/free-trial/)をする。
3. [GitLab.comの公開プロジェクトとして試す。](https://docs.gitlab.com/ee/subscriptions/gitlab_com/#:~:text=All%20GitLab%20SaaS%20public%20projects%2C%20regardless%20of%20the%20subscription%2C%20get%20access%20to%20features%20in%20the%20Ultimate%20tier)（ソースコードがインターネット公開されるので、用途としてはサンプルプロジェクトやOSSプロジェクトに限定されます。）
※Self-managedの場合は、Docker系[Executor](https://docs.gitlab.com/runner/executors/#selecting-the-executor)のGitLab Runner（つまりDockerまたはKubernetes Executor）を導入し、[GitLab Serverと連携](https://docs.gitlab.com/runner/register/index.html#linux)しておきます。

## 既存のプロジェクトを使った開始手順
マージリクエスト内で以下を`.gitlab-ci.yml`に追記し、終了です。

```yml
include:
  - template: Security/SAST.gitlab-ci.yml #SASTを実行します。
  - template: Security/Secret-Detection.gitlab-ci.yml #Secret Detectionを実行します。
  - template: Security/Dependency-Scanning.gitlab-ci.yml #Dependency Scanningを実行します。
```

## サンプルプロジェクトを使った開始手順
適切な既存プロジェクトがない場合は、以下の手順にしたがっても試せます。熟練するとと45秒ほどで完了します！
やっていることは、[Spring Petclinic](https://github.com/spring-projects/spring-petclinic)というSpring BootのサンプルプロジェクトをGitLabへインポートし、フィチャーブランチ（マージリクエスト）内で4行のGitLab用のCI定義を追加し、DevSecOpsを開始するうえで基本的な静的系セキュリティテストを自動実行させます。
※Spring PetclinicとGitLabの各種セキュリティスキャナーは双方日々アップデートされているため、最新状態では実行エラーが発生する可能性があり、渡すパラメータ等の調整が必要になることがあります。

### サンプルプロジェクトのインポート
1. [New project]ボタンで新規のGitLabプロジェクトを作成します。
1. [Import project]をクリックします。
1. [Repo by URL]をクリックします。
1. `https://github.com/spring-projects/spring-petclinic.git`を[Git repository URL]へ入力し、[Create project]ボタンをクリックします。

### イシュー、マージリクエスト、ブランチの作成
1. プロジェクトのインポートが完了したら、左側のメニューから[Issues] > [Boards]をクリックします。（マージリクエストとブランチはイシューから作成するのが一番楽かつCIの早期実行の観点からも有益です。）
1. ボードの[+]ボタンをクリックし、"Start DevSecOps"などというタイトルで新規のイシューを作成します（[Create issue]ボタンのクリック）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/4f8d6132-7b98-036c-8d64-e111eccc7675.png)
1. "Start DevSecOps"のタイトル部分をクリックし、イシューを開きます。
1. [Create merge request]ボタンをクリックし、CI定義編集用の新規のマージリクエストとブランチを同時に作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/c7740d30-2a20-fdfe-ce14-800aea48bbc3.png)

### セキュリティスキャンのCIジョブ追加
1. [Open in Web IDE]をクリックしてWeb IDEを起動し、Web IDEページの左上の新規ファイル作成用のアイコンをクリックし、`.gitlab-ci.yml`をクリックし、リポジトリルート直下に`.gitlab-ci.yml`の空ファイルの作成をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/448ccfdd-df26-d527-313c-592eb5f7381d.png)
1. `.gitlab-ci.yml`以下の~~4行~~6行を追加し、コミットします。（[Commit]ボタンを2回クリックすることになります。）

```yml
include:
  - template: Security/SAST.gitlab-ci.yml #SASTを実行します。
  - template: Security/Secret-Detection.gitlab-ci.yml #Secret Detectionを実行します。
  - template: Security/Dependency-Scanning.gitlab-ci.yml #Dependency Scanningを実行します。
variables:
  SAST_JAVA_VERSION: "11" #SASTのビルドで使用するJavaバージョンを11にします。最新のSpring Petclinicはデフォルトのバージョン8だと失敗します。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/c9bc6e5e-e0f5-afd1-221a-6ebb93151aa8.png)

### セキュリティスキャンの確認
1. ページの最下方の[Merge request !(数字)]のリンクから先ほどのマージリクエストへ戻ります。
1. 追加したCI定義により実行されたCI/CDパイプラインを確認すると、次のようにフィーチャーブランチに対し、SAST（JavaScript用とJava用の２つ）、Secret Detection、Dependency Scanningのジョブが自動的に実行されているのが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/4b6c974a-fe44-4cce-cdd3-077c19d0c2b3.png)
1. マージリクエストやパイプラインのセキュリティタブでは以下のような表示で脆弱性が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/90e46039-7ec3-39c4-85c1-328cfa5a7d56.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/2905e3a1-c7dd-3c29-ebca-3c9b19cc851a.png)


# GitLabのセキュリティスキャンついて
今回はシンプルに始められるセキュリティテスト3種類のみを加えましたが、実際にはGitLabでは以下の豊富なセキュリティテストも利用できます。ここからも[GitLabがDevSecOpsに力を入れている](https://about.gitlab.com/solutions/dev-sec-ops/)ことが分かります。

スキャンの仕組みとしては、GitLabが公式提供するテストタイプごとの最新のスキャン用イメージが上記で`include:`したCIテンプレートで呼び出されて、言語やパッケージマネージャを検出して実行されます。これだけのスキャンエンジンを各チームが自分たち手動で常に最新にアップデートし、CI環境と連携するのをメンテナンスするのはかなり大変です。

| セキュリティテストタイプ | 分類 | 説明| 定義例 | Tips|
|:--|:-:|:--|:--|:--|
| [SAST](https://docs.gitlab.com/ee/user/application_security/sast/) | 静的スキャン  | リポジトリに格納しているソースコードの言語を自動検出し、脆弱性を検出します。| 基本はCIテンプレートを1行指定するのみ。|どんなSASTでも誤検出（FP）は発生します。脆弱性のトリアージ（選別）はコストが高い作業であるため、[誤検出含め指摘を正しく解消するようなコードを日々コミットするのがベストプラクティス](https://youtu.be/eL62jCj65Js?t=1291)という考えを推奨します。 |
| [Secret Detection](https://docs.gitlab.com/ee/user/application_security/secret_detection/)  | 静的スキャン | SASTの一種でハードコードされたパスワードやアクセスキーを検出します。   | 基本はCIテンプレートを1行指定するのみ。 |
| [Dependency Scanning](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/)  | 静的スキャン | リポジトリに直接格納していないOSSなどの外部依存ライブラリの既知の脆弱性を検出します。利用するパッケージマネージャーは自動的に検出します。（Apache Log4j、Python Requestsなど） | 基本はCIテンプレートを1行指定するのみ。 |依存するJavaScriptライブラリは、リポジトリに直置きではなく、NPMパッケージなどを使って依存関係で管理するとこのスキャナで正しく脆弱性を検出されます。脆弱性の特性上、マージ後、リリース後にも新規に発覚するため、継続的な定期スキャンが必要です。|
| [Container Scanning](https://docs.gitlab.com/ee/user/application_security/container_scanning/)  | 静的スキャン | Dependency ScanningのDockerイメージ版。OSにインストールするレイヤーのライブラリを検出します。（OpenSSL、NGINXなど）| 前段でGitLab Docker Registryにコンテナイメージをpushするジョブを組んだうえで、CIテンプレートを1行指定。 |Slim系、Alpine系のイメージなど、なるべくライブラリの少ないをイメージを採用すると日々指摘される量を軽減できます。脆弱性の特性上、マージ後、リリース後にも新規に発覚するため、継続的な定期スキャンが必要です。|
| [IaC Scanning](https://docs.gitlab.com/ee/user/application_security/iac_scanning/) | 静的スキャン | Terraform, Ansible, Kubernetes Manifest,AWS CloudFormationなどのインフラ構築コードのセキュリティの問題を検出します。 | 基本はCIテンプレートを1行指定するのみ。 |インフラ構築前にセキュリティの問題を指摘できる存在は貴重です。|
| [Coverage-guided fuzz testing](https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing/) | ランタイムスキャン  | ホワイトボックス的に関数、メソッドにランダムな引数を渡し、予期せぬエラーや例外をあぶり出すようなテストです。  | CIテンプレートを1行指定する以外に、対応言語により異なるscriptの設定が必要。 |コード品質チェックの観点としても活用できます。|
| [DAST](https://docs.gitlab.com/ee/user/application_security/dast/)  | 動的スキャン  | 起動中のWebアプリに対しブラックボックス的に脆弱性を検出します。  | スキャン対象のURLをCI変数か`environment_url.txt`で指定し、CIテンプレートを1行指定。|Kubernetes等動的にデプロイしたアプリケーションのURLを払い出せる仕組みがあれば、フィチャーブランチで実施がスムーズになります。|
| [DAST browser-based crawler](https://docs.gitlab.com/ee/user/application_security/dast/browser_based.html) | 動的スキャン | 上記のDASTが苦手とするSPAなどJavaScriptを多用したモダンなサイトを対象とした場合に有効なDASTスキャナです。  | DASTの定義に加え、`DAST_BROWSER_SCAN: "true"`を追記。 |Kubernetes等動的にデプロイしたアプリケーションのURLを払い出せる仕組みがあれば、フィチャーブランチで実施がスムーズになります。|
| [DAST API](https://docs.gitlab.com/ee/user/application_security/dast_api/) | 動的スキャン  | Web APIに特化したDASTです。 | スキャン対象のURLをCI変数か`environment_url.txt`で指定し、Web APIの定義ファイルを渡し、CIテンプレートを1行指定。|Kubernetes等動的にデプロイしたアプリケーションのURLを払い出せる仕組みがあれば、フィチャーブランチで実施がスムーズになります。|
| [Web API Fuzz Testing](https://docs.gitlab.com/ee/user/application_security/api_fuzzing/) | 動的スキャン  | 異常なHTTPリクエストやパラメータを生成し、500 Internal Server ErrorなどWeb APIの予期せぬエラーをあぶり出すようなテストです。  | スキャン対象のURLをCI変数か`environment_url.txt`で指定し、Web APIの定義ファイルを渡し、CIテンプレートを1行指定。 |Kubernetes等動的にデプロイしたアプリケーションのURLを払い出せる仕組みがあれば、フィチャーブランチで実施がスムーズになります。|
|[Cluster Image Scanning](https://docs.gitlab.com/ee/user/application_security/cluster_image_scanning/index.html)| 動的スキャン  |Kubernetesクラスタのリソースや環境に関する脆弱性を検出します。   | Kubernetesクラスタに専用のサービスをデプロイしておく必要あり。 ||



# 実際に脆弱性を追加してみる
Spring Petclinicは日々更新されているため、最新の断面で試すとクリティカルな脆弱性はほとんど検出されないはずです。わざと重大な脆弱性を加えて本当に検出されるか試してみます。

1. `pom.xml`にApache Log4jの脆弱性であるLog4shell（[CVE-2021-44228](https://nvd.nist.gov/vuln/detail/CVE-2021-44228), [CVE-2021-45046](https://nvd.nist.gov/vuln/detail/CVE-2021-45046)）を追加し、再度CI/CDパイプラインを実行します。

```xml
...
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version>
    </dependency>
...
```

2. ちゃんと検出されていることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/fd0fdd8e-5ff4-b793-521a-0b429a9e5e0c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149690/5ad6dbfe-53ad-523d-5c3f-b183f462bb38.png)

# まとめ
GitLabのDevSecOpsは、Built-inかつOut of the boxで開始できることが分かったと思います。

# 参考URL
1. [Secure your application | GitLab](https://docs.gitlab.com/ee/user/application_security/)
1. [脆弱性対応勉強会Expansion 第04回（アジャイルセキュリティ）](https://www.youtube.com/watch?v=eL62jCj65Js)