ストアドプロシージャはSQLインジェクションを防ぎますか?


83

ストアドプロシージャがPostgreSQLデータベースに対するSQLインジェクション攻撃を防ぐのは本当ですか?少し調べてみると、ストアドプロシージャのみを使用していても、SQL Server、Oracle、MySQLはSQLインジェクションに対して安全ではないことがわかりました。ただし、この問題はPostgreSQLには存在しません。

PostgreSQLコアのストアドプロシージャの実装は、SQLインジェクション攻撃を防ぎますか?または、ストアドプロシージャのみを使用する場合でも、PostgreSQLはSQLインジェクションの影響を受けますか?もしそうなら、例を示してください(本、サイト、論文など)。


4
奇妙なことに、ここでの一番の答えは主にSQL Serverを扱うOTですが、質問はPostgresについてです。Postgresの関連する回答はdba.stackexchange.com/questions/49699/…です。他の人のカップルがありますが、検索を試してみてください。dba.stackexchange.com/...
アーウィンBrandstetter

@ErwinBrandstetterは、元の質問に(OPによって)postgresのタグが付けられておらず、他のいくつかのDBMSに言及していました。それが他のDBMSに焦点を当てたさまざまな答えの理由だと思います。Postgresに焦点を合わせてもう1つ追加することをお勧めします。
ypercubeᵀᴹ

@ypercubeᵀᴹ:時間があるときに答えをここに追加します。その間、私はdba.stackexchange.com/questions/49699/…をより明確で包括的なものに更新しました。
アーウィンブランドステッター

回答:


71

いいえ、ストアドプロシージャはSQLインジェクションを妨げません。残念ながらSQLインジェクションを許可するストアドプロシージャの実際の例(私が働いている場所で誰かが作成した社内アプリから)です。

このSQLサーバーコード:

CREATE PROCEDURE [dbo].[sp_colunmName2]   
    @columnName as nvarchar(30),
    @type as nvarchar(30), 
    @searchText as nvarchar(30)           
AS
BEGIN
    DECLARE @SQLStatement NVARCHAR(4000)
    BEGIN
        SELECT @SQLStatement = 'select * from Stations where ' 
            + @columnName + ' ' + @type + ' ' + '''' + @searchText + '''' 
        EXEC(@SQLStatement)
    END      
END
GO

postgresとほぼ同等:

CREATE or replace FUNCTION public.sp_colunmName2 (
    columnName  varchar(30),
    type varchar(30), 
    searchText  varchar(30) ) RETURNS SETOF stations LANGUAGE plpgsql            
AS
$$
DECLARE SQLStatement VARCHAR(4000);
BEGIN
    SQLStatement = 'select * from Stations where ' 
            || columnName || ' ' || type || ' ' || ''''|| searchText || '''';
    RETURN QUERY EXECUTE  SQLStatement;
END
$$;

開発者のアイデアは、汎用性の高い検索手順を作成することでしたが、その結果、WHERE句にはユーザーが必要なものをすべて含めることができ、小さなBobby Tablesからの訪問が可能になります。

SQLステートメントを使用するか、ストアドプロシージャを使用するかは関係ありません。重要なのは、SQLがパラメーターを使用するか、連結文字列を使用するかです。パラメーターはSQLインジェクションを防ぎます。連結された文字列は、SQLインジェクションを許可します。


46

SQLインジェクション攻撃は、この標準的なXKCDコミックに示されているように、信頼できない入力にクエリが直接追加され、ユーザーが任意のコードを効果的に実行できるようにする攻撃です。

したがって、状況は次のようになります。

userInput = getFromHTML# "Robert ')Drop table student;-"

クエリ= "select * * studentName ="の学生からuserInput

ストアドプロシージャは、一般に、着信パラメータが解析されることはないため、 SQLインジェクション攻撃に対する優れた防御策です。

ストアドプロシージャでは、ほとんどのDB(およびプログラム、プリコンパイルされたクエリはストアドプロシージャとしてカウントされることを忘れないでください)は次のようになります。

 

ストアドプロシージャfoo(
選択* studentName =:1の生徒から
);

次に、プログラムがアクセスを希望する場合foo(userInput)、結果を呼び出して喜んで取得します。

ストアドプロシージャは、SQLインジェクションに対する魔法のような防御ではありません。人々は、不正なストアドプロシージャを書くことができるからです。ただし、SQL注入の仕組みを理解している場合、データベースまたはプログラムに格納されているプリコンパイルされたクエリは、セキュリティホールを開くのがはるかに困難です。

SQLインジェクションの詳細を読むことができます。


29

はい、ある程度。
ストアドプロシージャだけでは、SQLインジェクションは防止されません。

OWASPからのSQLインジェクションについて最初に引用させてください

SQLインジェクション攻撃は、クライアントからアプリケーションへの入力データを介したSQLクエリの挿入または「インジェクション」で構成されます。SQLインジェクションの成功は、データベースから機密データの読み取り、データベースデータの変更(挿入/更新/削除)、データベースに対する管理操作の実行(DBMSのシャットダウンなど)、DBMSファイルに存在する特定のファイルのコンテンツの回復を可能にしますシステム、場合によってはオペレーティングシステムにコマンドを発行します。SQLインジェクション攻撃は、事前定義されたSQLコマンドの実行に影響を与えるために、SQLコマンドがデータプレーン入力に挿入されるインジェクション攻撃の一種です。

