sql注入 - 如何清理程序生成的sql子句?

时间:2011-09-03 06:49:55

标签: javascript sql security sql-injection

在标准Ajax中,whereorder by SQL子句由程序(非用户)提供,例如

var url = ".select?dd=emp&where="+escape("emp_tp='abc' and hire_dt<current_date-'2 years' and super_emp_id is distinct from emp_id")

在服务器上回答

$where = (isset($_GET['where'])) ? pureClause($_GET['where']) : null;
$order = (isset($_GET['order'])) ? pureClause($_GET['order']) : null;
...
$query = $query.(($where)?" where $where":'').(($order)?" order by $order":'');

问题是函数pureClause应该是什么样的?

如果存在以下任何一种情况,

现在pureClause只会引发错误:

; select insert update delete drop create truncate

如果其他注入导致查询失败,那很好,只要数据完好无损

对我来说,这似乎已经足够了,但在我心里,我知道我错了。

澄清:

  • 在Postgres中准备好的语句,虽然速度很快,但设置和维护都很痛苦 - 它们对于使用得很好的查询很好,但不适用于自定义查询。
  • 为每个事务创建一个准备好的语句是一个巨大的数据库命中。如果可以在应用程序级别获得安全性,则更受欢迎。

最后,考虑where子句

emp_tp='abc' and hire_dt=current_dt-'2 years' and super_emp_id is distinct from emp_id

这里有多少占位符?这需要在被放入带有占位符的预准备语句之前正确解析,对吧?还是我完全错过了船?


主要事实:

  • 实际为参数化预准备语句编写SQL子句解析器
  • 实际编写一个保证无害的SQL子句清理程序

解决方案:

对于SELECTS,随机SQL可能是一个问题:因为保护数据库太难了,让数据库保护自己!让不同的用户拥有不同的角色/权限。使用只读用户进行选择。对于普通的SQL,这可以保证这些语句不会出现DML。

最佳做法:四个数据库用户访问

  1. developer,尽一切努力(永远不会在网络应用中用作连接)
  2. dml - 可以在几乎所有内容上选择/ dml(必须用于dml)
  3. read - 可以选择(用于所有选择,无论是准备还是文本)
  4. login - 只能执行登录/密码功能(在登录过程中使用)
  5. 密码保护:

    • dmlread可能无法通过select或dml访问密码数据
    • login应仅通过受保护的功能访问 密码数据,例如
         function login( username, password ) - returns user_id
         function set_password( usr_id, password ) - sets password
    
    • 只有login可以运行login()set_password()函数
    • 取决于您的数据库,login 可能需要sql访问密码列
    • 根据您的数据库,password列可能会受到保护;如果没有,那么应该从user表移出到自己的安全表

    使用管理员工具在mysql中进行此设置大约需要30分钟,包括编写登录功能和拆分密码列的时间。

4 个答案:

答案 0 :(得分:5)

您正在做的是sql注入的定义,无法清理。您无法以安全的方式传递WHERE条款期末故事。您必须在服务器端构建此部分查询。您没有意识到这一点的事实意味着您必须阅读有关sql注入的更多信息,显然要求StackOverflow是解决此问题的不安全方法。担心的是,您可能永远无法了解此漏洞的基本原理。

$order可以通过白名单以安全的方式完成。例如:

if(in_array($_GET['order'],$list_of_rows)){
   $order=$_GET['order'];
}

如果您传入的是表名或列名,请确保根据白名单进行检查,否则这将是sql注入。

答案 1 :(得分:1)

始终使用准备好的陈述。它将处理输入转义并避免sql注入。不需要pureClause这样的黑客攻击。 查看mysqli_stmt::prepare()

答案 2 :(得分:1)

正如@Stephen建议的那样,提供WHERE作为对象,然后解析对象并生成安全的SQL     var where = {        emp_tp:{          条件:平等          价值:'abc'     }     var order = {        emp_tp:'ASC'     }

将其发送为json:

var params = {
  w: where,
  o: order
}
$.post(url,params,function(result){...}, 'json');

在PHP中

$where = isset($_POST['w']) ? json_decode($_POST['w') : array();
if (!empty($where)) {
  foreach ($where as $field => $data) {
     // validate that field exists
     // validate that operator is valid
     $sql .= sprintf('%s %s "%s"', $field, $data->operator, mysql_escape_string($data->value));
  }
}

答案 3 :(得分:1)

知道了!通过仅被授予数据库SELECT权限的数据库用户(连接)来路由所有这些查询!

尝试DML会窒息。这不会阻止DoS攻击(有很多方法可以做到这一点!),但确实可以保护数据。安全查询也不会像登录那样。但对于客户端生成的WHERE和ORDER,以防止DML为目标,这应该可以正常工作。

十年/十五年前总是为不同的角色设置不同的用户,但是使用app layer等等已经摆脱了习惯。重新投资这些原则可能是一个好主意。

除非听到不同的意思,否则会将此标记为正确的答案 - 它符合所有标准,但它如何避免了写一个消毒剂在理论上不可能的挑战。