ストアドプロシージャについてよくわからないことがあります。 MySqlを使用した簡単な例で、ストアドプロシージャがどのようにSQLインジェクションを防ぐかを誰かに説明できますか?.
ストアドプロシージャは、パラメーター化されたクエリの形式です。 SQLインジェクションを引き起こす根本的な問題は、データがクエリ言語として扱われることです。
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
この例では、$password
〜foo' OR 'x'='x
、これを取得します:
SELECT * FROM users WHERE username = 'blah' AND password = 'foo' OR 'x'='x'
文字「x」は常に文字「x」と等しいため、このクエリは、ユーザー/パスが正しいかどうかに関係なく、常に行を返します。データベースには、コンテキストのない文字列しか与えられていないため、これが意図していないことを認識できません。
これを防ぐには、クエリとデータの違いを知る必要があります。ストアドプロシージャでは、パラメーターのマーカーを使用してクエリを事前に記述し、後でデータを渡せるようにすることで、この問題を解決しています。例えば:
SELECT * FROM users WHERE username = ? AND password = ?
データベースドライバーは、このストアドプロシージャの名前(または、標準のパラメーター化されたクエリでは、クエリテキスト自体)とパラメーターのリストを、プロトコルの個別のエンティティとして送信します。つまり、データベースサーバーはクエリ文字列をクエリ言語として安全に解析し、パラメーターをあいまいさなくデータとしてのみ扱うことができます。
私も 長い回答を書いた しばらく前に、これがすべての使用法である場合は、より詳細な方法でこのすべてを説明します。
ストアドプロシージャはSQLインジェクションの影響を受けません。ここで説明したように:
ストアドプロシージャ内で動的SQLを作成できる限り、SQLインジェクションに対して脆弱です。
MySQL5.0.13以降では、動的SQL機能を備えています。
https://stackoverflow.com/questions/190776/how-to-have-dynamic-sql-in-mysql-stored-procedure
したがって、SQLインジェクションに対して脆弱です。
SQLサーバーの例を次に示します。
http://www.sqlinjection.net/advanced/stored-procedure/
VULNERABLE STORED PROCEDURE USING EXEC STATEMENT.
CREATE PROCEDURE getDescription
@vname VARCHAR(50)
AS
EXEC('SELECT description FROM products WHERE name = '''+@vname+ '''')
RETURN
そして別の例:
VULNERABLE STORED PROCEDURE USING DYNAMIC CURSOR.
CREATE PROCEDURE printDescriptions
@vname VARCHAR(100)
AS
DECLARE @vdesc VARCHAR(1000)
DECLARE @vsql VARCHAR(4000)
SET @vsql = 'SELECT description FROM products WHERE name='''+@vname+''''
DECLARE cur CURSOR FOR EXEC @vsql
OPEN cur
FETCH NEXT FROM cur INTO @vdesc
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @vdesc
FETCH NEXT FROM cur INTO @vdesc
END
CLOSE cur
DEALLOCATE cur
RETURN
Oracleの例は次のとおりです。
http://software-security.sans.org/developer-how-to/fix-sql-injection-in-Oracle-database-code
https://www.blackhat.com/presentations/bh-europe-04/bh-eu-04-litchfield.pdf
上記のBlackhat EuropeのDavid Litchfieldによるプレゼンテーションには、より深刻なものがあります。SQLインジェクションから特権の昇格につながる可能性があるため、ストアドプロシージャが作成されている場合、通常のOracleユーザーはDBAとして実行できます。 DBA(たとえば、すべてのOracleシステムオブジェクト)。
SQLデータベースは、いくつかのステップでステートメントを処理します。最初にSQLステートメントのテストが解析され、その後、最適化およびコンパイルされます。これが完了すると、データベースには指定されたSQLステートメントを実行できる内部ソフトウェアが組み込まれます。
ストアドプロシージャは事前にコンパイルされています。つまり、データベースは、使用する前にその内部ソフトウェアを作成します。この場合、パラメータの影響を受けずにプログラムコードのみが解釈されます。
完全なSQLステートメントを含むパラメーターをデータベースに渡すと、上記の手順が処理されます。
例えば ...
SELECT * FROM myTable WHERE id=1
またはあなたはそのようなものを与える...
SELECT * FROM myTable WHERE id=1;DROP TABLE myTable
通常、誰も彼のプログラムコードで2番目のようなステートメントを記述しませんが、たとえばWebリクエストから直接パラメーターを取得すると、そのようなステートメントが生成される可能性があります。
var sqlString="SELECT * FROM myTable WHERE id=";
sqlString = sqlString+request.getParameter("id");
// database parse, compile and optimize
var result=database.doQuery(sqlString);
ストアドプロシージャまたはプリペアドステートメントを使用する場合。解析とコンパイルのプロセスはすでにデータベースで行われています。すべての解釈は、プログラムコードによって異なります。これを呼び出すと、データベースは、指定されたパラメーターをプリコンパイル済みコードに挿入するだけで、受け入れられるデータ型になります。
var sqlString = "call queryMyTable(?)";
// get the precompiled statement from database
var statement = database.createStatement(sqlString);
// inject the parameter
statement.setParameter(1,request.getParameter("id"));
// if 'id' is a number it works fine ...
// but if 'id' is '1;DROP TABLE myTable' you will got a type cast error and the risk of SQL injection is banned
var result = statement.execute();
ストアドプロシージャと準備されたステートメントは、同等のセキュリティを考慮しています。
それについて考えるもう1つの方法は、明示的であり、すでに与えられた答えを補強することです。
SELECT * FROM users WHERE username = 'blah' AND password = 'foo' OR 'x'='x'
準備されたステートメントがない場合、「foo」の後のOR
は扱われますコードとして
ここで、データベースの観点からの準備済みステートメントで、試行されたパスワードは次のとおりです。
'foo' OR 'x'='x'
つまり、「foo」が処理された後のOR
(およびその他すべて)データとして