LINQ to SQLによるSQLインジェクションの排除
論点をシンプルにするため、サンプルWebアプリケーションをコンソールアプリケーションに書き換えたものがリスト2です。このコンソールアプリケーションは、SQL文に埋め込まれる悪意あるSQLを単純化して表現しています(話を簡単にするために、どうやって埋め込むかは重要視しません。前のセクションで、値が直接埋め込まれるSQLにはインジェクションのおそれがあることを説明したので、不備のあるSQLが最終的に好ましくない結果となることが分かれば十分です)。
Imports System.Data.SqlClient
Module Module1
Sub Main()
SqlInjectionToDiscoverOtherDatabase()
End Sub
Sub SqlInjectionToDiscoverOtherDatabase()
Dim connectionString As String = _
"Data Source=.\SQLExpress;Initial _
Catalog=northwind;Integrated Security=True"
Dim sql As String = _
"SELECT CustomerID, CompanyName FROM Customers _
WHERE CustomerID = '{0}'" _
Dim customerID = _
"ALFKI' UNION SELECT 'database name' as dummy, " + _
"name FROM master.sys.databases --"
Using connection As SqlConnection = _
New SqlConnection(connectionString)
connection.Open()
Dim command As SqlCommand = _
New SqlCommand(String.Format(sql, customerID), connection)
Dim reader As SqlDataReader = command.ExecuteReader
While (reader.Read())
Dim i As Integer
For i = 0 To reader.VisibleFieldCount - 1
Console.WriteLine(reader(i).ToString())
Next
End While
End Using
Console.ReadLine()
End Sub
End Module
リスト2では、悪意のあるSQL(ユーザーによる入力)を表すcustomerIDローカル変数を宣言しています。LINQ to SQLによってSQLインジェクションがどのように排除されるかを解説する前に、ここでLINQ to SQLについての一般的な説明をしたいと思います。LINQ to SQLの基本がわかっている場合は、次のセクションを読み飛ばしてください。
LINQ to SQLとは
LINQ(Language Integrated Query)とは、VBまたはC#のコードで記述されたSQLライクな言語のことです。LINQ to SQLは、基本的にLINQ to SQLテクノロジによってSQLクエリに変換されるLINQコードを指します。
大まかに言って、LINQ to SQLの作成手順は次のようになります。SQLデータベースのテーブルに一致する、カスタムエンティティクラスを定義します。クラスにはTableAttribute属性を指定し、列を表すプロパティにはColumnAttribute属性を指定します。SqlMetalを使用して、これらのクラスを生成することもできます。これはまさに、定義した(またはSqlMetal.exeで生成した)カスタムクラスのプロパティと、ColumnAttribute属性でテーブルの列に関連付けられているプロパティのみがSQLから返されることを意味しています。次に、DataContextクラスを継承した簡単なクラスを定義します。このDataContextクラスは、データベースへの接続を表しています。作成はこれで完了です。ADO.NETを直接使用することはありません。SQLを直接記述することもありません。周辺の処理はLINQ to SQLの仕事です。
最終的な結果として、エンティティに一致するデータのみが返されます。ユーザーは、動的SQL文字列ではなくコンパイル済みのコードを使用することになります。SQLに直接渡す値はなく、SQLはLINQ to SQLエンジンによって生成されます。
LINQ to SQLがSQLインジェクションを防止する仕組み
動的SQLでは、SQLエンジンにテキスト文字列が渡されることが問題の種となります。LINQ to SQLの場合、コードは既にコンパイルされていて、SQLはLINQ to SQLテクノロジによって生成されます。
次に重要なのは、LINQがサポートするのはSelectクエリのみであるという点です。Update、Delete、Insertはサポートしていません。このため、悪意あるユーザーにできるのはデータを読み込むことだけです。これは少々不便に思われるかもしれませんが、そんなことはありません(詳しくは後述)。
LINQは、コンパイル時にメソッド呼び出しに変換されます。つまり、実行コードはコンパイルが完了しています。そのため、ユーザーが実行時に新たなLINQキーワードを入力して、事前に定義されたクエリを改ざんすることはできません。実行時にSelectをJoinやUnionに変えることはできないのです。
CodeDOMを使用して、LINQクエリを実行時にコンパイルするようにアプリケーションを設計すると、実行時にユーザーが動的LINQを入力できる場合があります。ただし、当然ながらこれには高度な技術を要します。方法が分かる人はあまりいませんし、ビジネスアプリケーションで動的コンパイルを使用する理由はほとんどありません。
