SQL文の中にJavaScriptの変数を入れたい場合、何も考えずにSQLと変数を連結させたり、テンプレートリテラルでSQL文を作成すると、SQLインジェクションのリスクが高くなります。
そこで今回は、JavaScriptの変数とプレースホルダーを上手に活用して、SQLインジェクションのリスクが少ないセキュアなSQL文を作成する方法をご紹介します!
SQLインジェクションとは
この手の話は定番中の定番なので、既にご存知の方は読み飛ばしちゃってください。
SQLインジェクションとは、アプリケーションのセキュリティ上の不備を意図的に利用し、アプリケーションが想定しないSQL文を実行させることにより、データベースシステムを不正に操作する攻撃方法のことです。
例えば、以下のようなコードがあったとします。
var name = req.body.name; var sql = "SELECT * FROM users WHERE name = '" + name + "'"; connection.query(sql, function (error, results) { // 省略 });
このコードでは、name
変数にユーザーが入力した値がそのままSQL文に埋め込まれています。
もしname
が以下のような値を入力したらどうなるでしょうか?
name = "' OR 1 = 1 --"
この場合、SQL文は以下のようになります。
SELECT * FROM users WHERE name = '' OR 1 = 1 --'
ここで、–
はコメント記号であり、それ以降の文字列は無視されます。
つまり、このSQL文は「name
が空文字かどうか、または1が1かどうか」という条件でusersテーブルから全てのレコードを取得することになります。
これは明らかにアプリケーションが想定していない動作です。
プレースホルダーを使ってSQLインジェクションを回避
JavaScriptでSQL文を書く際に使用するプレースホルダーとは、SQL文の中に埋め込む変数のようなものです。
プレースホルダーを使うと、SQLインジェクションなどの攻撃を防ぐことができます。
プレースホルダーの書き方
プレースホルダーの書き方は、使用するライブラリやデータベースによって異なりますが、一般的には?
や$1
などの記号を使います。
例えば、以下のようなSQL文があります。
SELECT * FROM users WHERE name = '山田';
このSQL文では、name
の値が固定されていますが、プレースホルダーを使うと以下のように書けます。
SELECT * FROM users WHERE name = ?;
または
SELECT * FROM users WHERE name = $1;
この場合、?
や$1
に対応する値を別途指定する必要があります。
JavaScriptでは、以下のようにqueryメソッドの第2引数に配列で渡すことができます。
connection.query('SELECT * FROM users WHERE name = ?', ['山田'], function (error, results) { // 結果で何かをする });
プレースホルダーを使ったSQL文
JavaScriptでSQL文の中に変数を入れたい場合は、変数の値を""
で囲む必要があります。
例えば、以下のようなコードです。
var name = "sample"; var sql = "INSERT INTO users VALUES(2,'" + name + "')";
また、mysqlモジュールを使っている場合は、プレースホルダー(?
)を使って変数を埋め込むこともできます。
例えば、以下のようなコードです。
var name = "sample"; var sql = "INSERT INTO users VALUES(2,?)"; connection.query(sql, [name], function (error, results) { // 省略 });
どちらの方法もSQLインジェクションのリスクを減らすことができます。
テンプレートリテラルを使ったSQL文
テンプレートリテラルの基礎
テンプレートリテラルとは、文字列をグレイヴ・アクセント
で囲み、変数をドル記号と波括弧でくくることで、文字列に変数の値を埋め込むことができる機能です。
テンプレートリテラルの書き方は、以下のようになります。
- 文字列をグレイヴ・アクセント(`)で囲む
- 変数や式を埋め込む場合は、ドル記号($)と波括弧({})でくくります。
- 改行やタブなどの空白文字はそのまま反映されます。
例えば、以下のようなコードがあります。
var name = "山田"; var age = 20; var message = `こんにちは、${name}さん! あなたは${age}歳です。`;
このコードでは、message
変数には以下のような文字列が代入されます。
こんにちは、山田さん! あなたは20歳です。
テンプレートリテラルとは、文字列を定義するときに変数を使用したり、ソース上の改行をそのまま取り込めるというものです。
SQL文の書き方にも便利ですが、注意点もあります。
SQL文で使うと危険!
上記の通り、テンプレートリテラルは大変便利に機能ですが、こちらもやはりそのままSQLの中にJavaScriptの変数を書くのは危険です。
例えば、以下のようにテンプレートリテラルでSQL文を作成します。
let name = "山田"; let age = 20; let sql = `SELECT * FROM users WHERE name = '${name}' AND age = ${age}`;
この場合、変数name
とage
の値がSQL文に埋め込まれます。
しかし、この方法はSQLインジェクションという攻撃手法に対して脆弱です。
攻撃を防ぐためにも、やはりプレースホルダーを使ってSQL文を作成する必要があります。
例えば、以下のようにテンプレートリテラルとプレースホルダーでSQL文を作成することができます。
let name = "山田"; let age = 20; let sql = "SELECT * FROM users WHERE name = ? AND age = ?"; // 変数の値をバインドしてからSQL文を実行する
この場合、変数name
に不正な値が入力されても、その値は文字列リテラルとして扱われるため、想定外の命令は実行されません。
以上がテンプレートリテラルを使ってSQL文の書き方について詳しく教える内容です。
- Original:https://minory.org/nodejs-secure-sql.html
- Source:Minory
- Author:管理者