👻

.NET でも .NET でなくても静的セキュリティコード検査 (Semgrep 編)

2022/11/25に公開

.NET でも .NET でなくても静的セキュリティコード検査 (Semgrep 編)

前回の Security Code Scan 編に続いて静的セキュリティコード検査です。今回は Semgrep 編です。

Semgrep とは?

オープンソースのソースコードの静的解析ツールです。標準で脆弱性やバグの検査ルールがついてきますので、そのまま脆弱性の静的解析に使うことができます。ルールはカスタマイズが可能で、Semgrep Registry に登録されているルールを使うこともできます。

対応言語が多いのも特徴です。(残念ながら Visual Basic は非対応です) これらの言語のソースコードをまとめて処理できます。最近だと .NET + JavaScript などのように複数の言語を利用することが多いですが、一度のスキャンで両方のソースコードを解析します。

Semgrep はソースコードから構文解析木を作成し、それに対してパターンマッチングを行います。パターンマッチングルールが書きやすいのも特徴だと思います。

前回紹介した Security Code Scan はコンパイルされたバイナリを利用して (おそらくですが)、クラス階層なども追いかけて検査します。Semgrep は構文解析木ですので、検出率はさすがに劣ります。

使い方

Semgrep の使い方は次の通りです。

  • CLI - pipbrew でインストールして、semgrep コマンドを利用します。
  • Semgrep App - GitHub や GitLab のプロジェクトと連携するためのアプリです。

ここでは CLI 版を Docker で動作させる方法を紹介します。

適当なディレクトリを作成し、次の脆弱性のあるクラスを追加してください。(このコードはコンパイルは通りませんが、Semgrepは検査します)

MyController.cs
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 rulesSemgrepを使った構文木ベースの検索と置換でコードのリファクタリングをする - Web Scratch が参考になります。

Discussion