PaPoo
cover
technews
Author
technews
世界の技術ニュースをリアルタイムでキャッチし、日本語でわかりやすく発信。AI・半導体・スタートアップから規制動向まで、グローバルテックシーンの「今」をお届けします。

Mavenを“セキュリティ対応”にする新しい発想:CI/CDのズレを減らすAppSecチェック入門

記事のキーポイント

この記事は何を問題にしているのか

この記事のテーマは、ひとことで言うと ​「Mavenのビルドを、ただのビルドではなく“セキュリティを意識したビルド”に変えよう」​ という話です。

ただし、著者が言っている問題は「Mavenでsecurity toolが使えない」という話ではありません。むしろ逆で、​使える。でも、うまく揃わない のが問題だとしています。

たとえば、こんな感じです。

この状態を著者は security build drift と呼んでいます。
直訳っぽく言うと「セキュリティ関連のビルド設定のズレ」です。

ここ、かなり本質的だと思います。
自動化って、動いているだけだと安心しがちですが、実際は「各所でちょっとずつ違う」ことがいちばん危ない。しかもその違いは、最初は小さいので気づきにくいんですよね。怖い。

よくあるCI/CD運用の“あるある”

image_0003.svg

記事では、典型的なMavenのCI/CD設定として次のような流れを例にしています。

./mvnw test
./mvnw org.owasp:dependency-check-maven:check
./mvnw org.cyclonedx:cyclonedx-maven-plugin:makeBom
./mvnw sonar:sonar

この形、1リポジトリだけならまあ回ります。
でも、サービスが増えると話が変わります。

つまり、​設定が散らばるんです。

散らばると何が起きるか。著者はこうまとめています。

これは地味だけど、かなり痛いです。
「動いてはいるけど信用できない」状態が、いちばんコスト高なんですよね。

著者の答え:Scannerを増やすのではなく、Mavenの中に組み込む

image_0004.svg

この問題に対して著者が作ったのが secure-maven-extension です。

ポイントは、​scannerを新しく作ったわけではない こと。
JaCoCo、SonarQube、Dependency-Check、CycloneDX といった既存ツールを使いながら、​Mavenのライフサイクルにsecurity workflowを埋め込む 発想です。

ここが面白いところです。

普通は、CI/CDのYAMLにコマンドを足していきます。
でもそれだと、プロジェクトごとに差が出やすい。
そこで著者は、​CI/CDの外ではなく、Maven自体を“セキュリティ対応の実行基盤”にする方向へ寄せています。

個人的には、この発想はかなり筋がいいと思います。
CI/CDは本来「実行する場所」であって、「毎回どう実行するかの細かい流儀」を抱え込むには重すぎるんですよね。
その流儀をMaven側に寄せるのは、かなり自然です。

なぜ core extension なのか

著者は、ただの Maven plugin ではなく core extension を選んでいます。

Maven pluginでも似たことはできますが、pluginだと各プロジェクトに明示的な設定が必要になりがちです。
それだと、結局 copy-paste が残る。

core extension は、もっと早い段階で Maven に入り込めます。
この拡張は .mvn/extensions.xml から読み込まれます。

image_0005.svg

<extensions>
  <extension>
    <groupId>io.github.niki1337.securebuild</groupId>
    <artifactId>secure-maven-extension</artifactId>
    <version>0.1.0</version>
  </extension>
</extensions>

そして、Mavenの afterProjectsRead というタイミングで動きます。

このタイミングが重要です。
ここでは、すでに root POM や module POM を読んだ後なので、

などが見えます。
でも、まだライフサイクルは始まっていない。

つまり、​実行前に“どういうセキュリティ設定を入れるか”を決められるわけです。
これはかなり賢い設計だと思います。

何を自動で面倒見てくれるのか

この extension がつなぐのは、主に以下のツールです。

image_0006.svg

SBOMは、ざっくり言うと「このソフトが何でできているかの部品表」です。
食品の原材料表示みたいなものだと思うと近いです。
セキュリティやサプライチェーン対策では、かなり重要です。

使い方は見た目上、普通の Maven コマンドのままです。

mvn package
mvn verify
mvn sonar:sonar

でも内部では、これらがセキュリティを意識した動きになります。

たとえば:

つまり、見た目は普通の Maven、でも中身は security-aware。
この「見た目は変えない」が、現場ではかなり効くと思います。

設定は1種類に押しつけない

ここも実務っぽくて良い点です。
現場って、きれいに一枚岩ではありません。

image_0007.svg

この記事の extension は、こうした複数の設定源を受け入れます。

