次の方法で共有


安全な ADO.NET コーディングのためのガイドライン

アプリケーションを保護するためには、安全なコードを作成する必要があります。作成するコードでは、クライアント コードが必要とする情報と機能だけを公開します。ADO.NET のセキュリティに関する一般的な問題には、SQL 挿入攻撃と、アプリケーションが返す例外からプライベート データベース情報を取得する攻撃があります。

SQL 挿入攻撃を防ぐ

SQL 挿入攻撃では、データ ソースを処理するコマンドに、攻撃を行う SQL ステートメントが挿入されます。そのようなコマンドは、データ ソース内の情報を破壊するだけでなく、プライベート情報を取得することもできます。SQL 挿入攻撃を受けやすいのは、コマンド文字列と外部入力を連結するようなコードです。SQL 挿入攻撃を受けやすいコードの例を次に示します。

' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()

' The following line of code allows for SQL Insertion attack.
Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = " & custID

Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();

// The following line of code allows for SQL Insertion attack.
string selectString = "SELECT * FROM Customers WHERE CustomerID = " + custID;

SqlCommand cmd = new SqlCommand(selectString, conn);
conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();

攻撃者は、クエリ対象の CustomerID として、"1;DROP TABLE Customers" という値を入力できます。その場合、次のコマンドがクエリとして実行されます。

SELECT * FROM Customers WHERE CustomerID = 1;DROP TABLE Customers

SQL 挿入攻撃からの保護を支援するには、外部ソースからの入力を検証します。また、値を連結して SQL ステートメントを作成するのではなく、列の値をパラメータとして渡します。

入力の検証

正規表現を使用して、入力が特定の書式と一致していることを検証できます。.NET Framework には、正規表現を使って値を検証するための Regex オブジェクトが用意されています。たとえば、次のコードは、値が 5 文字の英数字文字列かどうかを確認します。

Public Static Function Validate(inString As String) As Boolean
  Dim r As Regex = New Regex("^[A-Za-z0-9]{5}$")
  Return r.IsMatch(inString)
End Function
[C#]
public static bool Validate(string inString)
{
  Regex r = new Regex("^[A-Za-z0-9]{5}$");
  return r.IsMatch(inString)
}

パラメータの使用

パラメータを使うと、SQL ステートメントと共に渡される値やストアド プロシージャに渡される値を簡単に扱うことができます。また、パラメータを使う場合は、外部ソースから受け取る値が必ず値として渡され、SQL ステートメントの一部になることはないので、SQL 挿入攻撃を防ぐことができます。そのため、値に挿入されていた SQL コマンドがデータ ソースに対して実行されることはありません。渡された値は、単なるパラメータ値として処理されます。パラメータを使用して値を渡すコード例を次に示します。

' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()

Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = @CustomerID"

Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID

conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();

string selectString = "SELECT * FROM Customers WHERE CustomerID = @CustomerID";

SqlCommand cmd = new SqlCommand(selectString, conn);
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID;

conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();

例外情報を秘密にする

攻撃者は、システムを攻撃するときに、サーバー、データベース、テーブルなどの名前を例外情報から取得して使用することがよくあります。例外には、アプリケーションやデータ ソースに関する具体的な情報が含まれている場合があるので、アプリケーションとデータ ソースの保護を強化するには、クライアント側で必要としている情報だけを公開するようにします。

例外を通じてプライベート情報が公開されるのを防ぐには、システム例外の内容をユーザーに返さないようにします。その代わりに、例外は内部で処理します。ユーザーにメッセージを送信する必要がある場合は、最小限の情報だけの独自のメッセージ (たとえば "接続に失敗しました。システム管理者に問い合わせてください。") を返すようにします。具体的な情報はログに記録し、管理者が参照できるようにしておきます。

たとえば、次のコードは、接続を開くときに例外をトラップして、例外をイベント ログに書き込みます。

Dim conn As SqlConnection = New SqlConnection("Data Source=localhost;Initial Catalog=Northwind;")

Try
  conn.Open()

Catch e As SqlException
  Dim log As System.Diagnostics.EventLog = New System.Diagnostics.EventLog()
  log.Source = "My Application"
  log.WriteEntry(e.ToString())

  If conn.State <> ConnectionState.Open Then _
    Console.WriteLine("Connection was not opened.")

Finally
  conn.Close()
End Try
[C#]
SqlConnection conn = new SqlConnection("Data Source=localhost;Initial Catalog=Northwind;");

try
{
  conn.Open();
}
catch (SqlException e)
{
  System.Diagnostics.EventLog log = new System.Diagnostics.EventLog();
  log.Source = "My Application";
  log.WriteEntry(e.ToString());

  if (conn.State != ConnectionState.Open)
    Console.WriteLine("Connection was not opened.");
}
finally
{
  conn.Close();
}

参照

安全な ADO.NET コードの作成