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;