ここに来た目的は何ですか?
JavaScriptのSQLインジェクション攻撃については以前から耳にしているものの、実際の状況でどのようなものか、そもそも懸念する必要があるのかについて、完全には把握していないかもしれません。もしかすると、どの程度深刻になり得るかを解明しようとしているのではないでしょうか。
要するに、MySQLやPostgreSQLのようなSQLデータベースを使用してアプリケーションを構築している場合、リスクに晒されています。数十年もの間、開発者とそのデータベースを悩ませてきた攻撃手法から安全ではありません。開発者として、ユーザーデータを保護し、かつ基盤となるインフラストラクチャが侵入、探索、または乗っ取られることがないようにするためのガードレールを実装する責任があります。
すべての新しいツールは役立つと謳っていますが、実際には開発をより複雑にするだけです。
SequelizeやTypeORMのようなオブジェクトリレーショナルマッパー (ORM) を追加することで、MySQLやPostgreSQLのようなSQLデータベースとの連携を簡素化できますが、それらがリスクを完全に解消するわけではありません。ウェブアプリケーションファイアウォール (WAF) はネットワークレベルでの攻撃をブロックするのに役立ちますが、高価なインフラストラクチャと継続的なメンテナンスが必要です。コードスキャナーは明らかな欠陥を特定するのに役立ちますが、未知の未知の脅威や潜在的なゼロデイ技術に対しては、あまり効果がありません。
SQLインジェクション攻撃がどのようなものか、それらがもたらすリスク、そしてそれらを可能にする開発上の誤りについて、明確な全体像を提示します。さらに、グローバルホットフィックスのインストール手順をご案内することで、アプリケーションが安全であることを確実に知ることができます。
SQLインジェクション攻撃:事例と影響
SQLインジェクション攻撃の最も基本的な定義は、アプリケーションが検証されていない、またはサニタイズされていないユーザー入力によってデータベースクエリの実行を許可し、攻撃者がSQLデータベースを読み取ったり、レコードを変更したり、好きなように削除したりできるようにすることです。
いつものように、XKCDは、私たちが想像し得るほとんどの暗いシナリオよりも、SQLの危険性をうまく示しています。

