web-dev-qa-db-ja.com

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

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

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

84
Am1rr3zA

いいえ、ストアドプロシージャは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インジェクションを可能にします。

71
Kyralessa

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

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

 userInput = getFromHTML# "Robert ')ドロップテーブルの学生;-" 
 
クエリ= "select * from studentName =" + userInput 

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

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

 
 
 createストアドプロシージャfoo(
 select * from student where where StudentName =:1 
); 
 

次に、プログラムがアクセスを要求すると、foo(userInput)が呼び出され、結果が正常に取得されます。

badストアドプロシージャを書くことができるため、ストアドプロシージャはSQL-Injectionに対する魔法の防御策ではありません。ただし、SQL注入がどのように機能するかを理解している場合、データベースまたはプログラムに保存されているプリコンパイル済みクエリは、にセキュリティホールを開くことがはるかに困難です。

あなたはSQLインジェクションについてもっと読むことができます:

46

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

[〜#〜] owasp [〜#〜] からSQLインジェクションについて最初に引用させてください

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

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

Jeff Attwoodは、「 Give me parameterized SQL、or me me death 」でSQLを連結した結果を説明しました

以下は、SQLインジェクションを聞くたびに頭に浮かぶ面白い漫画です alt text ポイントがわかったと思います:-)

SQLインジェクション防止のチートシート を見てください。防止方法はきちんと説明されています...

29
CoderHawk

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

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

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

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

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

Parametrisingそれは与えるでしょう

_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インジェクションのために悪用される可能性があります。

parametrisation の詳細については、Stack Overflowをご覧ください。

12
gbn

「SQLインジェクション攻撃は、user inputが正しくエンコードされていない場合に発生します。通常、ユーザー入力は、ユーザーがクエリで送信するデータ、つまり$_GET$_POST$_COOKIE$_REQUEST、または$_SERVER配列。ただし、ユーザー入力は、ソケット、リモートWebサイト、ファイルなど、他のさまざまなソースから取得することもできます。したがって、実際には定数以外のすべてを処理する必要があります'foobar'など)ユーザー入力として。 "

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



YouTubeから


ウィキペディアから


OWASPから


From PHP Manual


MicrosoftおよびOracleから


スタックオーバーフロー


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

10
Ilia Rostovtsev

ストアドプロシージャは魔法のように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'のような不正なものである場合( "or 1 = 1"の部分は、すべての行を選択することを意味し、ユーザーは表示してはいけない給与を見ることができます!)、quote_literalは、結果の文字列を作る:

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

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

それが要点です!ここで、SQLインジェクションをテーマにしたOracleの達人Tom Kyteによる古典的な投稿へのリンクを残して、要点を説明します。 Linky

2
MWDB