C# Tips | 文字列処理:SQLインジェクション対策

C# C#
スポンサーリンク

はじめに 「SQLインジェクション対策」は“文字列をいじる話ではなく、SQLの組み立て方の話”

「文字列処理のユーティリティ」と聞くと、
「シングルクォートを2つに置き換えるエスケープ関数を作ればいいのかな?」
と考えがちですが、SQLインジェクション対策は本質的には 文字列エスケープの話ではありません

本質はただ一つです。

SQL文と値(パラメータ)を、絶対に文字列連結で混ぜないこと。

C# では「パラメータ化クエリ」を使うことで、
ユーザー入力を安全にSQLに渡すことができます。

ここでは、初心者向けに、

SQLインジェクションがなぜ起きるのか
やってはいけない「文字列連結」と「自前エスケープ」
正しい「パラメータ化」の書き方と、ユーティリティ化の考え方
LIKE検索などで“だけ”文字列処理が必要になる場面

を、例題付きでかみ砕いて説明していきます。


なぜSQLインジェクションが起きるのか

文字列連結でSQLを組み立てると何が起きるか

まず、典型的な「危ないコード」を見てみます。

string userName = GetUserInput(); // ユーザー入力
string sql = "SELECT * FROM Users WHERE Name = '" + userName + "'";

using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand(sql, connection);

connection.Open();
using var reader = command.ExecuteReader();
C#

一見すると、「Name = ‘入力された名前’」という条件を作っているだけに見えます。

しかし、ユーザーが次のような文字列を入力したらどうなるでしょうか。

' OR 1=1 --

すると、SQLはこうなります。

SELECT * FROM Users WHERE Name = '' OR 1=1 --'

OR 1=1 によって条件が常に真になり、
-- 以降はコメント扱いになってしまいます。

結果として、「全ユーザーが取得される」など、
開発者の意図しないSQLが実行されてしまいます。
これが SQLインジェクションです。


原則:文字列エスケープではなく「パラメータ化」

良くない「SQLエスケープユーティリティ」の発想

よくある誤解として、

「シングルクォートを '' に置き換えるユーティリティを作れば安全なのでは?」

という発想があります。

例えば、こんな感じです。

public static string EscapeSqlLiteral(string value)
{
    return value.Replace("'", "''");
}
C#

そして、

string userName = EscapeSqlLiteral(GetUserInput());
string sql = $"SELECT * FROM Users WHERE Name = '{userName}'";
C#

一見マシになったように見えますが、
これは「DBごとの仕様」「文字コード」「照合順序」「他の特殊文字」などを考え始めると、
途端に破綻しやすいアプローチです。

何より、「SQL文と値を文字列として混ぜている」という構造自体が危険です。

正しい発想:「SQL」と「値」を分ける

C# での正しい対策は、パラメータ化クエリを使うことです。

SQL文の中には「プレースホルダ(@Name など)」だけを書き、
実際の値は SqlParameter として別に渡します。

string userName = GetUserInput();

string sql = "SELECT * FROM Users WHERE Name = @Name";

using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand(sql, connection);

command.Parameters.Add(new SqlParameter("@Name", SqlDbType.NVarChar) { Value = userName });

connection.Open();
using var reader = command.ExecuteReader();
C#

ここで重要なのは、

SQL文は常に固定の文字列
ユーザー入力は「パラメータ」として別枠で渡す

という構造になっていることです。

DBドライバが「値を適切にエスケープして安全に渡す」処理をやってくれるので、
自分で文字列をいじる必要はありません。


ADO.NETでのパラメータ化クエリをユーティリティ化する

SqlCommand にパラメータを追加する基本形

パラメータ追加の基本形は次の通りです。

var command = new SqlCommand(sql, connection);
command.Parameters.Add("@Name", SqlDbType.NVarChar).Value = userName;
command.Parameters.Add("@Age", SqlDbType.Int).Value = age;
C#

これを毎回手で書いてもいいのですが、
業務システムでは「似たようなコード」が大量に出てきます。

そこで、「パラメータ追加を少しだけ楽にするユーティリティ」を用意すると便利です。

シンプルなパラメータ追加ユーティリティ

using System.Data;
using Microsoft.Data.SqlClient; // または System.Data.SqlClient

public static class SqlCommandExtensions
{
    public static SqlParameter AddParam(this SqlCommand command, string name, SqlDbType type, object? value)
    {
        var param = command.Parameters.Add(name, type);
        param.Value = value ?? DBNull.Value;
        return param;
    }
}
C#

使い方はこうなります。

string sql = "SELECT * FROM Users WHERE Name = @Name AND Age >= @Age";

using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand(sql, connection);

command.AddParam("@Name", SqlDbType.NVarChar, userName);
command.AddParam("@Age", SqlDbType.Int, minAge);

