【问题标题】:When the column is dynamic, how to avoid SQL Injection in UPDATE statement SET %s=%s [duplicate]当列是动态的时,如何避免 UPDATE 语句中的 SQL 注入 SET %s=%s [重复]
【发布时间】:2019-09-23 23:19:24
【问题描述】:

我尝试在 pymysql 中使用 UPDATE 语句来更新某些列的值,其中self.keyself.valueself.id 是三个变量。

cur.execute("UPDATE environment_history SET {key}=%s WHERE id=%s".format(key=self.key),
                    (self.value, self.id)
                    )

虽然在上面的代码中,这为 SQL 注入留下了机会。我们只需要发布一些这样的数据

{"id":"23151","key":"uuid='ac211';#","value":"abcde"}

这将更新所有行,这可能更危险。 我必须阻止这种情况发生。

我尝试了一些不切实际的解决方案,其中之一是:

cur.execute("UPDATE environment_history SET %s=%s WHERE id=%s",
                    (self.key,self.value, self.id)
                    )

但是,pymysql 会将列名转义为\'uuid\'

这是一个 SQL 语法错误,因为 SET col_name=3 和 SET `col_name`=3 是正确的。

那么,如何避免这种SQL注入呢?

尝试手动替换和转义 self.key 是我能想到的唯一选择,但这听起来不是一种优雅的方式。

我的数据库是 MySQL。

有什么想法吗?谢谢!

【问题讨论】:

  • 列名不能作为准备好的语句中的参数。即使上述方法可以工作,你也不应该这样做。相反,请找到一种方法,以便您可以对列名和表名进行硬编码。
  • 您是否直接从输入中获得self.key?这本身就很危险。为什么不按特定关键字过滤呢?
  • 我投票关闭作为“如何动态准备 SQL 查询(也包括列名)避免 SQL 注入”的重复项,即使该问题使用 PHP 作为示例代码。但是任何语言的原理都是一样的。将您的 self.key 限制为已知列名值的白名单,然后使用纯字符串插值将其插入 SQL 字符串,而不是使用引用值的 cur.execute() 魔法。

标签: python mysql sql-injection pymysql


【解决方案1】:

根据我对此类动态查询的经验,我建议您将表/键定义保留在某处并检查它们是否有效。

基本想法是为表保留有效键的字典,例如:

valid_keys = {
   'table1': ['key1', 'key2', 'key3'],
   'table2': ['key1', 'key2'],
   ...
}

并做一个简单的检查,例如:

keys = valid_keys[table] if table in valid_keys.keys() else None
if keys != None and key in keys:
   # do your query stuff

您可以将该检查放入方法/函数中,并使其更加简单,而无需重复自己:

# checks table and key validity
def query_data_valid(table, key):
   keys = valid_keys[table] if table in valid_keys.keys() else None
   if keys != None and key in keys:
      return True

   return False

...
# block in your query calling method
if query_data_valid(table, key):
   # do your query stuff

【讨论】:

    【解决方案2】:

    我建议将self.key 过滤到已知值,然后才将其用作列。例如:

    keywords = ["uuid", "other", "keywords"]
    if self.key in keywords:
        column_name = self.key
    else:
        column_name = keywords[0]
    

    【讨论】:

      【解决方案3】:

      这并不能回答您的问题,而是一般规则。正如post 中所述,您需要使用参数化查询。

      参数化查询是一种查询,其中占位符用于参数和在执行时提供的参数值。这意味着参数化查询只编译一次。

      不正确(存在安全问题)

      c.execute("SELECT * FROM foo WHERE bar = %s AND baz = %s" % (param1, param2))
      

      正确(带有转义)

      c.execute("SELECT * FROM foo WHERE bar = {0} AND baz = {1}".format(param1, param2))
      

      【讨论】:

      • 这不是在回答问题。发帖人想使用一个参数作为列名。
      猜你喜欢
      • 2019-10-28
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 2018-03-02
      • 2013-04-19
      • 2011-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多