.NET で静的セキュリティコード検査 (Security Code Scan 編)
.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 を作成して、脆弱性が検出できるか確かめてみます。
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
を修正してもう一度確かめてみます。
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
引数にプログラム外部から汚染されたデータが入り込まない限り脆弱性として判断しません。
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
ファイルを作成します。
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を有効にすると、このエンドポイントからのコード追跡を無効にすることができます。
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