connection.Open();
using var reader = command.ExecuteReader();
C#

ここでのポイントは、

パラメータ追加を1行で書ける
null のときに DBNull.Value を自動で入れてくれる

といった「ちょっとした楽さ」です。

重要なのは、“文字列をエスケープするユーティリティ”ではなく、“パラメータを追加するユーティリティ”を作ることです。


LIKE検索やワイルドカードでだけ、文字列処理が必要になる

LIKE検索の基本形

名前の部分一致検索などで、次のようなSQLを書きたくなることがあります。

SELECT * FROM Users WHERE Name LIKE '%山田%'

これをパラメータ化すると、こうなります。

string keyword = GetUserInput(); // 例: "山田"

string sql = "SELECT * FROM Users WHERE Name LIKE @Pattern";

using var connection = new SqlConnection(connectionString);
using var command = new SqlCommand(sql, connection);

string pattern = "%" + keyword + "%";
command.Parameters.Add("@Pattern", SqlDbType.NVarChar).Value = pattern;
C#

ここで初めて、「文字列連結」が出てきます。
ただし、連結しているのは SQL文ではなく、パラメータの値 です。

SQL文自体は "WHERE Name LIKE @Pattern" のまま固定なので、
インジェクションの危険はありません。

ワイルドカード文字(% と _)をエスケープしたい場合

ユーザー入力に %_ が含まれていると、
LIKE のワイルドカードとして解釈されてしまいます。

「入力された文字列を“そのまま”検索したい」場合は、
%_ をエスケープする必要があります。

例えば、SQL Server なら ESCAPE 句と組み合わせて、
次のようなユーティリティを作れます。

public static class SqlLikeUtil
{
    public static string EscapeLikePattern(string value, char escapeChar = '\\')
    {
        if (string.IsNullOrEmpty(value))
        {
            return value;
        }

        string s = value
            .Replace(escapeChar.ToString(), escapeChar.ToString() + escapeChar)
            .Replace("%", escapeChar + "%")
            .Replace("_", escapeChar + "_");

        return s;
    }
}
C#

使い方はこうです。

string keyword = GetUserInput(); // 例: "100%達成"

string escaped = SqlLikeUtil.EscapeLikePattern(keyword);
string pattern = "%" + escaped + "%";

string sql = "SELECT * FROM Goals WHERE Title LIKE @Pattern ESCAPE '\\'";

using var command = new SqlCommand(sql, connection);
command.Parameters.Add("@Pattern", SqlDbType.NVarChar).Value = pattern;
C#

ここでの文字列処理は、あくまで「LIKE のワイルドカード制御」のためであり、
SQLインジェクション対策の本体ではありません。
本体はあくまで「パラメータ化」です。


ORマッパー(Dapper / EF Core)を使う場合の考え方

Dapper の例

Dapper のようなマイクロORMを使うと、
パラメータ化はさらに簡単になります。

string sql = "SELECT * FROM Users WHERE Name = @Name";

var users = connection.Query<User>(sql, new { Name = userName });
C#

ここでも、@Name はパラメータとして扱われ、
Dapper が内部で安全に値をバインドしてくれます。

自分で「SQLエスケープユーティリティ」を書く必要はありません。

EF Core の例

Entity Framework Core では、
LINQ を書くだけで内部的にパラメータ化されたSQLが生成されます。

var users = dbContext.Users
    .Where(u => u.Name == userName)
    .ToList();
C#

このとき、生成されるSQLはだいたい次のような形です。

SELECT ... FROM Users WHERE Name = @__userName_0

ここでも、@__userName_0 はパラメータです。
LINQ を素直に書いている限り、SQLインジェクションの心配はほぼありません。


まとめ 「SQLインジェクション対策ユーティリティ」は“文字列をいじる道具”ではなく“パラメータを徹底させる仕組み”

SQLインジェクション対策は、「危険な文字をエスケープする」話ではなく、
「SQLと値を絶対に文字列連結しない」ための設計と習慣の話です。

押さえておきたいポイントは、

SQL文にユーザー入力を +$"{...}" で埋め込まない
必ず @Name のようなプレースホルダと SqlParameter(またはORMのパラメータ)を使う
ユーティリティを作るなら「エスケープ関数」ではなく「パラメータ追加ヘルパー」にする
LIKE検索などでだけ、パラメータの“中身”を文字列処理する(SQL文は固定のまま)
ORM(Dapper / EF Core)を使う場合も、「パラメータ化されている構文」を選ぶ

ここまで理解できれば、「なんとなく危なそうだからエスケープしている」段階から一歩進んで、
“SQLインジェクションを構造的に防ぐC#ユーティリティとコーディングスタイル”を、自分の中にしっかり根付かせていけるようになります。

タイトルとURLをコピーしました