📑

.NET で静的セキュリティコード検査 (Security Code Scan 編)

2022/09/25に公開

.NET で静的セキュリティコード検査 (Security Code Scan 編)

みなさん、脆弱性を意識してプログラミングしていますか? チーム開発だと全員がそれができることは少ないと思います。コードレビューや設計レビューで脆弱性をいち早く発見し、修正することが大事です。

レビューであらゆる脆弱性を検査するのがかなり大変です。コンピューターで機械的に脆弱性を検出できれば、その分レビューが楽になります。このシリーズでは、.NET の脆弱性の静的解析ツールをいくつか紹介していこうと思います。

Security Code Scan

Security Code Scan はオープンソースの脆弱性の静的コード検査ツールです。Visual Studio 拡張のほか、プロジェクトに組み込んだり、スタンドアローンの Runner を利用したりできます。

Security Code Scan は、C# だけでなく Visual Basic にも対応していて、.NET (Core) だけでなく .NET Framework にも対応しています。

Security Code Scan でスキャンする

プロジェクトへ組み込む

Security Code Scan の私のおすすめの使い方はプロジェクトの組み込みです。

まず、脆弱性検査をする対象のプロジェクトを作成します。

> mkdir Project1
> cd Project1
> dotnet new mvc

次のようにして、SecurityCodeScan.VS2019 NuGet パッケージをプロジェクトに組み込みます。

> dotnet add package SecurityCodeScan.VS2019

あとは dotnet build をするだけです。もちろん、この時点では脆弱性はありませんので、何も検出されませんが。

> dotnet build

...

ビルドに成功しました。
    0 個の警告
    0 エラー


経過時間 00:00:08.60

脆弱性のある MVC Controller を作成して、脆弱性が検出できるか確かめてみます。

Controllers/MyController.cs
using Microsoft.AspNetCore.Mvc;

namespace Project1.Controllers;

public class MyController : Controller {
    [HttpPost]
    public void Post() {}
}

もう一度ビルドします。

> dotnet build

...

C:\Users\masakura\tmp\Project1\Controllers\MyController.cs(7,17): warning SCS0016: Controller method is potentially vulnerable to Cross Site Request Forgery (CSRF). [C:\Users\masakura\tmp\Project1\Project1.csproj]
  Project1 -> C:\Users\masakura\tmp\Project1\bin\Debug\net7.0\Project1.dll
ビルドに成功しました。

...

見ての通り、MyController.cs で脆弱性 SCS0016 - Cross-Site Request Forgery (CSRF) が検出されました。

CSRF がどういった脆弱性かというのはこのブログでは解説しませんが、かなりやばいやつです。ValidateAntiForgeryToken をつけて、Anti Forgery Token を検証する必要があります。

MyController.cs を修正してもう一度確かめてみます。

Controllers/MyController.cs
using Microsoft.AspNetCore.Mvc;

namespace Project1.Controllers;

public class MyController : Controller {
    [HttpPost]
    [ValidateAntiForgeryToken]
    public void Post() {}
}
> dotnet build

...

ビルドに成功しました。
    0 個の警告
    0 エラー

経過時間 00:00:07.75

無事、脆弱性が解消されました!

このプロジェクトに組み込む方法は、Visual Studio だけでなく、Visual Studio Code や JetBrains Rider でも使うことができます。また、テストプロジェクトには SecurityCodeScan.VS2019 を組み込まないといったこともできます。

.NET Tools 版

コマンドライン版 (.NET Tools 版) も用意されています。

あなたのプロジェクトやソリューションで .NET Tools を有効にします。

> dotnet new tool-manifest

コマンドライン版をインストールします。

> dotnet tool install security-scan

スキャンします。

> dotnet security-scan YourApp.sln

...


╔═╗┌─┐┌─┐┬ ┬┬─┐┬┌┬┐┬ ┬  ╔═╗┌─┐┌┬┐┌─┐  ╔═╗┌─┐┌─┐┌┐┌
╚═╗├┤ │  │ │├┬┘│ │ └┬┘  ║  │ │ ││├┤   ╚═╗│  ├─┤│││
╚═╝└─┘└─┘└─┘┴└─┴ ┴  ┴   ╚═╝└─┘─┴┘└─┘  ╚═╝└─┘┴ ┴┘└┘

.NET tool by Jaroslav Lobačevski v5.6.7


Resolve         0:00.2294254    WebApp1.csproj (net6.0)
Finished loading solution 'WebApp1/WebApp1.csproj'
Found: /home/masakura/tmp/App1/WebApp1/Controllers/HomeController.cs(17,17): warning SCS0016: Controller method is potentially vulnerable to Cross Site Request Forgery (CSRF).
Completed in 00:00:04
1 warnings

コマンドを実行するとヘルプが表示されます。

> dotnet security-scan

╔═╗┌─┐┌─┐┬ ┬┬─┐┬┌┬┐┬ ┬  ╔═╗┌─┐┌┬┐┌─┐  ╔═╗┌─┐┌─┐┌┐┌
╚═╗├┤ │  │ │├┬┘│ │ └┬┘  ║  │ │ ││├┤   ╚═╗│  ├─┤│││
╚═╝└─┘└─┘└─┘┴└─┴ ┴  ┴   ╚═╝└─┘─┴┘└─┘  ╚═╝└─┘┴ ┴┘└┘

.NET tool by Jaroslav Lobačevski v5.6.7

...

プロジェクトをやルールを除外したり、設定を変更したりして実行ができます。

Security Code Scan のちょっとすごいところ

コードを追いかけて検出する

Security Code Scan は単純なパターンマッチングというわけではなく、コードを実際に追いかけているようです。

次の関数はコマンドインジェクションの脆弱性がありますが、fileName 引数にプログラム外部から汚染されたデータが入り込まない限り脆弱性として判断しません。

