.NET でも .NET でなくても静的セキュリティコード検査 (Semgrep 編)
.NET でも .NET でなくても静的セキュリティコード検査 (Semgrep 編)
前回の Security Code Scan 編に続いて静的セキュリティコード検査です。今回は Semgrep 編です。
Semgrep とは?
オープンソースのソースコードの静的解析ツールです。標準で脆弱性やバグの検査ルールがついてきますので、そのまま脆弱性の静的解析に使うことができます。ルールはカスタマイズが可能で、Semgrep Registry に登録されているルールを使うこともできます。
対応言語が多いのも特徴です。(残念ながら Visual Basic は非対応です) これらの言語のソースコードをまとめて処理できます。最近だと .NET + JavaScript などのように複数の言語を利用することが多いですが、一度のスキャンで両方のソースコードを解析します。
Semgrep はソースコードから構文解析木を作成し、それに対してパターンマッチングを行います。パターンマッチングルールが書きやすいのも特徴だと思います。
前回紹介した Security Code Scan はコンパイルされたバイナリを利用して (おそらくですが)、クラス階層なども追いかけて検査します。Semgrep は構文解析木ですので、検出率はさすがに劣ります。
使い方
Semgrep の使い方は次の通りです。
-
CLI -
pip
やbrew
でインストールして、semgrep
コマンドを利用します。 - Semgrep App - GitHub や GitLab のプロジェクトと連携するためのアプリです。
ここでは CLI 版を Docker で動作させる方法を紹介します。
適当なディレクトリを作成し、次の脆弱性のあるクラスを追加してください。(このコードはコンパイルは通りませんが、Semgrepは検査します)
using Microsoft.AspNetCore.Mvc;
class MyController
{
[HttpPost]
public IActionResult Save() {
}
}
実行します。
$ docker run --rm -v "${PWD}:/src" returntocorp/semgrep semgrep --config=auto
METRICS: Using configs from the Registry (like --config=p/ci) reports pseudonymous rule metrics to semgrep.dev.
To disable Registry rule metrics, use "--metrics=off".
Using configs only from local files (like --config=xyz.yml) does not enable metrics.
More information: https://semgrep.dev/docs/metrics
Semgrep rule registry URL is https://semgrep.dev/registry.
Scanning across multiple languages:
<multilang> | 52 rules × 2 files
csharp | 37 rules × 1 file
Blocking Findings:
MyController.cs
csharp.dotnet.security.mvc-missing-antiforgery.mvc-missing-antiforgery
Save is a state-changing MVC method that does not validate the antiforgery token or do
strict content-type checking. State-changing controller methods should either enforce
antiforgery tokens or do strict content-type checking to prevent simple HTTP request types
from bypassing CORS preflight controls.
Details: https://sg.run/Y0Jy
5┆ [HttpPost]
6┆ public IActionResult Save() {
7┆ }
Blocking Rules Fired:
csharp.dotnet.security.mvc-missing-antiforgery.mvc-missing-antiforgery
Ran 1035 rules on 1 file: 1 finding.
これだけです。前回と同じように [ValidateAntiForgeryToken]
属性をつけると脆弱性なしとなります。
レポートを出力する
Semgrepは検出結果のレポートの出力形式を選ぶことができます。
SARIF (静的解析の汎用レポート形式) 形式の JSON で semgrep.sarif
ファイルに出力してみます。
$ docker run --rm -v "${PWD}:/src" returntocorp/semgrep semgrep --config=auto --sarif --output=semgrep.sarif
SARIF 形式以外のフォーマットにも対応しています。
$ docker run --rm -v "${PWD}:/src" returntocorp/semgrep semgrep scan --help
...
Output formats: [mutually_exclusive]
Uses ASCII output if no format specified.
--emacs Output results in Emacs single-line format.
--json Output results in Semgrep's JSON format.
--gitlab-sast Output results in GitLab SAST format.
--gitlab-secrets Output results in GitLab Secrets format.
--junit-xml Output results in JUnit XML format.
--sarif Output results in SARIF format.
--vim Output results in vim single-line format.
検出できないものが多い
Security Code Scan だと次の a, b, c ともたぶん検出してくれると思うのですが、Semgrep だと単純なパターン (a) 以外は検出されませんでした。
using System.Diagnostics;
class Program
{
public void Run(string command)
{
Process.Start(command); // (a) 検出する
Process.Start(command + " -v"); // (b) 検出しない
var a = command;
Process.Start(a); // (c) 検出しない
}
}
このあたりは構文解析木方式の制約というより、Semgrep の C# の標準ルールの出来が悪いだけかもしれません。JavaScript だと結構頑張ってます。
var command1;
eval(command1); // (1) 検出する
var command2 = "ls";
eval(command2); // (2) 脆弱性でないと判断される
var command3;
eval(command3 + ' -v'); // (3) 検出する
var command4;
eval(`${command4} -v`); // (4) 検出する
masakura@sast:~/demo$
C# の global using にも対応していなさそうで、C# の最新機能を使うと検出率が落ちることもあるかもしれません。
まとめ
Semgrep は構文解析木を利用した静的コード解析ツールです。標準で脆弱性やバグの可能性のあるコードを検出します。
Security Code Scan の方が検出率は高いですが、Semgrep の方でしか検出できないものもあると思うので、両方利用するのもありかもしれません。
標準のルールだと検出できないものも多く、開発チームで独自のルールを書いた方がよさそうです。ルールを書くのはそれほど難しくはありません。公式ドキュメントの Writing rules や Semgrepを使った構文木ベースの検索と置換でコードのリファクタリングをする - Web Scratch が参考になります。
Discussion