コーダーはセキュリティを制する。共有と学習 - SQLインジェクション

2018年12月06日掲載
ジャープ・カラン・シン著
ケーススタディ

コーダーはセキュリティを制する。共有と学習 - SQLインジェクション

2018年12月06日掲載
ジャープ・カラン・シン著
リソースを見る
リソースを見る

簡単に言うと、SQL(Structured Query Language)とは、リレーショナルデータベースと通信するための言語で、開発者やデータベース管理者、アプリケーションが日々生成される膨大なデータを管理するために使用する問い合わせ言語のことです。

私たちのデータは、世界で最も価値のある商品の一つになりつつあります。価値のあるものは、悪人が自分の利益のためにそれを手に入れようとします。

攻撃者は、世界中の何百万ものデータベースに存在する機密情報を盗んだり変更したりするために、最も古く(1998年以降)、最も厄介なデータ脆弱性の1つであるSQLインジェクションを使用しています。データを安全に保つためには、開発者はSQLインジェクションについて理解する必要があります(また、その防御方法についても)。

そのために、SQLインジェクションの3つのポイントについて説明します。

  • その仕組み
  • なぜ危険なのか
  • どうやって防御するか

SQLインジェクションの理解

SQLインジェクションは、「コンテキスト」という言葉で理解することができます。

アプリケーションの中には、データ用とコード用の2つのコンテキストが存在します。コードのコンテキストは、何を実行するかをコンピュータに伝え、処理されるべきデータから分離します。

SQLインジェクションは、攻撃者が入力したデータが、SQLインタープリターによって誤ってコードとして処理されることで発生します。

一例として、ウェブサイトの入力フィールドに攻撃者が「'' OR 1=1」と入力すると、それがSQLクエリの最後に付加されます。このクエリが実行されると、データベースのすべての行に対して「true」が返されます。これは、クエリを実行したテーブルのすべてのレコードが返されることを意味します。

SQLインジェクションの影響は、壊滅的なものになる可能性があります。ログインページで発生した場合、ユーザー名やパスワードを含むすべてのユーザー記録が返される可能性があります。データを取り出すための単純なクエリが成功すれば、データを変更するためのクエリも成功するでしょう。

SQLインジェクションの脆弱性がどのようなものかを実際に確認するために、脆弱なコードを見てみましょう。

このコードをチェックしてください。

String query = "SELECT account balance FROM user_data WHERE user_name = "
+ request.getParameter("customerName");
try {
   Statement statement = connection.createStatement( ... );
   ResultSet results = statement.executeQuery( query );
}

ここでのコードは、検証を行わずに、クライアントからのパラメータ情報をSQLクエリの最後に追加するだけのものです。このような場合、攻撃者は入力フィールドやURLのパラメータにコードを入力すると、それが実行されてしまいます。

重要なのは、攻撃者が各SELECTクエリに「''OR 1=1」を追加することしかできないということではなく、攻撃者はあらゆるタイプのSQLクエリ(INSERT、UPDATE、DELETE、DROPなど)を操作し、データベースがサポートするあらゆるもので拡張することができるということです。どのようなことが可能なのかを示す素晴らしいリソースやツールが公開されています。

この問題を解決するための方法をすぐに学びます。まず、どのくらいのダメージがあるのかを理解しましょう。

SQLインジェクションが危険な理由

ここでは、SQLインジェクションが原因で発生した侵害の例を3つだけ紹介します。

  • イリノイ州選挙管理委員会のウェブサイトが、SQLインジェクションの脆弱性により侵害されました。攻撃者は、20万人の米国市民の個人データを盗みました。見つかった脆弱性の性質上、攻撃者はデータを変更することも可能でしたが、そうはしませんでした。
  • 南アフリカのウェブサイトホスティング会社であるHetzner社は、4万件の顧客記録が侵害されました。SQLインジェクションの脆弱性により、同社のデータベースに登録されているすべての顧客記録が盗まれた可能性があります。
  • 米国ミネソタ州にあるカトリック系の金融サービス会社が、SQLインジェクションを利用した不正アクセスを受けました。約13万人の顧客の口座番号を含む口座情報が盗まれました。

機密データは、アカウントの乗っ取り、パスワードのリセット、金銭の窃盗、詐欺などに利用される可能性があります。

機密情報や個人を特定できない情報であっても、別の攻撃に利用される可能性があります。住所情報や政府機関の識別番号の下4桁は、お客様になりすまして企業に連絡したり、パスワードをリセットしたりするのに使われます。

攻撃が成功すると、顧客は企業に対する信頼を失うことになります。システムへのダメージや規制による罰金からの回復には、数百万ドルの費用がかかります。