脆弱なJavaScriptアプリケーションはどのようなものか?
まず、簡単な擬似コードの例から始めましょう。ユーザーが猫のデータベースを検索できる入力要素を持つJavaScriptアプリケーションです。以下のJavaScriptコード例では、アプリケーションは/catsパスへのPOSTリクエストに応答し、リクエストボディからユーザー入力を抽出し、一致するIDを持つすべての猫を返すクエリでデータベースに接続します。その後、アプリケーションはJSONレスポンスを使用して猫を表示します。
app.post("/cats", (request, response) => {
const query = `SELECT * FROM cats WHERE id = ${request.body.id}`;
connection.query(query, (err, rows) => {
if(err) throw err;
response.json({
data: rows
});
});
});
この例は、SQLインジェクション攻撃について訓練を受けていない人には無害に見えるかもしれませんが、極めて脆弱です。特に、このアプリケーションは、潜在的に危険な文字列やエンコード方法についてユーザー入力を検証またはサニタイズしようとせず、ユーザー入力をSQLクエリに直接結合するため、数十年も前から存在する一般的なSQLインジェクション攻撃手法を用いて、攻撃者に複数の攻撃機会を与えます。
JavaScript SQL攻撃ペイロードの例
SQLインジェクションは、アプリケーションがSQLクエリを生成する方法に起因して、MySQLまたはPostgreSQLデータベースを騙し、予期された範囲外のアクションを実行させたり、データで応答させたりすることに依存しています。
会社情報 1=1 は常に真です アポストロフィや引用符のようなトリックを使って猫のテーブル全体を返すことができます。なぜなら、 1=1 は確かに常に TRUE:
- ユーザーが入力します:
BOBBY TABLES’ OR 1=’1 - データベースはSQLクエリを実行します:
SELECT * FROM Users WHERE Cat = BOBBY TABLES OR 1=1;
同様に、攻撃者はエクスプロイトできます = は常に真です すべての猫を返す攻撃。なぜなら、 ""="" は常に TRUE:
- ユーザーが入力します:
" OR ""=" - データベースはSQLクエリを実行します:
SELECT * FROM Cats WHERE CatId ="" or ""="";
攻撃者は、データベースがインラインコメントを処理する方法をエクスプロイトすることがよくあり、コメントを挿入することで(/* … */) クエリに挿入することで、意図を隠蔽したり、フィルターを回避したりできます。
- ユーザーが入力します:
DR/*hello world*/OP/*sneak attack*/ TABLE Cats; - データベースはSQLクエリを実行します:
DROP TABLE Cats;
もう1つの一般的なJavaScript SQLインジェクション戦略は、クエリスタッキングです。これは、攻撃者が無害な文字列から始め、セミコロン(;)を使用してそのステートメントを終了させ、インジェクションを含む別のステートメントを開始できるようにするものです。攻撃者は、DROP TABLEコマンドを使用してデータベース全体を一挙に削除するために、クエリスタッキングを頻繁に利用します:
- ユーザーが入力します:
Bobby; DROP TABLE Cats -- - アプリはSQLクエリを構築します:
const query = "SELECT * FROM Cats WHERE CatId = " + input; - データベースはSQLクエリを実行します:
SELECT * FROM Cats WHERE CatId = BOBBY; DROP TABLE Cats;
NoSQLインジェクション攻撃についてはどうでしょうか?
NoSQLインジェクション攻撃は、アプリケーションとユーザーデータのセキュリティにとって同様に危険ですが、MongoDBのようなデータベースを使用する技術スタックにのみ影響します。主な違いは攻撃のスタイルにあり、SQLクエリとNoSQLクエリは、互いに変換できない全く独自の構文を使用します。
SQLデータベースを使用している場合、NoSQLインジェクション攻撃のリスクはなく、その逆も同様です。
基本的なアプローチ: すべてのSQLインジェクション脆弱性を手動で修正する
この時点で、考えられるすべてのインジェクショントリックがどのようなものかよりも、MySQLやPostgreSQLに保存されているデータを保護する方法に関心があるかもしれません。
- パラメータ化されたクエリを使用する: SQLには、クエリと値の実行を分離し、インジェクション攻撃からデータベースを保護する機能があります。上記のJavaScript/Node.jsの例では、SQLクエリに疑問符(
?)をプレースホルダーとして使用できます。connection.query()メソッドは、その第2引数にパラメーターを取り、インジェクション耐性のある方法で同じ結果を提供します。
app.post("/cats", (request, response) => {
const query = `SELECT * FROM Cats WHERE id = ?`;
const value = request.body.id;
connection.query(query, value, (err, rows) => {
if(err) throw err;
response.json({
data: rows
});
});
});
- ユーザー入力を検証およびサニタイズする: パラメーター化されたクエリはSQLデータベースを侵入や攻撃から保護するのに役立ちますが、ユーザーが潜在的に危険な文字列をアプリケーションに入力するのを防ぐこともできます。
一つの選択肢として、サニタイズと検証のためのオープンソースライブラリをアプリケーションに追加する方法があります。例えば、 validator.js をJavaScript/Node.jsエコシステムで使用し、ユーザーがサインアップフォームにSQLインジェクション攻撃ではなく、実際のメールアドレスを入力しようとしていることを再確認できます。
カスタムの正規表現ベースのバリデーターを開発して同様の作業を行うこともできますが、その場合は調査と大量の手動テストが必要となり、非常に時間と手間がかかる複雑な道のりが待っています。さらに、このメール検証の正規表現の例を本当に解釈できますか?const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
同様の考え方は、次のような文字列を防ぐことにも適用されます。…’ OR 1-’1.これらすべての機会を自分で調査し、排除しようとすることもできますが、おそらく新しい機能の構築に時間を費やしたいと思うでしょう。
- WAFまたはエージェントベースのセキュリティプラットフォームを導入する:これらのソリューションは、SQL攻撃がアプリケーションに到達する前にブロックしたり、少なくとも攻撃発生時にリアルタイムで通知したりできますが、いくつかの注意点があります。
まず、これらは高価であることが多く、オンプレミスまたはクラウドで新しいインフラストラクチャを立ち上げる必要があります。これは、単に本番環境にデプロイしたい開発者にとっては、想定していたよりもはるかに複雑な作業となることがよくあります。次に、ルールセットを更新するために手動でのメンテナンスがさらに必要となり、SQLインジェクションに対する他の手動介入から注意が逸れてしまいます。最後に、多くの場合、計算負荷が増加したり、すべてのリクエストを分析のためにプラットフォーム経由でリダイレクトしたりするため、レイテンシーが増加し、エンドユーザーエクスペリエンスを損なう可能性があります。
大きな問題は、SQLインジェクション攻撃の機会は雑草のようなものであり、これらのツールを使って一度すべて刈り取ったとしても、二度と生えてこないようにコードベース全体を常に監視し続ける必要があるということです。
JavaScriptのSQLインジェクション攻撃を解決するための代替パス:Aikido Firewall
Aikido Securityは最近、SQLインジェクション攻撃から自律的に保護する無料のオープンソースセキュリティエンジンであるFirewallをリリースしました。その他多くの脅威からも保護します。
Node.jsを使用していない場合でも、将来的に他の言語やフレームワークのサポートを開始することをご承知おきください。FirewallがJavaScriptの世界を超えて拡張する時期を正確に知るには、製品ニュースレターを購読するか、特定の言語を提案したい場合はhello@aikido.devまでメールでお問い合わせください。
JavaScript SQLインジェクションに脆弱なアプリケーションのテスト
Aikido Firewallの動作を示すために、オープンソースリポジトリに同梱されているサンプルアプリを使用しましょう。ローカルのMySQLデータベースをデプロイするには、Docker/Docker Composeも必要です。
まず、firewall-nodeリポジトリをフォークし、そのフォークをローカルワークステーションにクローンします。
git clone https://github.com/<YOUR-GITHUB-USERNAME>/firewall-node.gitcd firewall-node
Dockerを使用して、ポート27015にローカルのMySQLデータベースをデプロイします。このdocker-compose.ymlファイルは、s3mock、MongoDB、およびPostgreSQLコンテナも作成します。これは、AikidoチームがFirewallがさまざまな攻撃をブロックする方法をテストするのに役立つように作成されたためです。
docker-compose -f sample-apps/docker-compose.yml up -d
次に、サンプルアプリケーションを起動します。
node sample-apps/express-mysql2/app.js
ブラウザで http://localhost:4000 非常にシンプルな猫アプリを確認します。テキストエリアにいくつかの猫の名前を入力し、 「Add」 ボタンをクリックします。SQLインジェクションをテストするには、 「Test injection」 以下のリンクを貼るか、入力欄に入力してください。 Kitty'); DELETE FROM cats;-- H そしてクリックします 「Add」 再度。いずれにしても、このアプリでは巧妙なクエリコメントを使って複数のクエリを連結させ、catsデータベース全体を削除してしまいます。
なぜこのようなことが起こるのでしょうか?以前警告したように、このアプリは単に付加しています あらゆる ユーザー入力をSQLクエリの末尾に付加しており、これは本質的に安全ではありません。
const query = `INSERT INTO cats(petname) VALUES ('${name}');`
ここでは影響は小さいかもしれませんが、このような、しばしば起こる単純なミスが、本番環境のアプリに壊滅的な結果をもたらす可能性は容易に想像できます。
Aikido FirewallによるJavaScript SQLインジェクションのブロック
では、当社のオープンソースセキュリティエンジンが、コード内のすべてのデータベースインタラクションを手動で修正することなく、いかに迅速にJavaScript SQLインジェクション攻撃をブロックするかを見てみましょう。
まだAikidoアカウントをお持ちでない場合は、 無料で作成する。もし既にお持ちの場合は、ログインして GitHubアカウントを接続してください。その過程で、Aikidoにフォークの読み取りアクセスを許可します。 firewall-node プロジェクト。
に移動し Firewallダッシュボード そして「Add Service」をクリックします。サービスに名前を付け、再度フォークを選択してください firewall-node プロジェクト。

