生成SQL“IN”子句:如何安全地处理输入+空值列表?

时间:2010-01-13 08:07:05

标签: python sql mysql sql-injection

在我的Python代码中,我经常发现自己正在执行以下操作(使用DB-API):

yValues = pickInterestingValuesOfY()

sql = "..." # includes a clause stating that "y must be in yValues"

c.execute(sql, yValues)

最后,正在执行的SQL可能就像

一样简单
SELECT x FROM table1 WHERE y IN (1,2,3);

问题是y(1,2,3)的可能值集在运行时确定。

我有两个问题:

  1. 在yValues为空的情况下生成有效的SQL('WHERE y IN()'为无效的SQL)
  2. 如果值来自不受信任的来源,请注意sql注入
  3. 要解决(2)我必须让DB-API实际将yValues插入到SQL语句中。所以我最终得到了以下复杂的解决方案:

    def inClause(columnName, values):
    
        if len(values):
             placeHolders = ','.join( ['%s'] * len(values) )
             sql = "%s IN (%s)" % (columnName, placeHolders)
        else: 
             sql = "FALSE"
        return "(%s)" % sql
    
    # get a db-api cursor called c
    c.execute("SELECT x FROM table1 WHERE %s;" % inClause('y', yValues), yValues)
    

    似乎正确地解决了上述两个问题。但是,我无法相信这个笨重的解决方案是需要的。

    您如何处理此类查询?我错过了一种更优雅的方式吗?

    我不是在寻找ORM。

    (我正在使用MySQL,所以如果有一些神奇的mysql非标准开关默认接受'WHERE y IN()'为有效,请让我知道并关注(1)将被处理。)

4 个答案:

答案 0 :(得分:5)

对于in子句,只需要包含一个不在列表中的值(例如负整数)。

答案 1 :(得分:3)

不。没有很好的方法可以做到这一点。 DB-API没有以这种方式处理序列的任何规范。

答案 2 :(得分:0)

对于MySQL,你可以这样做:

SELECT x FROM table1 WHERE y IN (SELECT NULL FROM DUAL WHERE 0);

DUAL是为此目的而诞生的虚拟表。它适用于选择不需要表格的数据。并且WHERE 0子句确保子查询不返回任何记录。

简而言之,SELECT NULL FROM DUAL WHERE 0是空列表的完美替代。

答案 3 :(得分:-2)

不要生成自己的SQL。

使用SQLAlchemy。它为你做到了这一点。