SQLインジェクションのリスク回避
SQLインジェクションのリスクは、SQLパラメータの値をsql
パッケージの関数引数として提供することで回避できます。 sql
パッケージの多くの関数は、SQLステートメントと、そのステートメントのパラメータで使用する値のパラメータを提供します(準備されたステートメントとパラメータのパラメータを提供するものもあります)。
次の例のコードでは、?
記号をid
パラメータのプレースホルダーとして使用しており、これは関数引数として提供されます。
// Correct format for executing an SQL statement with parameters.
rows, err := db.Query("SELECT * FROM user WHERE id = ?", id)
データベース操作を実行するsql
パッケージの関数は、指定した引数から準備されたステートメントを作成します。実行時、sql
パッケージはSQLステートメントを準備されたステートメントに変換し、パラメータとは別に送信します。
注:パラメータプレースホルダーは、使用しているDBMSとドライバーによって異なります。たとえば、Postgres用のpqドライバーは、?
の代わりに$1
のようなプレースホルダー形式を受け入れます。
パラメータを含む文字列としてSQLステートメントを組み立てるために、fmt
パッケージの関数を使用しようとするかもしれません – このように。
// SECURITY RISK!
rows, err := db.Query(fmt.Sprintf("SELECT * FROM user WHERE id = %s", id))
これは安全ではありません!これを行うと、Goは完全なSQLステートメントを組み立て、%s
フォーマット動詞をパラメータ値に置き換えてから、完全なステートメントをDBMSに送信します。これにより、コードの呼び出し元が予期しないSQLスニペットをid
引数として送信する可能性があるため、SQLインジェクションのリスクが生じます。そのスニペットは、アプリケーションにとって危険な予測不可能な方法でSQLステートメントを完了する可能性があります。
たとえば、特定の%s
値を渡すことで、データベース内のすべてのユーザーレコードを返す可能性のある、次のようなものができる可能性があります。
SELECT * FROM user WHERE id = 1 OR 1=1;