しかし、あなたがそのように終わる必要はありません。

SQLインジェクションの撲滅

SQLインジェクションを防ぐには、アプリケーションの一部を明確にラベル付けすることで、ある部分がデータなのか実行されるコードなのかをコンピュータに認識させる必要があります。これには、パラメータ化されたクエリを使用します。

SQLクエリがパラメータを使用する場合、SQLインタープリタはパラメータをデータとしてのみ使用します。コードとしては実行されません。

例えば、「''OR 1=1」のような攻撃は機能しません。データベースは「OR 1=1」という文字列を検索しますが、データベース内には見つかりません。単に肩をすくめて、"Sorry, I can't find that for you. "と言うだけです。

Javaでのパラメータ化されたクエリの例は次のようになります。

ほとんどの開発フレームワークには、SQLインジェクションに対する防御機能が組み込まれています。

.NETファミリーのEntity Frameworkのようなオブジェクトリレーショナルマッパー(ORM)は、デフォルトでクエリをパラメータ化します。これにより、SQLインジェクションの対策が何もしなくてもできるようになります。

しかし、特定のORMがどのように機能するかを知っておく必要があります。例えば、Javaの世界では人気の高いORMであるHibernate 、使い方を誤るとSQLインジェクションの危険性があります。

クエリのパラメータ化は最初で最良の防御策ですが、他にもあります。ストアドプロシージャもSQLパラメータをサポートしており、SQLインジェクションを防ぐために使用することができます。ただし、ストアドプロシージャも正しく構築されていなければなりませんのでご注意ください。

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";

PreparedStatement pstmt = connection.preparedStatement( query );
pstmt.setString(1, custname);
ResultSet results = pstmt.executeQuery( );

常に入力を検証し、サニタイズしてください。OR 1=1 "のような文字は、アプリケーションの正当なユーザーが入力することはないので、許可する必要はありません。ユーザーにエラーメッセージを表示したり、入力を処理する前にそれらの文字を取り除くことができます。

とはいえ、バリデーションやサニタイズだけに頼っていてはいけません。賢い人間はそれを回避する方法を見つけています。それらは深層部への防御(DiD)として有効ですが、パラメータ化は、すべてのベースをカバーする確実な方法です。

もうひとつの優れたDiD戦略は、データベース内で「最小特権」を使用し、入力をホワイトリスト化することです。最小限の特権を行使するということは、アプリケーションがデータベース内で無限の権限を持たないことを意味します。仮に攻撃者がアクセスしたとしても、その被害は限定的です。

OWASPでは、いくつかの言語やプラットフォームでこの脆弱性をどのように処理するかを示した素晴らしいSQLインジェクション・チートシートを用意していますが、さらに上を目指したい方は、今すぐ私たちのプラットフォームで、お好きな言語でSQLインジェクション・チャレンジをプレイすることができます。

C#でのSQLインジェクション

Node.jsにおけるSQLインジェクション

Python DjangoでのSQLインジェクション

Java SpringにおけるSQLインジェクション

旅の始まり

SQLインジェクションを理解し、それを修正するために必要なステップを理解する上で、あなたは大きな進歩を遂げました。すばらしいですね。

これまで、SQLインジェクションがどのように起こるかを説明してきました。通常、攻撃者は入力を使用してデータベースのクエリを制御し、悪意のある目的を達成します。

また、SQLインジェクションの脆弱性が悪用されることによる被害も目にしてきました。アカウントが侵害され、何百万ドルものお金が失われる......悪夢のような、そして高価なものです。

SQLインジェクションを防ぐ方法を見てきました。

  • クエリのパラメータ化
  • オブジェクトリレーショナルマッパーとストアドプロシージャの使用
  • ユーザー入力の検証とホワイトリスト化

あとは、あなた次第です。練習は、学習と習得を続けるための最良の方法です。だからこそ、私たちの ラーニングリソースSQLインジェクションの学習資料をご覧になり、無料体験版をお試しください。 無料デモプラットフォームの無料デモを試してみませんか?そうすれば、Secure Code Warrior に近づくことができるでしょう。

リソースを見る
リソースを見る

著者

Jaap Karan Singh

もっと知りたい?

セキュアコーディングに関する最新の知見をブログでご紹介しています。

当社の豊富なリソースライブラリは、安全なコーディングアップスキルに対する人間的なアプローチを強化することを目的としています。

ブログを見る
もっと知りたい?

開発者主導のセキュリティに関する最新の研究成果を入手する

