1. 序章

私たちの時代を本当に台無しにする可能性のある無数の攻撃において、SQLインジェクションは非常に特別な場所に値します。 これは、最も使用されているWebシステムのハッキング手法の1つです。 そして、それらは現在、多くのハッキングツールで武器化されています(例: sqlmap )。 これらのツールは、それほど熟練していないハッカーでさえ、非常に破壊的な攻撃に従事するのに役立ちます。

つまり、インジェクション攻撃者は、アプリケーションロジックに任意のコードを挿入しようとします。 それらが成功すると、このコードはアプリケーションのアクセス許可とセキュリティの役割を使用して実行されます。 これらの攻撃は、リモートコード実行攻撃(RCE)のより広いカテゴリに分類されます。 つまり、コード(バイナリまたはスクリプト)をアップロードし、サーバーをだまして後で実行させます。

インジェクション攻撃は、 OWASP のトップ10で長い間リスクが高いもののひとつであり、そうすべきです。 巧妙に作成された攻撃は、アプリケーションデータを簡単に変更したり、トラックを消去したりする可能性があります。 SQLインジェクションは、攻撃者がアプリケーションによって使用されるSQL句を変更および破損しようとする場合です。

Javaプログラミングのいくつかの側面をカバーするチュートリアルがすでにあります。 このチュートリアルでは、SQLインジェクションとは何かについてもう少し詳しく説明します。 それがどのように機能するか、そして何がそれをとても危険にするのか。 そのカテゴリと難読化手法を確認し、修正方法を示します。

2. SQLインジェクションはどのように機能しますか?

簡単に言うと、 SQLインジェクションは、アプリケーションが文字列をアプリケーション入力パラメーターと連結してSQLコマンドを作成するときに発生する可能性があります。 たとえば、一部のWebアプリケーションのログインで、次の擬似コードを使用して、ログインフォームの資格情報が有効かどうかを確認するとします。

結果のSQL句は、パラメータがUsername =“ root”およびPassword =“ root”の場合、次のようになります。

SELECT id,Fullname FROM users WHERE Password ='63a9f0ea7bb98050796b649e85481845' AND Username ='root'

ユーザー名を「root」または1=1 limit 1; –」として使用し、パスワードを空白にすると、結果の句は次のようになります。

SELECT id,Fullname FROM users WHERE Password ='d41d8cd98f00b204e9800998ecf8427e' AND Username ='root' or 1=1 limit 1;--'

後者は、攻撃者が正しいパスワードを推測できなかった場合でも、最初の結果と同じ結果をもたらします。 秘訣は「または1=1」条件の挿入でした。 これにより、アプリケーションがだまされて、有効なクレデンシャルを受け入れる可能性があります。

この単純なインジェクション攻撃は、たとえば次のような他のSQL句を追加することで、さらに悪化する可能性があります。

  • Username =” root’または1=1; select * from Customers; – “、他のテーブルで選択するには
  • Username =” root’または1=1; ユーザー(ユーザー名、パスワード)の値(’backdoor_user’、’some_hash’); – “に挿入して、データを挿入します
  • Username =” root’または1=1; *顧客を選択します。 — “、アプリケーションデータを盗むため。 あるいは
  • Username =” root’または1=1; ドロップテーブルの顧客; — “、データを削除します
  • Username =” root’または1=1 union select password_hash as fullname from users; — “、パスワードハッシュを盗む

ダメージの可能性は無限大です! もちろん、特定のアプリケーションで機能する実際のコマンドは、実際のデータモデルと、アプリケーションがSQLの変更にどのように応答するかによって異なります。 そのため、攻撃者が最初に行うことは、SQLインジェクションを使用してデータベースを識別し、そのモデルを取得しようとすることです。

これまで見てきたように、多くのアプリケーションがSQLインジェクション攻撃に対して脆弱になるのは、ユーザー入力が直接連結されてSQLコマンドを構築するという事実です。 さらに悪いことに、ほとんどの場合、これらの入力に検証ルールを適用することすらありません。