RunCommand.cs
public static class RunCommand {
    public static void Run(string fileName)
    {
        Process.Start(fileName);
    }
}

ASP.NET Core MVC Controller で notepad.exe を起動するように書いても、アプリケーションとしては脆弱性はありませんので、脆弱性とみなしません。

public class CommandInjectionController : Controller {
    [HttpGet]
    public void Index()
    {
        RunCommand.Run("notepad.exe");
    }
}

次のように、外部から汚染されたデータがわたってきた場合のみ脆弱性として検出します。

public class CommandInjectionController : Controller {
    [HttpGet]
    public void Index(string fileName)
    {
        RunCommand.Run(fileName);
    }
}

MVC と API の Controller を区別する

一般的に RESTful API では Anti Forgery Toke による CSRF 対策をしません。ASP.NET (Core) MVC と ASP.NET (Core) API の Controller はとても良く似ています。Security Code Scan は MVC か API かを区別しているようで、API Controller では CSRF を検出しません。

次のコードは ValidateAntiForgeryToken 属性が付いていませんが、脆弱性とは検出されません。

[ApiController]
public class MyApiController : ControllerBase {
    [HttpPost]
    public string Post() => "Hello!";
}

どのような脆弱性が検出できるのか

静的検査ツールがどのような脆弱性を検出して、逆に検出できないのかを理解することはとても重要です。

Security Code Scan は Rules に検出する脆弱性がまとめられています。

Security Code Scan はコードをある程度追いかけていますので、単純なパターンマッチのみの静的検査ツールに比べると、検出率は高めです。

こんな普通じゃありえないコードでも検出してくれます。

public class HogeAttribute : HttpPostAttribute {}

public class MyController : Controller {
    [Hoge]
    public void Post() {}
}

しかし、あらゆる CSRF を検出してくれるわけではありません。ASP.NET Core Middleware での CSRF は検出できません。

app.MapPost("/abc", _ => Task.CompletedTask);

SQL Injection も ADO.NET や Entity Framework (Core) には対応していますが、サードパーティーの ORM の Dapper には対応していません。

静的解析である以上、OWASP Top 10 (2021) の第一位のアクセス制御の不備も検出もできません。

どちらかというと、システムの脆弱性のうち、検出できない方が多いのです。検出できない部分は、ほかの動的解析ツールに頼ったり、レビュー (脆弱性診断含む) を頼ることになります。

カスタマイズ

Security Code Scan の設定方法は External Configuration File に書かれています。

グローバル設定もできますが、プロジェクト個別での設定もできます。

プロジェクトの直下に SecurityCodeScan.config.yml ファイルを作成します。

SecurityCodeScan.config.yml
Version: 3.1

// ...

このファイルのビルドアクションを AdditionalFiles にします。csproj ファイルに次を加えてください。

<Project Sdk="Microsoft.NET.Sdk.Web">

<!-- ... -->

  <ItemGroup>
    <None Remove="SecurityCodeScan.config.yml" />
    <AdditionalFiles Include="SecurityCodeScan.config.yml" />
  </ItemGroup>

</Project>

TIPS

クラスライブラリでも検出できるようにする

Security Code Scan はコードを追いかけて脆弱性を検出しますが、これが邪魔になることもあります。クラスライブラリプロジェクトでは ASP.NET (Core) のページや API がありませんので、脆弱性を検出できません。

Audit Modeを有効にすると、このエンドポイントからのコード追跡を無効にすることができます。

SecurityCodeScan.config.yml
Version: 3.1

AuditMode: true

// ...

ほかにも、エンドポイントを設定ファイルに書く方法もあるようですが... 大変そう...

レポートをファイルに出力する

コマンドライン版では、詳細な脆弱性レポートを SARIF 形式で出力することができます。SARIF 形式は静的解析結果の共通ファイルフォーマットで、様々なツールが対応しています。

> dotnet security-scan --export=report.sarif YourApp.sln

次のようなレポートが出力されます。

{
  "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json",
  "version": "2.1.0",
  "runs": [
    {
      "results": [
        {
          "ruleId": "SCS0016",
          "ruleIndex": 0,
          "level": "warning",
          "message": {
            "text": "Controller method is potentially vulnerable to Cross Site Request Forgery (CSRF)."
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "file:///home/masakura/tmp/App1/WebApp1/Controllers/HomeController.cs"
                },
                "region": {
                  "startLine": 17,
                  "startColumn": 17,
                  "endLine": 17,
                  "endColumn": 21
                }
              }
            }
          ],
          "properties": {
            "warningLevel": 1
          }
        }
// ...

脆弱性を無視する

明らかに脆弱性ではないが、脆弱性とマークされることもあります。また、局所的には脆弱性のある書き方をすることもあります。

プロジェクトが小さいうちは、ここは無視して OK! とすることもできますが、普通の開発プロジェクトではこんな方法をとると、脆弱性を見過ごすことが多くなります。

脆弱性の指摘を無視するには #pragma warning を利用します。

public static class RunCommand {
    public static void Run(string fileName)
    {
#pragma warning disable SCS0001
        Process.Start(fileName);
#pragma warning restore SCS0001
    }
}

まとめ

.NET 用の Seucirty Code Scan は、アプリケーションの脆弱性を検出するための静的解析ツールです。ソースコードを追いかけますので、一般的なパターンマッチングによる静的解析ツールと比べて誤検出や未検出は少なめだと思われます。

この手の検査ツールの常としてあらゆる脆弱性が検出できるわけではありませんし、どちらかというと検出できないことの方が多いのですが、簡単に利用できますし、少しでも検出できればレビューの手間を少なからず軽減できます。

ぜひ活用してください!

Discussion