web-dev-qa-db-ja.com

既存のWebサイトへのSQLインジェクションの影響を制限し、リスクを減らす方法は?

当社のウェブサイトは100%APIベースです(SPAクライアントを使用)。最近、ハッカーは次のようにSQLインジェクション(およびクラッキングpwd)を介して管理者のパスワード(SHA-256でハッシュ化)を取得することに成功しました:

https://example.com/api/products?userId=3645&type=sqlinject here

これは単なる例ですが、私たちには致命的です。幸いなことに、それはニースのハッカーであり、彼はハックについて私たちに電子メールを送った。 5年間存在し続けているウェブサイトにとって絶え間ない攻撃を受けている幸運なことです。

はい、MySQLにデータを送信する前に、ユーザー入力をどこでもチェックし、適切にフォーマット/エスケープ/準備済みステートメントを実行する必要があることを知っています。私たちは知っています、そして私たちはそれを修正しています。しかし、100%安全にするのに十分な開発者/テスターがいません。

ここで、SQLが何らかの形で注入される可能性が0.1%であると想定します。ハッカーがそれを見つけて被害を可能な限り制限するのをどのように困難にするのでしょうか?

これまでの迅速な修正/追加の測定:

  1. すべてのAPIによって共有される出力「ゲート」で、以前のように未加工のPDOExceptionとメッセージを送信しなくなりました。ただし、例外が発生したことをクライアントに伝える必要があります。

    {type: exception, code: err643, message: 'contact support for err643'}
    

    ハッカーが例外を見た場合、彼は挑戦し続けることを私は知っています...

  2. ユーザーPHPがMySQLへの接続に使用するのは、テーブルへのCRUDのみです。それで十分ですか?

  3. すべてのテーブルの名前を変更します(ハッカーがテーブル名を推測できるように、オープンソースを使用しています)。

他に何をすべきか?

更新:コメントが多いので、補足情報を教えてください

  1. まず、これはLEGACYアプリであり、エンタープライズグレードではなく、学生のような人々によって開発されました。開発チームはどこにも見当たらず、今では数百のデータアクセスクラスを扱う1〜2のハイブリッド開発テスト担当者しかいません。
  2. パスワードはSHA-256のハッシュです。そして、それを推奨されるphpの方法に変更しています。
  3. SQLインジェクションは17歳です。なぜそれがまだ残っているのですか?
  4. SQLインジェクションに対して準備されたステートメントは100%安全ですか?
34
Phung D. An

回避策や半分の修正に多くの時間を費やさないでください。ここで提案されていることを実装するために費やす1分ごとに、準備済みステートメントの実装に費やすことができます。それが唯一の真の解決策です。

SQLiの脆弱性がある場合は、攻撃者を遅くしようとして何をしようとも、最終的に攻撃者が勝つでしょう。したがって、改善を加えることはできますが、問題の根本原因を修正しない限り、結局は負けゲームをプレイしていることに注意してください。

これまでに試した内容については、エラーメッセージを非表示にすることから始めます。さらに厳しいアクセス許可を適用することをお勧めします(以下を参照)。 テーブル名の変更が役立つかどうかは議論の余地があります ですが、おそらく害はありません。

では、これらの権限についてはどうでしょうか?データベースユーザーが実行できることを、テーブルレベルまたは列レベルまで可能な限り制限します。複数のデータベースユーザーを作成して、さらにロックダウンすることもできます。たとえば、実際に書き込みを行う場合は、書き込み権限を持つデータベースユーザーのみを使用してください。実際にパスワード列を読み取る必要がある場合にのみ、パスワード列を読み取る権限を持つユーザーと接続してください。このように、他のクエリにインジェクションの脆弱性がある場合、それを利用してパスワードをリークすることはできません。

別のオプションは、Apacheのmod_securityなどのWebアプリケーションファイアウォール(WAF)を使用することです。疑わしいと思われるリクエストをブロックするように設定できます。ただし、完璧なWAFはありません。そして、それを構成するには時間とエネルギーが必要です。その時間を使用して準備済みステートメントを実装する方がよいでしょう。

繰り返しになりますが、これによって誤った安心感を抱かせないでください。問題の根本的な原因を修正する代わりはありません。

80
Anders

唯一正しい方法は、準備済みステートメントを使用することです。

  1. エラーメッセージを偽装すると、少し難しくなりますが、攻撃者を阻止することはできません。
  2. 権限を制限することはできますが、ユーザーに付与されたすべての権限は攻撃者によって取得される可能性があります。場合によっては、SQLインジェクションからシェルコマンドを実行することもできます。
  3. テーブルの名前を変更しても役に立ちません。ツールsqlmapは、charごとにテーブル名をブルートフォースします。これは多くの時間を必要としません。