その上、ほとんどのデータベースでは、同じクエリ文字列で複数のSQLコマンドを使用できます。 アプリケーションが単一の文字列コマンドで発行されたチェーンSQL句の結果を表示しない場合でも、それらはとにかく実行されます。 場合によっては、攻撃者はアプリケーションのエラーメッセージと結果からクエリ応答を推測できます。

SQLインジェクション攻撃は非常にスクリプト化可能です。このようにして、一部のツールは、データベースモデル全体を文字ごとにテストし、エラー状態をチェックするリバースエンジニアリングを行うことができます。 トレーニングの練習として、GitHub( https://github.com/gchehab/sql_inject_sandbox )に、コーディング中に実行しないことを示す脆弱なコンテナー化されたアプリケーションのサンプルがあります。

3. SQLインジェクション攻撃のカテゴリ

アプリケーションの反応に応じて、SQLインジェクションにはさまざまなアプローチがあります。

3.1. エラーベース

アプリケーションはSQLエラーを返します。 攻撃者は、エラーメッセージをフィードバックとして使用して、アプローチを改善できます。 そのため、適切なアプリケーションエラー処理を行うことをお勧めします。アプリケーションの動作に関する情報を開示する可能性のある詳細をユーザーに直接提供しないことを強くお勧めします。

3.2. ユニオンベース

巧妙に細工されたSQLインジェクション攻撃は、ユニオン構造を使用して単一の句文字列を使用しようとし、アプリケーションをだまして追加情報を送信させます。 そのためには、攻撃者は最初にデータベースの列名に関する情報を取得している必要があります。 アプリケーションが実際のSQLエラーを送り返す場合、有効な部分を含む完全なクエリも通知することに注意してください。これにより、テーブルと列の名前が確実に開示されます。

3.3. ブラインドSQLインジェクション

以前の手法では、アプリケーションはどういうわけか、攻撃者に情報を提供するようにだまされていました。 ただし、アプリケーションがユーザーにエラーまたは誤ったSQLのフィードバックを提供しない場合は、より洗練されたが使用されます。 したがって、攻撃者は自分の試みの効果を推測する必要があります。

彼らは、ブールベースのクエリとサブストリングを使用して、必要な情報を文字ごとに繰り返し推測しようとすることができます。 これは、完全な情報へのブルートフォース攻撃の一形態です。 はい、それは非常に長い時間がかかります、そしてはい、有用な情報を収集するためにその後の何千ものクエリ。 しかし、システムが監視されないままになっている場合、それは時間と永続性の問題です。

ちなみに、ブール演算とは、yesまたはno、trueまたはfalseの情報を返す人のことです。 たとえば、ユーザーがアプリケーションに存在しないことを通知することは、この攻撃にとって公正なゲームです。 そのため、セキュア対応システムは、ログインの失敗がユーザーまたはパスワードのエラーによるものかどうかを決して開示しません。

システムにブール演算がない場合でも、攻撃者は応答の時間を測定できます。 データベースで見つかった情報は、そうでない情報よりも常に高速に取得されます。 推測するだけで十分かもしれません。 セキュア対応アプリケーションは、クエリ結果に関係なく、定期的な応答時間を実現するように調整される場合があります。 さらに、彼らはしつこい繰り返しのクエリへの応答時間を短縮することができます。

4. 高度なSQLインジェクション難読化手法

一部のアプリケーションは、入力データでSQL予約語を検索することで攻撃の検出を開始しました(’ select ‘、’ update ‘、ínsert‘、’ delete [たとえば、X159X]’、’ drop ‘、’ create ‘)。 それだけでは十分ではありません。

攻撃者は、創造的な難読化手法を使用して検出を回避します。 小文字と大文字を混在させる、エスケープ文字を使用する、またはSQLコマンドを使用してASCIIコードを文字に変換することは、その戦術の一部です。 以下のコードは、実際の難読化された攻撃の抜粋です。

GET http://www.somesite.com/Portal/showPortalPage.do?action&codItem=(cONVErt(int,(ChAR(58)%2BChAR(113)%2BChAR(101)%2BChAR(118)%2BChAR(58)%2B(selECT%2F%2A%2A%2FTOP%2F%2A%2A%2F1%2F%2A%2A%2FsubsTriNG((IsNull(caSt(APP..syscolumns.name%2F%2A%2A%2FaS%2F%2A%2A%2FNVARChAR(4000)),ChAR(32))),1,100)%2F%2A%2A%2FFrom ...

このコマンドを解析すると、抜粋は次のようになります(太字の攻撃ペイロード):

GET http://www.somesite.com/Portal/showPortalPage.do?action&coditem=<strong>(</strong>
<strong>convert(int,(:qev:(select top 1 substring((isnull(cast(app..syscolumns.name as nvarchar(4000)), )),1,100) from</strong> ...

すごいですね。 それを適切に解析するために巨大なスクリプトを書かなければなりませんでした…

5. SQLインジェクション攻撃の軽減:パラメーターバインディング

どうすれば、これらの厄介な、それほど小さなものではない攻撃から身を守ることができますか? まず第一に、私たちはすべきです入力データを使用してクエリを直接作成しないでください。  つまり、SQLを作成するために常にパラメータバインディングを使用します。 最新のオブジェクトリレーショナルマッピングフレームワークの多くは、パラメータバインディングを使用しています。 ただし、それらの動作はオーバーライドされる可能性があります。たとえば、独自の生のSQL句を作成します。

パラメータバインディングは、あらゆる業界のジャックの一種です。 SQLインジェクションに対して中程度の保護を提供するだけでなく、データベースのパフォーマンスも向上させます。 前に示したサンプルコードに対応する基本擬似コードは次のとおりです。

SQLを直接実行する代わりに、次のようになります。

  • prepare コマンドは、SQLテンプレートをデータベースに送信します。 このテンプレートに基づいて、データベースエンジンはコマンドを解析し、実行プランを生成してキャッシュします。
  • bindParam コマンドは、使用するパラメーターについて通知します。 データベースは他のデータアクセス戦略を確立できます
  • 実行コマンド、データベースにクエリの実行を要求します
  • データを取得する

SQLステートメントを準備したら、さまざまなパラメーターを使用して再度実行できます。 手順2〜4を再生するだけで、多くのデータベースリソースを節約できます。

この手法を使用すると、申請フォームを介して送信されるすべての情報は、クエリのパラメータとしてのみ使用されます。 そうすれば、SQLクエリ自体と混同されることはありません。 。 コードがもう少し多くのラインコードを取得することは明らかです。 したがって、一般的なデザインパターンは、SQLの準備、パラメーターバインディング、およびクエリの実行をライブラリ関数にカプセル化することです。

5.1. その他のフレームワーク

どのフレームワークにも独自のパラメータバインディング方法があることがわかります。

  • Java EE – PreparedStatement()を使用して変数をバインドします
  • Hibernate – createQuery()を使用して名前付きパラメーターをバインドします
  • PHP –PDOでは prepare ステートメントとステートメントを作成するためのデータベースオブジェクトメソッド bindParam 名前付きパラメーターをバインドするメソッド
  • .NET –次のようなパラメータ化されたクエリを使用する SqlCommand() また OleDbCommand()

5.2. パラメータバインディングのその他の利点

同じ準備済みSQLステートメントを後で使用すると、パラメーターが異なっていても、キャッシュされた実行プランが再利用されます。 これにより、パフォーマンスが大幅に向上します。 ちなみに、最近のデータベースは、パラメーターをバインドしていない句に対しても、クエリの計画を再利用しようとします。 一方で、それでもパフォーマンスのオーバーヘッドがいくらか追加されます。

ちなみに、パラメータバインディングを使用しないことは、かつてデータベースのパフォーマンスを低下させる最も簡単な方法の1つでした。 データベースキャッシュ領域全体を上書きするサービス拒否攻撃は、次のような単純なスクリプトで実行できます。

6. 追加のヒントとグッドプラクティス

パラメータバインディングに加えて、コードの保護とセキュリティを強化するために心に留めておく必要のあるいくつかの優れたプラクティスもあります。

6.1. 入力フォームの検証とエスケープ

パラメータバインディングは、優れた保護手法です。 それでも、すべての入力データを予想される境界とコンテンツに対して適切に検証することを強くお勧めします。 これにより、他の形式のリモートコード実行攻撃に対する回復力が追加される可能性があります。

つまり、データをさらに使用する前に、データが適切なドメインに属しているかどうかを確認します。 たとえば、数値の場合、それらが10進文字のみであり、境界内にあるかどうかを確認します。 通常のテキストの場合は、英数字のみがあるかどうかなどを確認してください。 また、入力フィールドに実際に必要な数よりも多くの文字を受け入れさせないようにします。 常に適切な制限を課します。

6.2. ストアドプロシージャ

パラメータバインディングと同様に、ストアドプロシージャを使用すると、SQLクエリパラメータがステートメントから分離されます。 これは、パラメーターが関数パラメーターとしてデータベースに送信されるため、データベースがそれらをステートメントと混合してはならないために発生します。 もちろん、ストアドプロシージャが文字列の連結を使用して動的クエリをその場で作成しようとしない限り、SQLインジェクションの安全性が保証されます。

6.3. 最小特権

いずれにせよ、ご覧のとおり、特にコーディングされていない限り、クエリはアプリケーションのデータベースアクセス権限を使用して実行されます。 この動作のリスクを軽減する1つの方法は、アプリケーションデータベースサービスアカウントを必要最小限のアクセスレベルに調整することです。

アカウントのDBAまたはスキーマ所有者特権は最小の開始点ではありません。 しかし、それは確かにもっと制限される可能性があります。 場合によっては、より高い特権のタスクに特定のアプリケーションアカウントを設定することも良い習慣です。

6.4. Webアプリケーションファイアウォール

また、クローズドソースまたは修正が難しい脆弱なアプリケーションがある場合はどうなりますか?

その場合、Webアプリケーションファイアウォールを使用できます。 通常のネットワークファイアウォールと混同しないでください。

Webアプリケーションファイアウォールは、OSIアプリケーション層で動作するソフトウェアソリューションです。 これは、アプリケーションのデータフローのすべての側面を検査することを意味します。 SSL / TLS https など)接続では、これを行うためにサーバーの秘密鍵が必要です。 アプリケーションの有効なパラメータと使用パターンを学習して、それらを強制することができます。 これは、ユーザー入力の注入パターンを検証、エスケープ、および検出するために、前述の保護技術のいくつかを適用しようとします。

さらに、 SSNやクレジットカード番号などの自動化されたWebリクエストやデータ漏洩を検出して、それ以上の試行をブロックすることができます。 それらの主な問題は、特にアプリケーションの動作が完全に理解されていない場合、構成が難しく、誤検知が発生しやすいことです。

Modsecurity Naxsi Shadow Daemon など、商用パッケージに切り替える前に試すことができるオープンソースのWebアプリケーションファイアウォールオプションもいくつかあります。 また、さまざまなクラウドベースのセキュリティまたはCDN(コンテンツ配信ネットワーク)ソリューションには、サービスに対するそのような保護が含まれています。

7. 結論

ずさんなコーディングが開く可能性のある脆弱性を知ることは、安全なアプリケーション開発の鍵です。 また、攻撃がどのように機能するかを知ることで、攻撃を軽減する方法をよりよく理解できます。 これは最も一般的な攻撃の1つであり、簡単に修正できるものの1つですが、SQLインジェクション用に開いているアプリケーションはまだたくさんあります。

このチュートリアルでは、SQLインジェクションについてもう少し説明しました。 脆弱なアプリケーションはどのように機能し、それらを修正するためのベストプラクティスは何ですか。