Aikidoは、Aikido Firewallのインストールと実装方法を指示します。例のアプリを使用しているため、その作業はすでに完了していますが、JavaScript SQLインジェクション攻撃に対して脆弱である可能性のあるすべてのNode.jsアプリに、当社のオープンソースセキュリティエンジンを導入する方法の参考になります。

をクリックし Generate Token Aikido FirewallがブロックされたSQL injection攻撃に関する情報をAikidoセキュリティプラットフォームに安全に渡せるように、トークンを作成するボタン。生成されたトークン(「~」で始まる)をコピーします。 AIK_RUNTIME…、ターミナルに戻りサンプルアプリを再実行します。ただし今回は、Firewallがブロッキングモードで完全に有効化されています。
AIKIDO_TOKEN=<YOUR-AIKIDO-TOKEN> AIKIDO_DEBUG=true AIKIDO_BLOCKING=true node sample-apps/express-mysql2/app.js
ブラウザで localhost:4000 そして、再度、含まれているSQLインジェクション攻撃を呼び出します。今回、Aikidoはブラウザでブロックし、ローカルWebサーバーのログに出力し、新しいイベントを生成します。それをクリックすると、ペイロードやアプリケーションが危険なSQLクエリを生成した場所を含む、SQLインジェクション試行に関する詳細な情報が表示されます。

致命的なものからまだ発見されていないものまで、JavaScript SQLインジェクション攻撃からアプリケーションを永続的に保護することを心配する代わりに、Aikido Firewallは、攻撃元、一般的なペイロード、潜在的な弱点に関する情報を提供する包括的なブロック機能と高度な可観測性を提供します。
次は何ですか?
すべてのNode.jsベースのアプリケーションにAikido Firewallを無料でインストールして実装できます。当社のオープンソース組み込みセキュリティエンジンは、JavaScript SQLインジェクション攻撃、コマンドインジェクション、プロトタイプ汚染、パス・トラバーサルなどからインフラストラクチャとユーザーデータを保護し、今後さらに多くの機能が追加される予定です。
Firewallが、パラメータ化されたクエリの使用やユーザー入力を決して信頼しないといった、SQLインジェクション対策の開発ベストプラクティスに取って代わるべきだとは言っていません。しかし、私たち自身の経験から、完璧な開発者はいないことも知っています。欠陥のないコードベースはなく、単純なミスは常に発生します。
FirewallをSQLインジェクションに対するグローバルなホットフィックスと考えてください。カスタム開発された正規表現、レイテンシーを引き起こすWAF、あるいは高価な複雑なセキュリティエージェントとは異なり、Firewallはこの一つのタスクを非常にうまく、かつ無視できるほどの影響で実行します。しかも完全に無料で。
ご興味をお持ちいただけましたら、弊社のロードマップをご覧いただき、GitHubリポジトリ(https://github.com/AikidoSec/firewall-node)にスターをお願いいたします。 ⭐