攻撃者をより困難にするため、または疑わしい呼び出しについて警告を発するために呼び出しの数を制限することもできますが、このマニュアルを停止できますが、この問題に対処する唯一の正しい方法は準備されたステートメントを使用することです。ソースコードをgrepして、動的なコンテンツを含むSQLステートメントを調べて置き換えます。

65
trietend

わかっていますが、100%安全にするのに十分な開発者/テスターがいません。

これが本当の問題です。より多くの人を雇うか、優先順位を再割り当てするまで、あなたは安全ではありません。攻撃者の99.9%を阻止することは、データが侵害されたことを意味します。バンドエイドソリューションは悪いであり、それらはしない機能します。実際の問題を修正する間、SQLアクセスパターンをリファクタリングする時間を無駄にしないでください。

コードソリューションに関しては、準備されたステートメントを使用することは、ここで多くの人が提案しているように、SQLを安全に使用する最も簡単な方法です。

Phpを使用して引数を自分でクリーンアップするよりもはるかに簡単で、時間も大幅に短縮されます。 SQL関連のすべてのコードベース全体をgrepして修正するだけです。

25
Gloweye

コードからすべてのSQLを永久にパージする

この問題の最善の解決策は、すべてのSQLコードをリテラル文字列としてコードから削除し、二度と導入しないことです。代わりに、HibernateやEntity Frameworkなどの [〜#〜] orm [〜#〜] ソフトウェアを使用してください。とにかく、このソフトウェアは生のSQLよりもはるかに使いやすく、最終的にデータベースにアクセスするときにSQLを自動的にパラメーター化します。可能であれば、このポリシーを使用してください。

これらのパッケージを学習して実装する時間がない場合、または他の理由でそれらを使用できない場合、 準備されたステートメント を使用して、trietendの答えが次善の策です。これにより、SQLインジェクションから安全になります。つまり、開発者に迅速なソリューションを提供するように圧力をかけるまで、開発者はもう一度、単一の変数をパラメーター化することを忘れ、最初からやり直すことができます。

わかっていますが、100%安全にするのに十分な開発者/テスターがいません。

準備されたステートメントはすべての一般的なWeb言語(PHP、Python、Java、node.jsなど)で取るに足らないものであり、SQLインジェクションに対してほぼ100%効果的な対策であるため、このステートメントの意味が完全に明確ではありません。 。

あなたは開発を外注していて、彼らが契約であなたのために書いたコードをQAする余裕がないということですか?あなたはそれをする余裕がありません:あなたはまだここの責任者です。

あなたの開発者はコードベースを更新するのに数時間かかるには機能の実装に忙しいということですか?準備されたステートメント以外はすべて時間がかかり、コストがかかります(例:ORM、難読化、きめ細かい権限など)。

あなたの開発者はこの単純なタスクを実行するのに十分な能力がないことを意味しますか?これはかなり大きな問題です(正確には、SQLインジェクションはその時点で最大の問題ではありません)。

あなたのコードベースは、ネストされたincludesのショットガン形式のスパゲッティロジックの混乱であり、有能な開発者でさえも、そうでなければ数時間の単純なタスクであるものを実行するために永遠にかかるでしょうか?同様に、より大きな問題。

どちらにせよ、解決策は、昨日、準備されたステートメントを使用することです。

9
Jared Smith

最初の「準備されたステートメント」のアドバイスが実装されたら、SQLインジェクションについて読むことをお勧めします。 「SQLインジェクションを防ぐにはどうすればよいですか」という質問は、かなり大まかな質問です。セキュリティは、妥協のリスクを減らすために(開発チーム全体が)熟知している必要があるテーマです。 1つの穴は、他のすべての努力を損ないます。

こちらのOpen Web Application Security Project(OWASP)にアクセスしてください。 https://www.owasp.org/index.php/SQL_Injection

特に追加の資料:

  • SQLインジェクション防止チートシート
  • クエリのパラメーター化に関するチートシート
  • SQLインジェクションの脆弱性を回避する方法に関するガイド記事

そしてまた

  • SQLインジェクションの脆弱性のコードを確認する方法
5
Nicolas

暫定的な対策は、Webアプリケーションファイアウォール(WAF)を調べることです。これをサービスとして提供している会社は数社あり、サービスも提供しているクラウドプロバイダーはいくつかあります。 CloudFlareは1つを提供しているようです。1つのクライアントでIncapsulaを使用したことがあり、AWS WAFオファリングにはSQLインジェクション用の管理されたルールセットがいくつかあることを知っています。

これが機能する方法は、すべてのトラフィックが(サーバーに含めるか、CDNの場合と同じようにDNSがWAFを指すように設定することによって)WAFとの間で送受信されることで、SQLインジェクションの試みのように見えるものを探しますパラメータ。すべての攻撃をキャッチするわけではなく、正当なトラフィックをブロックする可能性がありますが、これは利用可能なバンドエイドの修正です。

4
CoderTao

「十分な開発者がいない」とは、組織内の影響力のある人々がより高い優先順位を持っていると考えていることを意味します。したがって、セキュリティの問題を修正することは、費用便益分析で他のものに負けることになります。

ここでのあなたの仕事は、修正を実装することと同じくらい、彼らの考えを変えることです。何が問題になっているのかを数値化します-金銭と評判の両方の観点から、別の違反の被害は何ですか? 必須ユーザーのデータの侵害を報告することは、管轄区域の法律にある可能性があります。それは恥ずかしいでしょうか?あなたの組織の貧弱な慣行に関するメディアの報道はありますか?

セキュリティを修正するための投資を保険と考えてください。振り返ってみると、建物が10年間燃えなかったことがわかった後、保険料が無駄だったと主張するかもしれません。しかし、あなたとあなたの組織doあなたは不確実な未来に直面しているので、保険料を支払います。それはリスク管理です。

リスクには、確率と影響の2つの要素があります。別の違反の可能性は高いです-あなたはそれが可能であり、少なくとも一度は行われたことを知っています。潜在的な結果も悪い場合は、セキュリティの脆弱性を修正する優先順位を上げる必要があります。

2
Concrete Gannet

みんなが言ったように、サイトのコードを修正します。

壁に任意のパッチを作成することができますが、壁が正しく構築されるまで、すべてのパッチは出血の問題の回避策となります。

コードをクリーンアップするための他の提案は、Linus Torvaldsがmasterbationと呼ぶものです。

  • コードを修正し(最も明らかな注入ポイントから最後のI.E Getパラメータまで)、準備されたステートメントを使用します。 phpでのSQLインジェクションを防ぐにはどうすればよいですか

    • できるまで、[〜#〜] waf [〜#〜](IE ModSecurity )を一時的に試すことができます物事を修正するために。

    • おそらく、「サイトの人々のテスト」、または行われている危険なクエリのブロックに対する、ある種のIP禁止を試してください。

    • これが解決されるまで、一時的な(安全な)ログイン画面を前面に表示することができる場合。

    • これがcloudflareなどの問題が解決されるまで、サードパーティのサービスを試してください。

PDOは、PHP 5.1)以来利用可能です...開発者は、それらが貧弱なコーディング習慣の結果であることを理解する必要があり、私は個人的に品質への説明責任があるはずだと思います提供されている仕事の。

とにかく、PDOは簡単に実装できます。開発者がより良い品質を提供できない場合は、パージを提案します...

1
Qndel

これまでのすべての素晴らしい答えに加えて、ユーザー資格情報を抽出する特定の問題に取り組む場合は、データベースをリファクタリングして、次のような厳密なアクセス権を持つ個別のテーブルにパスワードを含めることができます。 Unixシステムがそれらを/ etc/shadowに配置する方法。

資格情報を検証するには、入力したパスワード(またはハッシュ)をパラメーターとして取り、ブール値を返すストアドプロシージャが必要です。つまり、認証情報を検証するジョブをデータベースにオフロードし、実際のハッシュがデータベースから離れることはありません。

これは最小限のリファクタリングを必要とし、特定のSQLインジェクションではなく、根本的な問題を解決しますが、次のインジェクションに対して脆弱です。

0
Tom

最も重要なルールは次のとおりです。

安全なオプションを最も簡単なオプションにするライブラリ、ツール、およびAPIを使用します。

Nachtは、このルールのサブセットである「ORMを使用する」と述べています。

準備されたステートメントはこのルールに違反します。それらは使用するのが面倒であり、コードの膨張を引き起こします。また、データベースによっては、準備されていないステートメントよりも遅くなることがよくあります。それらは有効な解決策です。私はそれらを使用する人を批判しませんが、それらは必ずしも最良かつ最も便利な解決策ではありません。

そして、私たちは安全なオプションが簡単で便利であるようにしたいので、プログラマは自己利益と怠惰からそれを使用します。安全でないオプションを使用するために、プログラマーが実際に邪魔にならないように努力し、ある程度の労力を費やす必要があります!...

より良い解決策はPython dbapiのようなAPIです。私は何年も前に同等のphpを書きました。

db_query( "SELECT * FROM mytable WHERE id=%s", $id )

これは準備されたステートメントよりも便利です。入力するのは1行だけです。また、foreach()が使用できる形式で結果を返すので、fetch()などよりもはるかに簡単に使用できます。さらに重要なことは、このAPIの背後に隠されたコードがエスケープを適切に処理することです。これにより、SQLインジェクションは無関係になります。 ORMを使用しない場合、これはクエリの約99%を処理します。動的クエリ(通常は検索)には特別な処理が必要ですが、それでも簡単です。

このルールを念頭に置いておくと、いつかhtmlspecialchars()を使用する必要があるのか​​、なぜ言語に安全な設定を行う機能がないのか(つまり、XSSの脆弱性がない)のか疑問に思われるでしょう。最も使いやすい?なんでそうなの?そして、それはあなたがPHPを落とすときです。

0
peufeu