ホワイトペーパーからウェビナーまで、開発者主導のセキュアコーディングを始めるために役立つリソースが満載のリソースライブラリです。今すぐご覧ください。

リソース・ハブ

コーダーはセキュリティを制する。共有と学習 - SQLインジェクション

2024年1月22日発行
Jaap Karan Singh著

簡単に言うと、SQL(Structured Query Language)とは、リレーショナルデータベースと通信するための言語で、開発者やデータベース管理者、アプリケーションが日々生成される膨大なデータを管理するために使用する問い合わせ言語のことです。

私たちのデータは、世界で最も価値のある商品の一つになりつつあります。価値のあるものは、悪人が自分の利益のためにそれを手に入れようとします。

攻撃者は、世界中の何百万ものデータベースに存在する機密情報を盗んだり変更したりするために、最も古く(1998年以降)、最も厄介なデータ脆弱性の1つであるSQLインジェクションを使用しています。データを安全に保つためには、開発者はSQLインジェクションについて理解する必要があります(また、その防御方法についても)。

そのために、SQLインジェクションの3つのポイントについて説明します。

  • その仕組み
  • なぜ危険なのか
  • どうやって防御するか

SQLインジェクションの理解

SQLインジェクションは、「コンテキスト」という言葉で理解することができます。

アプリケーションの中には、データ用とコード用の2つのコンテキストが存在します。コードのコンテキストは、何を実行するかをコンピュータに伝え、処理されるべきデータから分離します。

SQLインジェクションは、攻撃者が入力したデータが、SQLインタープリターによって誤ってコードとして処理されることで発生します。

一例として、ウェブサイトの入力フィールドに攻撃者が「'' OR 1=1」と入力すると、それがSQLクエリの最後に付加されます。このクエリが実行されると、データベースのすべての行に対して「true」が返されます。これは、クエリを実行したテーブルのすべてのレコードが返されることを意味します。

SQLインジェクションの影響は、壊滅的なものになる可能性があります。ログインページで発生した場合、ユーザー名やパスワードを含むすべてのユーザー記録が返される可能性があります。データを取り出すための単純なクエリが成功すれば、データを変更するためのクエリも成功するでしょう。

SQLインジェクションの脆弱性がどのようなものかを実際に確認するために、脆弱なコードを見てみましょう。

このコードをチェックしてください。

String query = "SELECT account balance FROM user_data WHERE user_name = "
+ request.getParameter("customerName");
try {
   Statement statement = connection.createStatement( ... );
   ResultSet results = statement.executeQuery( query );
}

ここでのコードは、検証を行わずに、クライアントからのパラメータ情報をSQLクエリの最後に追加するだけのものです。このような場合、攻撃者は入力フィールドやURLのパラメータにコードを入力すると、それが実行されてしまいます。

重要なのは、攻撃者が各SELECTクエリに「''OR 1=1」を追加することしかできないということではなく、攻撃者はあらゆるタイプのSQLクエリ(INSERT、UPDATE、DELETE、DROPなど)を操作し、データベースがサポートするあらゆるもので拡張することができるということです。どのようなことが可能なのかを示す素晴らしいリソースやツールが公開されています。

この問題を解決するための方法をすぐに学びます。まず、どのくらいのダメージがあるのかを理解しましょう。

SQLインジェクションが危険な理由

ここでは、SQLインジェクションが原因で発生した侵害の例を3つだけ紹介します。

  • イリノイ州選挙管理委員会のウェブサイトが、SQLインジェクションの脆弱性により侵害されました。攻撃者は、20万人の米国市民の個人データを盗みました。見つかった脆弱性の性質上、攻撃者はデータを変更することも可能でしたが、そうはしませんでした。
  • 南アフリカのウェブサイトホスティング会社であるHetzner社は、4万件の顧客記録が侵害されました。SQLインジェクションの脆弱性により、同社のデータベースに登録されているすべての顧客記録が盗まれた可能性があります。
  • 米国ミネソタ州にあるカトリック系の金融サービス会社が、SQLインジェクションを利用した不正アクセスを受けました。約13万人の顧客の口座番号を含む口座情報が盗まれました。

機密データは、アカウントの乗っ取り、パスワードのリセット、金銭の窃盗、詐欺などに利用される可能性があります。

機密情報や個人を特定できない情報であっても、別の攻撃に利用される可能性があります。住所情報や政府機関の識別番号の下4桁は、お客様になりすまして企業に連絡したり、パスワードをリセットしたりするのに使われます。

攻撃が成功すると、顧客は企業に対する信頼を失うことになります。システムへのダメージや規制による罰金からの回復には、数百万ドルの費用がかかります。

