.NETにおけるSQLインジェクション対策

#SQLインジェクションとは
>SQLインジェクションとは、データベースと連動したWebアプリケーションなどに対する攻撃手法の一つで、検索文字列など外部から指定するパラメータの一部にSQL文の断片などを混入させ不正な操作を行うもの。また、そのような攻撃を可能にする脆弱性。
[引用元：IT用語辞典 e-words](https://e-words.jp/w/SQL%E3%82%A4%E3%83%B3%E3%82%B8%E3%82%A7%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3.html)

要するに、文字列を組み立ててSQLを動的に生成するアプリケーションに、悪意を持った文字列を渡して不正な操作をすること。

以下がサンプルコード(VB.NETで書きました)。パスワードを更新するサブルーチンです。

```VB
'''パスワードをアップデートする
Public Sub Update(ByVal user_name As String, ByVal pass As String)
    '***** クエリを生成　ユーザー名とパスワードを変数としてクエリに組み込む ******
    Dim Sql As String = "UPDATE passwords SET pass = '" & pass & "' WHERE user_name = '" & user_name & "';"
    '***********************************************************************

    'DB接続オブジェクトを作る
    Using Conn As New MySqlConnection("Database=" + DB_Name _
                                + ";Data Source=" + DB_Source _
                                + ";Port=" + DB_Port _
                                + ";User Id=" + DB_Id _
                                + ";Password=" + DB_Pw _
                                + ";sqlservermode=True;")
        Conn.Open()  'コネクション形成
        Dim cmd As MySqlCommand = New MySqlCommand(sql, Conn)
        cmd.ExecuteNonQuery() 'SQL実行
        Conn.Close()  'コネクションを閉じる
    End Using
End Sub
```

例えば、上記クエリのuser_nameに、「'; DELETE FROM passwords;」という文字列を入れると、パスワードテーブルのデータがすべて削除できてしまう。

```SQL
--うしろのDELETEで、すべてのデータが削除されてしまう
UPDATE passwords SET pass = '" & pass & "' WHERE user_name = ''; DELETE FROM passwords ; 
```

悪い人は世の中に沢山いるので、未然に対策をしておく必要がある。

#対策①：引用符のエスケープ
SQLでは、引用符としてシングルクオーテーションが使用されているが、二つ重ねることでシングルクオートをエスケープ(一般文字化)することができる。

```VB
'''パスワードをアップデートする
Public Sub Update(ByVal user_name As String, ByVal pass As String)
     '*** シングルクオートを二重に重ねるように置換する ***
     user_name = user_name.Replace("'", "''")
     pass = pass.Replace("'","''")
     '**************************************************
    
    '***** クエリを生成　ユーザー名とパスワードを変数としてクエリに組み込む ******
    Dim Sql As String = "UPDATE passwords SET pass = '" & pass & "' WHERE user_name = '" & user_name & "';"
    '***********************************************************************

    'DB接続オブジェクトを作る
    Using Conn As New MySqlConnection("Database=" + DB_Name _
                                + ";Data Source=" + DB_Source _
                                + ";Port=" + DB_Port _
                                + ";User Id=" + DB_Id _
                                + ";Password=" + DB_Pw _
                                + ";sqlservermode=True;")
        Conn.Open()  'コネクション形成
        Dim cmd As MySqlCommand = New MySqlCommand(sql, Conn)
        cmd.ExecuteNonQuery() 'SQL実行
        Conn.Close()  'コネクションを閉じる
    End Using
End Sub
```

#対策②：プレースホルダを利用する
プレースホルダとは

>プレースホルダとは、実際の内容を後から挿入するために、とりあえず仮に確保した場所のこと。また、そのことを示す標識などのこと。
[引用元：IT用語辞典 e-words](https://e-words.jp/w/%E3%83%97%E3%83%AC%E3%83%BC%E3%82%B9%E3%83%9B%E3%83%AB%E3%83%80.html)

要するに、クエリ内に、後から文字列に置き換える場所をあらかじめ確保しておき、クエリ実行前に値を渡す方法である。

プレースホルダーで渡す値はリテラルとして扱われるため、意図しないクエリが実行されることを防ぐことができる。

```VB
'''パスワードをアップデートする
Public Sub Update(ByVal user_name As String, ByVal pass As String)
    '***** クエリを生成　ユーザー名とパスワードのプレースホルダを設定 ******
    Dim Sql As String = "UPDATE passwords SET pass = @pass WHERE user_name = @user_name;"
    '******************************************************************

    'DB接続オブジェクトを作る
    Using Conn As New MySqlConnection("Database=" + DB_Name _
                                + ";Data Source=" + DB_Source _
                                + ";Port=" + DB_Port _
                                + ";User Id=" + DB_Id _
                                + ";Password=" + DB_Pw _
                                + ";sqlservermode=True;")
        Conn.Open() 'コネクション形成

        '***************** プレースホルダと値をバインド ******************
        Dim cmd As MySqlCommand = New MySqlCommand(Sql, Conn)
        cmd.Parameters.Add(New MySqlParameter("@user_name", user_name)) '''ユーザー名
        cmd.Parameters.Add(New MySqlParameter("@pass", pass))           '''パスワード
        '***************************************************************
        cmd.ExecuteNonQuery() 'SQL実行

        Conn.Close() 'コネクションを閉じる
    End Using

End Sub
```

###対策③：O/Rマッピングの利用
>O/Rマッピングとは、オブジェクト指向プログラミング言語におけるオブジェクトとリレーショナルデータベース（RDB）の間でデータ形式の相互変換を行うこと。そのための機能やソフトウェアを「O/Rマッパー」（O/R mapper）という。
[引用元：IT用語辞典 e-words](https://e-words.jp/w/O-R%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0.html)

要するに、RDBのデータをオブジェクトにセットし直す(=マッピング)ことで、扱いやすくすること。
VB.NETでは、Dapperというパッケージを利用することで、O/Rマッピングを実装することができる。

サンプルコードコードのように、マッピングされているオブジェクトが保持する値を、クエリに組み込むことができる(※これを実施するためには、プレースホルダの名前と、オブジェクトがもつ変数の名前を一致させる必要がある)。
この際、②でプレースホルダを利用した際と同様に、値がリテラル扱いされるので、SQLインジェクション対策で利用することができる。

```VB
'''パスワードをアップデートする
Public Sub Update(ByVal user_name As String, ByVal pass As String)
    '***** クエリを生成　ユーザー名とパスワードのプレースホルダを設定 ******
    Dim Sql As String = "UPDATE passwords SET pass = @pass WHERE user_name = @user_name;"
    '******************************************************************

    'DB接続オブジェクトを作る
    Using Conn As New MySqlConnection("Database=" + DB_Name _
                                + ";Data Source=" + DB_Source _
                                + ";Port=" + DB_Port _
                                + ";User Id=" + DB_Id _
                                + ";Password=" + DB_Pw _
                                + ";sqlservermode=True;")
        Conn.Open() 'コネクション形成

        Dim user As User = New User(user_name, pass)  'Userクラスにユーザー名とパスワードを入れる

        Conn.Execute(Sql, user)  'userクラスに入れた値で、SQLを実行する

        Conn.Close() 'コネクションを閉じる
    End Using
End Sub
```

```VB
'O/Rマッピング対象クラス
Public Class User
　　'''コンストラクタ
    Public Sub New(ByVal user_name As String, ByVal pass As String)
        Me.user_name = user_name  'ユーザー名
        Me.pass = pass            'パスワード
    End Sub

    Public Property user_name  '自動実装プロパティ：ユーザー名
    Public Property pass       '自動実装プロパティ：パスワード

End Class
```

#最後に
今更な内容ですが、他人に説明する用の資料として文章化してみました。

P.S. IT用語辞典 e-wordsさん、いつも本当にお世話になっています！