我一直在阅读关于MySQL Injection的内容,因为我刚开始在我的网站上添加预备语句。我想更多地了解它实际上是如何保护的,在阅读之后,我认为我没有正确地完成它。
我在这里阅读:http://www.tizag.com/mysqlTutorial/mysql-php-sql-injection.php
我真的不明白它是如何“注入”的,或者准备好的陈述如何解决它。特别是这一点:
Normal: SELECT * FROM customers WHERE username = 'timmy'
Injection: SELECT * FROM customers WHERE username = '' OR 1''
使用SELECT时,我用于WHERE的东西只是ID或用户名。他们无法更改自己的ID,用户在注册时会验证用户名:
function protect($string) {
global $con;
return mysqli_real_escape_string($con,strip_tags(addslashes($string)));
}
这就是“protect()”阻止MySQL注入吗?
如果是这样,准备好的陈述有什么用途?这种保护是否正确?
而不是
$car = 'nissan';
$update_car = mysqli_query($con,"UPDATE stats SET stats.car = $car WHERE stats.id = 4");
我说:
$car = 'nissan';
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
如果这是正确的,我不明白除了添加另一行代码之外该怎么做?
修改
首先准备好的声明,有什么好处吗?
$getusername = mysqli_prepare($con, "SELECT users WHERE users.username = '",mysqli_real_escape_string($username),"'");
答案 0 :(得分:1)
另请注意,mysqli_real_escape_string是为了防止使用字符串注入。如果你有一个(例如)整数字段,那么转义传递的变量来处理引用并不能真正起作用,因为注入不需要引用。
例如: -
$some_field_expected_to_be_int = '1 OR 1=1';
$query = "SELECT * FROM users WHERE id = ".mysqli_real_escape_string($con, $some_field_expected_to_be_int);
仍会提供一段SQL阅读: -
SELECT * FROM users WHERE id = 1 OR 1=1
并返回所有内容。
在这种情况下,你应该确保它是一个整数: -
$some_field_expected_to_be_int = '1 OR 1=1';
$query = "SELECT * FROM users WHERE id = ".(int)$some_field_expected_to_be_int;
个人准备好的陈述和逃避字段都有自己的位置。准备好的语句使得忘记逃避项目变得更加困难,并且对于许多数据库而言,它们具有性能优势,但是mysqli占位符是不可读的,因此调试语句是不可行的,并且当用于高度动态的sql时它变成了一场噩梦。但逃避会给你带来风险,你会忘记某些地方而且代码看起来很混乱。
编辑 - 在准备好的陈述上添加一点。
我们使用了几个内部数据库类,它们与mysql / mysqli / pdo / sql server分开了一些东西,但是对于使用prepare和绑定参数的基本示例: -
<?php
if($stmt = $this->db->prepare("SELECT users WHERE users.username = ? "))
{
$stmt->bind_param('s', $username);
$stmt->execute();
}
?>
在这种情况下,SQL不是使用变量构建的(可以被操作),而是将变量作为参数传递。
答案 1 :(得分:1)
首先,让我们来看看你逃跑的尝试:
function protect($string) {
global $con;
return mysqli_real_escape_string($con,strip_tags(addslashes($string)));
}
这里有两个错误:首先,addslashes
正在执行与mysqli_real_escape_string
相同的工作(但做得很糟),所以删除它。
其次,您应该始终清理以查找您正在使用的上下文。因此,在生成SQL时,您需要清理SQL;生成HTML时,清理HTML。因此将strip_tags
放在这里是没有意义的,因为它与HTML有关,但这显然是一个SQL转义函数。当您在模板中准备输出时,请单独执行此操作。
简而言之,只有mysqli_real_escape_string
本身就会比你在这里更好。
接下来,让我们看看参数化参数:
$car = 'nissan';
$query = "UPDATE stats SET stats.car = '$car' WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
此语句未准备或参数化 - 就数据库而言,它仍然只是一串SQL。如果$car
实际上是根据用户输入设置的(例如$car = $_GET['car'];
),那么您可以在查询中间插入任何SQL:
$car = "nissan'; DROP TABLE stats; --";
$query = "UPDATE stats SET stats.car = '$car' WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
// MySQL will now drop your table; oops!
// UPDATE stats SET stats.car = 'nissan'; DROP TABLE stats; --' WHERE stats.id = 4
添加$car = mysqli_real_escape_string($car);
将正确地转义输入中的'
,停止它结束字符串并启动新的SQL语句。
参数化查询以不同的方式避免问题:它们告诉数据库查询的哪些部分应该是SQL,哪些只是用户提供的数据。它看起来像这样:
$car = "nissan'; DROP TABLE stats; --";
$query_with_placeholder = "UPDATE stats SET stats.car = ? WHERE stats.id = 4";
// Note the ?, without quotes, represents somewhere for data to be put by MySQL
$prepared_statement = mysqli_prepare($con, $query_with_placeholder);
// Now we tell MySQL what to put in the placeholder
mysqli_stmt_bind_param($prepared_statement, 's', $car);
// And execute it
mysqli_stmt_execute($prepared_statement);
因为$car
实际上从未插入到SQL中,只是作为一个单独的参数传递,所以没有办法注入任何令人讨厌的东西,而且我们不需要进行任何额外的转义。
答案 2 :(得分:0)
转义数据并使用准备好的查询都可以防止SQL注入。但是逃避更容易出错,因为很容易忘记在你需要它的所有地方调用它。如果使用预准备语句,则会自动进行保护。
之间没有显着差异
$update_car = mysqli_query($con,"UPDATE stats SET stats.car = $car WHERE stats.id = 4");
和
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
$update_car = mysqli_query($con,$query);
这只是一种风格选择。当您要插入调试语句时,第二个版本非常有用,例如
$query = "UPDATE stats SET stats.car = $car WHERE stats.id = 4";
echo $query;
$update_car = mysqli_query($con,$query);
答案 3 :(得分:0)
流行的做法是,只要有可能的用户输入变量,就可以使用查询清理输入和使用预准备语句。
卫生还应该防止用户溢出变量并将大量数据上传到您的应用程序。
准备语句是您对安全性的第二次衡量,它可以防止您以后可能犯的错误,例如忘记转义输入或者在您下次重构应用程序时没有意识到实际用户输入的内容。
如果您没有正确防范注入,您可以打开项目,删除数据,修改数据或将恶意代码上传到您的应用程序。 (你真的希望有人在文本字段中上传javascript吗?)