Skip to content

除了参数化查询,还有哪些有效防止SQL注入的手段

  1. 输入验证和过滤

    • 白名单过滤:只允许已知安全和预期的输入,拒绝所有其他输入。例如,如果某个字段只接受数字,就只允许数字输入。
    • 数据类型检查:确保用户输入的数据类型与预期的数据库字段类型匹配。
    • 长度校验:限制输入字符串的长度,防止过长的恶意输入。
    • 正则表达式:使用正则表达式来验证输入的格式,例如邮箱、电话号码等。
    • 转义特殊字符:对可能被解释为SQL命令的特殊字符(如单引号、双引号、反斜杠等)进行转义处理,使其成为普通字符串的一部分,而不是SQL代码。
  2. 最小权限原则

    • 数据库用户应该只拥有执行他们需要执行的操作所需的最小权限。例如,一个Web应用的用户不应该拥有DROP TABLE或ALTER TABLE等数据库管理权限。这样,即使攻击者成功注入恶意SQL代码,他们也只能访问和修改受限制的数据。
  3. 使用存储过程(Stored Procedures)

    • 在数据库中预编译存储过程,并确保存储过程内部使用参数化查询来处理输入。如果存储过程内部仍然通过拼接字符串来构建SQL语句,那么依然存在SQL注入的风险。
  4. 避免动态拼接SQL语句

    • 尽可能避免在应用程序代码中直接使用字符串拼接的方式构建SQL语句。如果必须动态构建SQL,务必对所有用户输入进行严格的验证和转义。
  5. 使用ORM框架

    • 对象关系映射(ORM)框架(如Hibernate、MyBatis等)通常内置了参数化查询的机制,可以有效防止SQL注入。然而,在使用ORM框架时,仍需注意其提供的动态SQL功能(如MyBatis的$符号),不当使用仍可能引入风险。
  6. 安全编程实践

    • 开发人员应遵守安全编程规范,不信任任何用户输入。
    • 定期对代码进行安全审计和漏洞扫描。
  7. Web应用防火墙(WAF)和入侵检测系统(IDS)

    • 在网络层面部署WAF和IDS可以帮助检测和阻止常见的Web攻击,包括SQL注入,即使应用程序代码存在漏洞。

MyBatis中#$的区别

在MyBatis中,#{}${}是用于在SQL映射文件中处理参数的两种不同方式,它们在防止SQL注入方面的表现截然不同:

  1. #{}(参数占位符)

    • 原理#{}会将传入的参数视为一个字符串,并在生成的SQL语句中自动为其添加引号。 它会使用JDBC的PreparedStatement(预编译语句)机制。
    • 安全性#{}能够很大程度上防止SQL注入。当MyBatis遇到#{}时,它会预编译SQL语句,将#{}替换为?占位符,然后将实际参数值作为独立的参数传递给数据库。数据库会将参数值视为数据,而不是可执行的SQL代码的一部分。
    • 示例:如果传入username= 'admin' OR '1'='1',使用WHERE username = #{username},最终执行的SQL可能是WHERE username = 'admin'' OR ''1''=''1',数据库会将其作为一个普通的字符串值进行匹配,而不是执行恶意的OR条件。
  2. ${}(字符串替换)

    • 原理${}会将传入的参数直接拼接到SQL语句中,进行简单的字符串替换,不会进行预编译或参数处理,也不会自动添加引号。
    • 安全性${}无法防止SQL注入。因为它直接将参数值原样嵌入SQL语句,如果参数值包含恶意SQL代码,这些代码将被数据库直接执行。
    • 用途:主要用于动态地替换SQL语句中的数据库对象,例如表名、列名、排序字段(ORDER BY ${columnName})等,因为这些部分不能作为预编译参数。
    • 示例:如果传入username= 'admin' OR '1'='1',使用WHERE username = ${username},最终执行的SQL可能是WHERE username = admin' OR '1'='1',这可能导致SQL注入攻击,绕过身份验证。

总结

  • 推荐使用#{}:在MyBatis中,应始终优先使用#{}来处理所有用户输入的参数,以确保SQL语句的安全性,防止SQL注入。
  • 谨慎使用${}:如果确实需要使用${}来动态替换表名、列名等数据库对象,必须在应用程序层面手动对这些参数进行严格的校验和过滤,以防止潜在的SQL注入风险。