ストアドプロシージャを使用している場合でも、ユーザー入力をサニタイズする必要があり、SQLステートメントを連結しないでください。

ジェフ・アットウッドは、内のSQL連結の結果について説明し、「私にパラメータ化されたSQLを与えるのか、私に死を与えます

以下は、SQLインジェクション代替テキスト を聞くたびに頭に浮かぶ面白い漫画 です。

見ていたSQLインジェクションの防止チートシートを、予防法がきちんと説明されています...


12

文字列の連結は、SQLインジェクションの原因です。これはパラメーター化を使用して回避されます。

ストアドプロシージャは、連結するときに無効な構文を適用することでセキュリティのレイヤーを追加しますが、たとえば動的SQLを使用する場合は「安全」ではありません。

したがって、上記のコードはこれらの文字列の連結によって引き起こされます

  • exec sp_GetUser '
  • x' AND 1=(SELECT COUNT(*) FROM Client); --
  • ' , '
  • monkey
  • '

幸いなことに、これは無効な構文を与えます

それをパラメータ化すると

exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'

これの意味は

  • @UserName = x' AND 1=(SELECT COUNT(*) FROM Client); --
  • @Password = monkey

さて、上記のコードでは、ユーザーがいないと仮定しているため、行は取得されません。 x' AND 1=(SELECT COUNT(*) FROM Client); --

ストアドプロシージャが(連結された動的SQLを使用して)このように見える場合、パラメータ化されたストアドプロシージャコールは引き続きSQLインジェクションを許可します

...
SET @sql = 'SELECT userName from users where userName = ''' + 
               @UserName + 
               ''' and userPass = ''' +
               @Password +
               ''''
EXEC (@sql)
....

したがって、実証されているように、文字列の連結はSQLインジェクションの主な敵です

ストアドプロシージャは、カプセル化、トランザクション処理、アクセス許可の削減などを追加しますが、SQLインジェクションのために依然として悪用される可能性があります。

パラメーター化の詳細については、Stack Overflowをご覧ください。


10

とき、「SQLインジェクション攻撃が起こり、ユーザー入力が不適切にエンコードされている。一般的に、ユーザ入力は、ユーザが自分のクエリ、内すなわち値で送信データの一部である$_GET$_POST$_COOKIE$_REQUEST、または$_SERVER配列。しかし、ユーザの入力は、他の様々なから来ることができますソケット、リモートWebサイト、ファイルなどのソース。したがって、ユーザー入力として定数(など'foobar')以外のすべてを実際に扱う必要があります

私は最近このテーマについて徹底的に調査してきましたが、非常に興味深い資料を他の人と共有したいので、この投稿をより完全で有益なものにします。



YouTubeから


ウィキペディアから


OWASPから


PHPマニュアルから


MicrosoftおよびOracleから


スタックオーバーフロー


SQLインジェクションスキャナー


2

ストアドプロシージャはSQLインジェクションを魔法のように防ぐことはありませんが、SQLのインジェクションを防ぐことは非常に簡単です。必要なことは、次のようなものだけです(Postgresの例):

CREATE OR REPLACE FUNCTION my_func (
  IN in_user_id INT 
)
[snip]
  SELECT user_id, name, address FROM my_table WHERE user_id = in_user_id; --BAM! SQL INJECTION IMMUNE!!
[snip]

それでおしまい!この問題は、文字列連結(つまり、動的SQL)を介してクエリを形成する場合にのみ発生し、そのような場合でもバインドできる場合があります!(データベースに依存します。)

動的クエリでSQLインジェクションを回避する方法:

手順1)動的クエリが本当に必要かどうかを自問します。入力を設定するためだけに文字列をつなぎ合わせているのであれば、おそらく間違っているでしょう。(このルールには例外があります。1つの例外は、一部のデータベースでクエリをレポートすることです。実行ごとに新しいクエリを強制的にコンパイルしないと、パフォーマンスの問題が発生する場合があります。 )

ステップ2)特定のRDBMSの変数を設定する適切な方法を調査します。たとえば、Oracleでは次のことができます(ドキュメントから引用)。

sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE ' 
           || v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!

ここでは、まだ入力を連結していません。あなたは安全にバインドしています!やった!

データベースが上記のようなものをサポートしていない場合(願わくば、どれもまだこれほど悪いわけではありませんが、私は驚かないでしょう)-または、入力を連結する必要がある場合(クエリをレポートする「時々」の場合のように)上記でほのめかしました)、適切なエスケープ機能を使用する必要があります。自分で書いてはいけません。たとえば、postgresはquote_literal()関数を提供します。だからあなたは実行します:

sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);

このように、in_nameが「[snip] or 1 = 1」(「または1 = 1」の部分はすべての行を選択することを意味するため、ユーザーは給与を表示できないはずです!)結果の文字列を作成します:

SELECT salary FROM employees WHERE name = '[snip] or 1=1'

結果が見つかりません(本当に奇妙な名前の従業員がいない限り)。

それがその要点です!今、私はちょうどポイント自宅を駆動するために、SQLインジェクションの対象にOracleの第一人者トム・カイトにより、古典的な記事へのリンクを残すみましょう:Linkyは


言及することを忘れないでくださいquote_ident()-しかし、一般に、インジェクション防止の動的SQLを記述する最も簡単な方法は、識別子とリテラルのformat()プレースホルダー%Iを使用すること%Lです。そうすれば、SQLは||and quote_....()関数を使用した同等のバージョンよりもはるかに読みやすくなります
-a_horse_with_no_name
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.