准备好的语句如何防止SQL注入攻击?
准备好的语句如何帮助我们防止SQL注入攻击?
维基百科说:
已准备好的语句对SQL注入具有适应性,因为稍后使用不同协议传输的参数值无需正确转义。 如果原始语句模板不是从外部输入派生的,则SQL注入不会发生。
我看不出原因。 简单的英语和一些例子会是一个简单的解释吗?
这个想法非常简单 - 查询和数据分别发送到数据库服务器。
就这样。
SQL注入问题的根源在于代码和数据的混合。
事实上,我们的SQL查询是一个合法的程序 。 我们正在动态创建一个这样的程序,通过动态添加一些数据。 因此,这些数据可能会干扰程序代码甚至修改它,因为每个SQL注入示例都会显示它(PHP / Mysql中的所有示例):
$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";
会产生一个定期查询
SELECT * FROM users where id=1
而这段代码
$spoiled_data = "1; DROP TABLE users;"
$query = "SELECT * FROM users where id=$spoiled_data";
会产生恶意序列
SELECT * FROM users where id=1; DROP TABLE users;
它的工作原理是因为我们直接将数据添加到程序体中,并且它成为程序的一部分,所以数据可能会改变程序,并且根据传递的数据,我们将定期输出或删除一个表users
。
尽管在准备好陈述的情况下,我们不会改变我们的计划,但它仍然保持完好
这才是重点。
我们首先发送一个程序到服务器
$db->prepare("SELECT * FROM users where id=?");
数据被称为参数或占位符的变量替代。
请注意,发送给服务器的查询完全相同,没有任何数据! 然后我们用第二个请求发送数据,基本上与查询本身分开:
$db->execute($data);
所以,它不能改变我们的计划并造成任何伤害。
很简单 - 不是吗?
但值得一提的是, 并非每次使用占位符时,都会将其作为预处理语句进行处理 。
占位符是将实际数据替换为变量以用于将来处理的一般想法printf()
例如,参见printf()
),而准备好的语句是其唯一的子集。
有些情况下(特别是PHP中的PDO可以这样做),当准备好的语句可以被模拟时,查询实际上与数据一起组成并且在一个请求中发送到服务器。 但重要的是要明白,这种方法同样安全 ,因为每一点数据都根据其类型正确格式化 ,因此无可能发生任何错误。
我必须添加的唯一内容总是在每个手册中省略:
准备好的语句只能保护数据,但不能保护程序本身 。
因此,例如,一旦我们必须添加动态标识符 (例如字段名称),准备好的语句就无法帮助我们。 我最近解释过这件事 ,所以我不会再重复一遍。
这是用于设置示例的SQL:
CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);
INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);
Inject类容易受到SQL注入的影响。 查询动态地与用户输入一起粘贴。 查询的目的是显示有关Bob的信息。 基于用户输入的工资或奖金。 但恶意用户通过在where子句中加入相当于'或true'的操作来破坏查询,以便返回所有内容,包括应该隐藏的关于Aaron的信息。
import java.sql.*;
public class Inject {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
System.out.println(sql);
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
运行这个,第一种情况是正常使用,第二种情况是恶意注入:
c:temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50
c:temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0
您不应该使用用户输入的字符串连接构建您的SQL语句。 它不仅容易受到注入的影响,而且它对服务器也有缓存影响(语句更改,因此获取SQL语句缓存命中的可能性较小,而绑定示例始终运行相同的语句)。
这里是一个绑定的例子来避免这种注入:
import java.sql.*;
public class Bind {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
Connection conn = DriverManager.getConnection(url);
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
System.out.println(sql);
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, args[0]);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
使用与前面示例相同的输入运行此代码显示恶意代码不起作用,因为没有与该字符串匹配的paymentType:
c:temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50
c:temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
基本上,通过准备好的语句,来自潜在黑客的数据被视为数据 - 并且无法将它与应用程序SQL混合和/或解释为SQL(当数据传入时直接放入您的数据应用程序SQL)。
这是因为准备好的语句首先“准备”SQL查询以找到一个有效的查询计划,并发送稍后从表单中派生出来的实际值 - 此时实际执行查询。
这里有更多的信息:
编写语句和SQL注入
链接地址: http://www.djcxy.com/p/16767.html上一篇: How can prepared statements protect from SQL injection attacks?
下一篇: Dynamic PIVOT Table