ホームページ >運用・保守 >安全性 >PDOの原理と正しい使用方法

PDOの原理と正しい使用方法

王林
王林転載
2020-01-11 17:29:476479ブラウズ

PDOの原理と正しい使用方法

まえがき

データベースのパラメータ化されたクエリの手法がますます一般的になるにつれて、SQL インジェクションの脆弱性は以前に比べて大幅に減少しており、PDO は最も典型的な脆弱性です。 PHP: プリコンパイルされたクエリ方法は、ますます広く使用されるようになってきています。

ご存知のとおり、PDO は PHP で SQL インジェクションを防ぐ最良の方法ですが、SQL インジェクションを 100% 排除できるわけではありません。重要なのは PDO の使用方法によって異なります。

以前の記事で、PDOシナリオで制御可能なパラメータによってマルチセンテンスが実行されるなどの問題があることを知りました(https://xz.aliyun.com/t/3950)ので、 PDO シナリオでの SQL インジェクションについて説明しましたが、再度検討されました。

PDO クエリ ステートメントは、既存のセキュリティ問題を制御できます。

まず、ローカルに新しいライブラリとテーブルを作成し、何気なく何かを書きます。

PDOの原理と正しい使用方法

次に、test.php を作成し、PDO を使用して簡単なクエリを実行します。

<?php 
try{
  $db = new PDO(&#39;mysql:host=localhost;dbname=pdotest&#39;,&#39;root&#39;,&#39;&#39;);
} 
catch(Exception $e)
{  echo $e->getMessage();
}if(isset($_GET[&#39;id&#39;]))
{
  $id = $_GET[&#39;id&#39;];
}else{
  $id=1;
}
$query = "select balabala from table1 where 1=?";echo "id:".$id."</br>";
$row = $db->prepare($query);
$row->bindParam(1,$id);
$row->execute();
$result = $row->fetch(PDO::FETCH_ASSOC);if($result)
{  echo "结果为:";
  print_r($result);  echo "</br>";
}

入力コンテンツとページ上で取得された結果を出力します。

PDOの原理と正しい使用方法

#PDO には、セキュリティ問題に関連する次の 3 つの主な設定があります。

PDO::ATTR_EMULATE_PREPARES
PDO::ATTR_ERRMODE
PDO::MYSQL_ATTR_MULTI_STATEMENTS

は、それぞれ、シミュレートされたプリコンパイル、エラー報告、およびマルチセンテンス実行に関連しています。

PDO では、デフォルトでマルチセンテンス実行とシミュレートされたプリコンパイルが可能ですが、パラメータを制御できるとスタック インジェクションが発生することは、これまでの多くの記事で書かれています。

たとえば、クエリ ステートメントを

$query = "select balabala from table1 where 1={$id}";
$row = $db->query($query);

に変更すると、$db->query() ステップが実行される前に $query に対して不正な操作を実行できます。 Useless と同等:

PDOの原理と正しい使用方法

PDO のデフォルト設定におけるセキュリティ リスク:

クエリ ステートメントに制御可能なパラメータがない場合、Is に従ってパラメータを入力します。 prepare->bindParam->executeで書いても問題ないでしょうか?

次のステートメントに従ってクエリを実行します:

$query = "select balabala from table1 where 1=?";
$row = $db->prepare($query);
$row->bindParam(1,$_GET[‘id’]);
$row->execute();

URL にパラメータ: ?id=asdasd を入力し、SET GLOBAL GENERAL_LOG=ON を設定して、. からリアルタイムで監視します。 SQL ステートメントが実際に実行する内容を見てください:

PDOの原理と正しい使用方法

シミュレートされたプリコンパイル済みリクエストの送信方法は以前の mysqli と変わらないことがわかりましたが、元のクエリ ステートメントではパラメーターが一重引用符で囲まれていませんが、ここでは一重引用符で囲まれているため、一重引用符などの特殊文字を入力してみることができます。

PDOの原理と正しい使用方法 #一重引用符がエスケープされていることがわかりました。このとき、gbk エンコーディングが設定されている場合に何が起こるかを考えずにはいられませんでした。 select * from table1 が正常に実行されたことがわかります。PDO は結果を返すだけですが、実際に実行されています。

つまり、クエリ ステートメントに制御可能なパラメータがなく、? や :id などのバインドされたパラメータのみがある場合でも、スタック インジェクションを実行できます。

複数文の実行をオフにするとどうなりますか? PDOの原理と正しい使用方法

PDO::MYSQL_ATTR_MULTI_STATEMENTS を false に設定し、上記の操作を繰り返しました。

これが機能しなくなっていることがわかりました。

PDOの原理と正しい使用方法実際には、gbk を設定するステートメントだけが実行されました。

しかし、これで終わりでしょうか?

ユニオンインジェクションなどの他の方法を試してみてはいかがでしょうか? PDOの原理と正しい使用方法

#試してみたところ、ユニオンインジェクションもできることが分かりました!複数の文を実行する必要はまったくありません。

实际上,在模拟预编译的情况下,PDO对于SQL注入的防范(PDO::queto()),无非就是将数字型的注入转变为字符型的注入,又用类似mysql_real_escape_string()的方法将单引号、双引号、反斜杠等字符进行了转义。

这种防范方法在GBK编码的情况下便可用宽字节进行绕过,而在非GBK编码的情况下,若存在二次注入的情况,是否能利用呢?

答案是否定的。

二次注入是由于对添加进数据库中的数据没有再次处理和转义而导致的,而预编译对每次查询都进行转义,则不存在二次注入的情况。

上述安全隐患,是由于未正确设置PDO造成的,在PDO的默认设置中,PDO::ATTR_EMULATE_PREPARES和PDO::MYSQL_ATTR_MULTI_STATEMENTS都是true,意味着模拟预编译和多句执行是默认开启的。

而在非模拟预编译的情况下,若语句中没有可控参数,是否还能这样做呢?

答案是否定的。

我们将PDO::ATTR_EMULATE_PREPARES设为false,来看看sql语句到底执行了什么:

PDOの原理と正しい使用方法

它对每一句sql语句都进行了预编译和执行两个操作,在执行select balabala from table1 where 1=?这句时,如果是GBK编码,那么它将会把?绑定的参数转化成16进制,这样无论输入什么样的东西都无法再进行注入了。

如果不是GBK编码,如上面所说,也不存在二次注入的情况,故可以避免SQL注入漏洞。

相同原理的Prepare Statement方法

PDO的原理,与Mysql中prepare语句是一样的。上面PDO所执行的SQL语句,用如下的方式可以等效替代:

Set @x=0x31
Prepare a from “select balabala from table1 where 1=?”
Execute a using @x

我们可以手动将输入的参数设置为@x,并将其转化为16进制,随后预编译,再执行

也就是说,不用PDO也可以仿照其原理手动设置预编译:

$db = new mysqli(&#39;localhost&#39;,&#39;root&#39;,&#39;&#39;,&#39;pdotest&#39;);if(isset($_GET[&#39;id&#39;]))
{
	$id = "0x".bin2hex($_GET[&#39;id&#39;]);
}else{
	$id=1;
}echo "id:".$id."</br>";
$db->query("set names gbk");
$db->query("set @x={$id}");
$db->query("prepare a from &#39;select balabala from table1 where 1=?&#39;");
$row = $db->query("execute a using @x");
$result = $row->fetch_assoc();if($result)
{	echo "结果为:";
	print_r($result);	echo "</br>";
}

得到的结果和使用PDO是一样的:

PDOの原理と正しい使用方法

这样设置不用担心没有合理地设置PDO,或是用了GBK编码等情况。

Prepare Statement在SQL注入中的利用

Prepare语句在防范SQL注入方面起到了非常大的作用,但是对于SQL注入攻击却也提供了新的手段。

Prepare语句最大的特点就是它可以将16进制串转为语句字符串并执行。如果我们发现了一个存在堆叠注入的场景,但过滤非常严格,便可以使用prepare语句进行绕过。

例如我们将createtable table2 like table1转化成16进制,然后执行:

PDOの原理と正しい使用方法

我们发现数据库中已经多了一个表table2。则语句成功执行了。

总结

对于此类问题的防范,主要有以下三个方面:

1. 合理、安全地使用gbk编码。即使采用PDO预编译的方式,如若配置不当,依然可造成宽字节注入

2. 使用PDO时,一定要将模拟预编译设为false

3. 可采用使用Prepare Statement手动预编译,杜绝SQL注入

相关文章教程推荐:网站安全教程

以上がPDOの原理と正しい使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はfreebuf.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。