つまり、設定の“書き方”を強制しない。
代わりに、​最終的に解決された挙動を揃えるのがゴールです。

この考え方はかなり現実的です。
理想論で「全部 pom.xml に統一しましょう」みたいに言われても、実際は難しいですからね。
環境ごとの事情を認めたうえで、結果だけを揃える。大人の設計です。

JaCoCoの面倒を自動で片付ける

Coverage(テスト網羅率)は、地味にAppSecの品質に効きます。
SonarQube自体は coverage なしでも動きますが、当然ながら分析の質は下がります。

よくある失敗は、

というパターンです。

著者の extension は、Java の jar/war project で JaCoCo が未設定なら自動注入します。
流れはこうです。

image_0008.svg

initialize -> jacoco:prepare-agent
verify -> jacoco:report

そして XML レポートは次の場所に出ます。

target/site/jacoco/jacoco.xml

さらに、そのパスを SonarQube の

sonar.coverage.jacoco.xmlReportPaths

に渡します。

このあたり、ほんとに「わざわざ手でやるほどではないけど、ミスると痛い」領域です。
だからこそ自動化向き。
著者の問題意識はここでかなり一貫しています。

SonarQubeは token だけでは足りない

SonarQube連携って、ありがちなのが「URL・project key・tokenがあればOKでしょ」という雑な理解です。
でも実際には、もっと情報が要ります。

image_0010.png

たとえば Java project では、

などが必要になります。

この記事の extension は、こうした設定をまとめて準備します。

sonar.sources
sonar.tests
sonar.java.binaries
sonar.java.test.binaries
sonar.coverage.jacoco.xmlReportPaths
sonar.exclusions
sonar.test.exclusions
sonar.cpd.exclusions
sonar.coverage.exclusions

さらに、GitLab の merge request pipeline では、CI変数を SonarQube の pull request 分析に対応させます。

CI_MERGE_REQUEST_IID -> sonar.pullrequest.key
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME -> sonar.pullrequest.branch
CI_MERGE_REQUEST_TARGET_BRANCH_NAME -> sonar.pullrequest.base

普通の branch pipeline なら branch analysis metadata を設定するとのことです。

ここは、手で pipeline に書くとすぐ壊れます。
だからこそ、​一度 core extension の中に閉じ込めて、バージョン管理された再利用可能な仕組みにする
この判断はかなり筋がいいと思います。

image_0012.png

Dependency-Checkの出力も揃える

Dependency-Check は、依存ライブラリの脆弱性を調べるツールです。
いわば「使っている部品に危ないものが入っていないか」を見るものですね。

著者は、これも lifecycle に組み込みます。

single-module: verify -> dependency-check:check
multi-module: verify -> dependency-check:aggregate

出力形式も統一します。

そして、レポートの出力先は

target/reports/dependency-check

に揃えるとのことです。

image_0013.png

さらに興味深いのは、デフォルトでネットワーク依存の analyzer を無効にしている点です。
RetireJS、Node audit、Node package analyzer、OSS Index、hosted suppressions などですね。

これは重要です。
外部サービスに依存すると、

ということが起こります。
セキュリティチェックは本来、できるだけ再現性が大事なので、ここを切り離すのは理にかなっています。

必要なら、Dependency-Track のような内部ミラーを使える設計も示されています。
DT_API_URL を使う例も出ていました。

この記事の面白さは「セキュリティの話なのに、実は運用の話」であること

個人的に、この文章でいちばん面白いのは、表向きは AppSec の話なのに、実態はかなり 開発者体験と運用設計の話 だという点です。

「脆弱性スキャンを入れよう」は誰でも言えます。
でも本当に難しいのは、そのあとです。

image_0014.png

ここを解決しないと、セキュリティはだんだん“形だけ”になります。
著者はそこをかなり正面から見ています。
地味だけど、すごく実務的です。

まとめ:security checkを「追加する」のではなく「ビルドの性質にする」

この記事の主張を一言でまとめるなら、
セキュリティチェックをCI/CDのオプションとして足すのではなく、Mavenビルドの基本動作にするべき
ということだと思います。

これによって、

ことを狙っています。

もちろん、どの現場でもすぐ同じやり方がハマるとは限りません。
でも、「scannerを足す」ではなく「ビルドにセキュリティを埋め込む」という発想は、かなり有望ではないかと思います。

特に、複数サービスを運用している組織ほど、この手の drift はじわじわ効いてきます。
なので、この記事は単なるツール紹介というより、​**“セキュリティを運用可能な形にする設計論”** として読むとかなり面白いです。


参考: Making Maven Builds Security-Aware: AppSec Checks Without CI/CD Drift

同じ著者の記事