PreparedStatement如何避免或阻止SQL注入?

Pra*_*u R 111 java sql sql-injection jdbc prepared-statement

我知道PreparedStatements避免/阻止SQL注入.它是如何做到的?使用PreparedStatements构造的最终表单查询是字符串还是其他?

Pau*_*lin 185

考虑两种做同样事情的方法:

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();
Run Code Online (Sandbox Code Playgroud)

要么

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();
Run Code Online (Sandbox Code Playgroud)

如果"user"来自用户输入并且用户输入是

Robert'); DROP TABLE students; --
Run Code Online (Sandbox Code Playgroud)

然后在第一个例子中,你会被软管.在第二个,你将是安全的,小Bobby表将在你的学校注册.

  • 小鲍比表.XD很棒的参考 (59认同)
  • 不,在第一个例子中,你会得到那个声明.在第二个中,它将插入"Robert'); DROP TABLE学生; - "插入用户表. (17认同)
  • 所以,如果我做对了,第二个例子中将要执行的查询实际上是:INSERT INTO学生值("罗伯特"); DROP TABLE学生; - ") - 或至少类似的东西.这是真的? (7认同)
  • +1引用Little Bobby Tables. (7认同)
  • 对不起,嵌套引号是我试图避免的,因为这样的混乱.这就是我喜欢带参数的PreparedStatements的原因. (6认同)
  • 这就是我的意思,在第二个例子中("安全的"),字符串*Robert'); DROP TABLE学生; - *将保存到学生表中的字段中.我还写了别的东西吗?;) (2认同)

Jay*_*esh 106

要了解PreparedStatement如何阻止SQL注入,我们需要了解SQL Query执行的各个阶段.

1.编译阶段.2.执行阶段.

每当SQL Server引擎收到一个查询时,它必须通过以下阶段,

查询执行阶段

  1. 解析和规范化阶段: 在此阶段,检查Query的语法和语义.它检查查询中使用的引用表和列是否存在.它还有许多其他任务要做,但我们不详细说明.

  2. 编译阶段: 在此阶段,查询中使用的关键字如select,from,where等将转换为可由机器理解的格式.这是解释查询并确定要采取的相应动作的阶段.它还有许多其他任务要做,但我们不详细说明.

  3. 查询优化计划: 在此阶段,创建决策树以查找可以执行查询的方式.它找出了执行查询的方式的数量以及与执行Query的每种方式相关的成本.它选择执行查询的最佳计划.

  4. 缓存: 在查询优化计划中选择的最佳计划存储在缓存中,以便每当下次同一查询进入时,它不必再次通过阶段1,阶段2和阶段3.当下次查询进入时,它将直接在Cache中检查并从那里拾取执行.

  5. 执行阶段: 在此阶段,执行提供的查询并将数据作为ResultSet对象返回给用户.

PreparedStatement API在上述步骤中的行为

  1. PreparedStatements不是完整的SQL查询,并且包含占位符,这些占位符在运行时由实际的用户提供的数据替换.

  2. 每当包含占位符的任何PreparedStatment传递到SQL Server引擎时,它都会通过以下阶段

    1. 解析和归一化阶段
    2. 编译阶段
    3. 查询优化计划
    4. 缓存(带有占位符的编译查询存储在缓存中.)

UPDATE用户设置用户名=?和密码=?WHERE id =?

  1. 以上查询将被解析,使用占位符编译作为特殊处理,优化并获得缓存.此阶段的查询已经编译并以机器可理解的格式转换.所以我们可以说存储在缓存中的Query是预编译的,只有占位符需要用用户提供的数据替换.

  2. 现在,在用户提供的数据进入运行时,从Cache中选取预编译查询,并使用用户提供的数据替换占位符.

PrepareStatementWorking

(请记住,在使用者数据替换占位符后,不会再次编译/解释最终查询,并且SQL Server引擎将用户数据视为纯数据,而不是需要再次解析或编译的SQL;这就是PreparedStatement的优点. )

如果查询不必再次进行编译阶段,则占位符上替换的任何数据都将被视为纯数据,对SQL Server引擎没有任何意义,它直接执行查询.

注意:它是解析阶段之后的编译阶段,它理解/解释查询结构并为其提供有意义的行为.对于PreparedStatement,查询只编译一次,并且一直拾取缓存的编译查询以替换用户数据并执行.

由于PreparedStatement的一次编译功能,它没有SQL注入攻击.

您可以通过示例获取详细说明:http: //javabypatel.blogspot.in/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html

  • 字面上最完整的答案如何工作 (3认同)
  • 很好的解释 (2认同)

tan*_*ens 67

SQL注入的问题是,用户输入用作SQL语句的一部分.通过使用预准备语句,您可以强制将用户输入作为参数的内容进行处理(而不是作为SQL命令的一部分).

但是,如果您不将用户输入用作预准备语句的参数,而是通过将字符串连接在一起来构建SQL命令,那么即使使用预准备语句,您仍然容易受到SQL注入的攻击.

  • 示例请 - 但是如果您不使用用户输入作为预准备语句的参数,而是通过将字符串连接在一起来构建SQL命令,那么即使使用预准备语句,您仍然容易受到SQL注入的攻击. (14认同)
  • FWIW Prepared语句不是JDBC的东西 - 它们是SQL的东西.您可以在SQL控制台中准备和执行预准备语句.PreparedStatement只是在JDBC中支持它们. (2认同)

Tra*_*man 26

PreparedStatement中使用的SQL是在驱动程序上预编译的.从那时起,参数作为文字值而不是SQL的可执行部分发送给驱动程序; 因此,不能使用参数注入SQL.PreparedStatements的另一个有益的副作用(预编译+仅发送参数)是在多次运行语句时提高性能,即使参数的值不同(假设驱动程序支持PreparedStatements),因为驱动程序不必执行SQL解析和编译参数改变的时间.

  • 实际上,SQL通常是在数据库上预编译的.也就是说,在数据库上准备执行计划.执行查询时,将使用这些参数执行计划.额外的好处是可以使用不同的参数执行相同的语句,而查询处理器不必每次都编译新的计划. (4认同)