しかし、あなたがそのように終わる必要はありません。

SQLインジェクションの撲滅

SQLインジェクションを防ぐには、アプリケーションの一部を明確にラベル付けすることで、ある部分がデータなのか実行されるコードなのかをコンピュータに認識させる必要があります。これには、パラメータ化されたクエリを使用します。

SQLクエリがパラメータを使用する場合、SQLインタープリタはパラメータをデータとしてのみ使用します。コードとしては実行されません。

例えば、「''OR 1=1」のような攻撃は機能しません。データベースは「OR 1=1」という文字列を検索しますが、データベース内には見つかりません。単に肩をすくめて、"Sorry, I can't find that for you. "と言うだけです。

Javaでのパラメータ化されたクエリの例は次のようになります。

ほとんどの開発フレームワークには、SQLインジェクションに対する防御機能が組み込まれています。

.NETファミリーのEntity Frameworkのようなオブジェクトリレーショナルマッパー(ORM)は、デフォルトでクエリをパラメータ化します。これにより、SQLインジェクションの対策が何もしなくてもできるようになります。

しかし、特定のORMがどのように機能するかを知っておく必要があります。例えば、Javaの世界では人気の高いORMであるHibernate 、使い方を誤るとSQLインジェクションの危険性があります。

クエリのパラメータ化は最初で最良の防御策ですが、他にもあります。ストアドプロシージャもSQLパラメータをサポートしており、SQLインジェクションを防ぐために使用することができます。ただし、ストアドプロシージャも正しく構築されていなければなりませんのでご注意ください。

// This should REALLY be validated too
String custname = request.getParameter("customerName");
// perform input validation to detect attacks
String query = "SELECT account_balance FROM user_data WHERE user_name = ? ";

PreparedStatement pstmt = connection.preparedStatement( query );
pstmt.setString(1, custname);
ResultSet results = pstmt.executeQuery( );

常に入力を検証し、サニタイズしてください。OR 1=1 "のような文字は、アプリケーションの正当なユーザーが入力することはないので、許可する必要はありません。ユーザーにエラーメッセージを表示したり、入力を処理する前にそれらの文字を取り除くことができます。

とはいえ、バリデーションやサニタイズだけに頼っていてはいけません。賢い人間はそれを回避する方法を見つけています。それらは深層部への防御(DiD)として有効ですが、パラメータ化は、すべてのベースをカバーする確実な方法です。

もうひとつの優れたDiD戦略は、データベース内で「最小特権」を使用し、入力をホワイトリスト化することです。最小限の特権を行使するということは、アプリケーションがデータベース内で無限の権限を持たないことを意味します。仮に攻撃者がアクセスしたとしても、その被害は限定的です。

OWASPでは、いくつかの言語やプラットフォームでこの脆弱性をどのように処理するかを示した素晴らしいSQLインジェクション・チートシートを用意していますが、さらに上を目指したい方は、今すぐ私たちのプラットフォームで、お好きな言語でSQLインジェクション・チャレンジをプレイすることができます。

C#でのSQLインジェクション

Node.jsにおけるSQLインジェクション

Python DjangoでのSQLインジェクション

Java SpringにおけるSQLインジェクション

旅の始まり

SQLインジェクションを理解し、それを修正するために必要なステップを理解する上で、あなたは大きな進歩を遂げました。すばらしいですね。

これまで、SQLインジェクションがどのように起こるかを説明してきました。通常、攻撃者は入力を使用してデータベースのクエリを制御し、悪意のある目的を達成します。

また、SQLインジェクションの脆弱性が悪用されることによる被害も目にしてきました。アカウントが侵害され、何百万ドルものお金が失われる......悪夢のような、そして高価なものです。

SQLインジェクションを防ぐ方法を見てきました。

  • クエリのパラメータ化
  • オブジェクトリレーショナルマッパーとストアドプロシージャの使用
  • ユーザー入力の検証とホワイトリスト化

あとは、あなた次第です。練習は、学習と習得を続けるための最良の方法です。だからこそ、私たちの ラーニングリソースSQLインジェクションの学習資料をご覧になり、無料体験版をお試しください。 無料デモプラットフォームの無料デモを試してみませんか?そうすれば、Secure Code Warrior に近づくことができるでしょう。

弊社製品や関連するセキュアコーディングのトピックに関する情報をお送りする許可をお願いします。当社は、お客様の個人情報を細心の注意を払って取り扱い、マーケティング目的で他社に販売することは決してありません。

フォームを送信するには、「Analytics」のCookieを有効にしてください。完了したら、再度無効にしてください。