//连接数据库
$coon = mysql_connect('localhost','root','root') or die('Connect Error '.mysql_error());
mysql_select_db('sqlinjection',$coon) or die('Datebase Error '.mysql_error());
//构造查询语句
$query="SELECT * FROM user WHERE username='".$_GET["username"]."' AND password='".$_GET["password"]."'";
echo $query."<br/>";
//执行sql语句
$result= mysql_query($query);
//echo $result;
//判断返回数据的数目
$rowcount=mysql_num_rows($result);
//判断 如果数量大于1标示登录成功
//echo $rowcount;
if($rowcount!=0){
echo "OK";
}else{
echo "ERROR";
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SQL injection test</title>
</head>
<body>
<form class="" action="test.php" method="GET">
<input type="test" name="username" value=""><br/>
<input type="password" name="password" value=""><br/>
<input type="submit" name="sub" value="Login">
</form>
</body>
</html>
まず、テストコードを見てみましょう。最初にわかるのは、パラメータがフィルタリングされておらず、インジェクションが確実に発生することですが、今日はユニバーサルパスワードの問題について説明しています。
ユニバーサルパスワードについて知っておく必要があります。一般的なユニバーサルパスワードには次のものがあります。
1:"or "a"="a
2: ')or('a'='a
3:or 1=1--
4:'or 1=1--
5:a'or' 1=1--
6:"or 1=1--
7:'or'a'='a
8:"or"="a'='a
9:'or''='
10:'or'='or'
11:1 or '1'='1'=1
12:1 or '1'='1' or 1=1
13: 'OR 1=1%00
テストコードから、ログインを確認するときに、開発者がクエリ条件としてユーザー入力のみを使用し、mysql_num_row関数が返されたデータの数を判断することがわかります。返された数が1より大きい場合ユーザーがデータベースに存在し、ログインが許可されていることを証明します。通常の状況で実行されるSQLステートメントは次のとおりです。
[AppleScript] プレーンテキストビューの コピーコード
1 |
|
ただし、['または' 1 '=' 1]を送信すると、プログラムは特殊文字をフィルタリングしないため、ユーザー入力が直接クエリに取り込まれ、最後に実行されたSQLステートメントは次のようになります。
[AppleScript] プレーンテキストビューの コピーコード
1 |
|
このように、SQLによって返される値は常に1であり、プログラムの検証を正常にバイパスします。
修理について:
[PHP] プレーンテキストビューの コピーコード
1 2 3 4 5 6 7 8 9 |
|
上記のコードは、返されたデータをユーザー入力データと比較および検証し、ユニバーサルパスワードの生成を回避します。
パラメータ化されたクエリがSQLインジェクションを解決する方法
まず、SQLインジェクションを使用したコードを見てください。
[PHP] プレーンテキストビューの コピーコード
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
参数没有进行任何过滤 而且查询后没有对数据进行验证,看到这我们应该想到万能密码(http://bbs.ichunqiu.com/thread-10851-1-1.html)
正常的查询如图
我们用万能密码尝试登录,我们可以看到红线之后的所有数据已经被注释掉,只有之前的语句被mysql所执行,同时后边的and 1=1返回永远为1,从而造成万能密码
下面看一下我们修改为预置参数查询后的代码:
[PHP] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
我们正常登录
我们看一下执行的语句(mysql日志)
我们再试一下万能密码 发现登录并没有成功
我们再看一下执行的语句
我们将将16进制转换一下
--------
我们将参数化查询与普通的查询对比一下
可以看到 参数化查询在真正执行之前会进行数据库预编译 放在缓存里面,当运行时动态地参数传递,即使参数里有敏感字符 数据库也会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令。