C#のAPIサーバーにてSQLインジェクションの脆弱性について警告されたから深掘りしてみた
テスト用で作成されたC#で構成されたAPIサーバーをいじっているとこんな警告が
The SQL expression passed to 'FromSql' embeds data that will not be parameterized. Review for potential SQL injection vulnerability.
なんとなくSQLインジェクションの脆弱性について警告されていることはわかる。
ChatGPTに聞いてみると'FromSql'メソッドなるものを使ってDBと通信をしていることが原因で警告されているらしい、「パラメータ化されていないデータ」とは生のSQLクエリのことで、そのまま生のSQLクエリ投げてるとインジェクションされちゃうよ!って警告でした。
例えば
var users = context.Users.FromSql($"SELECT * FROM Users WHERE UserId = {userId}").ToList();
こんな記述だった場合、{userId}部分にSQLを入力されると予期せぬデータを抜き取られる可能性がある。
ではパラメータ化とは?
一例としてSqlParameter
メソッドを使用したパラメータ化についてChatGPTに聞いてみました。
以下のような記述になるようです。
var userId = 1;
var users = context.Users
.FromSql("SELECT * FROM Users WHERE UserId = @userId",
new SqlParameter("@userId", userId))
.ToList();
SqlParameterインスタンスの引数として入力値を扱うことで、単なる「値」としてSQLクエリに組み込まれるそうな。
ふーん、なんで?
結局SQL文になるんじゃないの?わからん。
気になるので更に深掘り
「" OR 1=1;」 が送られてきたらどうなるの?
これが例文だそうです。
var userId = "1' OR 1=1 --";
var users = context.Users
.FromSql("SELECT * FROM Users WHERE UserId = @userId",
new SqlParameter("@userId", userId))
.ToList();
" OR 1=1; で聞いたのに "1' OR 1=1 --" はどこからでてきたんだ?
パラメータ化された影響でエスケープ処理(特殊文字の置換)がされるようです。
そうやって意味の無い文字列にしてやることでSQLインジェクションを無効化するんだそうな。
なるほど、やっと納得できました。
クエリはこうなる
SELECT * FROM Users WHERE UserId = '1'' OR 1=1 --'
UserId = '1'にヒットするものだけを検索するらしい。
' OR 1=1 --'は無視されるそうです。
ふーん、なんで???
後半が無視される意味がわからない・・・
そのあとも深掘りしてみましたが、どうやら先の@userId
はINT型であるという前提があり、文字列である' OR 1=1 --'は無視されるとのことでした。
ではSTR型だった場合は?'1'' OR 1=1 --'エスケープ処理されたこの部分→'1''
シングルクオートが2回続くことで1'
として解釈されるそうです。
なるほどそうやって条件式ORを無効化しているのか。
ちなみにAPIサーバーとしては、
入力値をSQLクエリに組み込んで通信を行うことは無いようなので問題無いようです。
追記
朝起きてUserId=1の「1」ってどこからきたんだっけともう一度ChatGPTに聞いてみると、有効な値と無効な値を説明するためにアドリブで入れたそうで、本来であれば別に必要ないそうな。なんじゃそりゃ
Discussion