【问题标题】:Inserting a table name into a query gives sqlite3.OperationalError: near "?": syntax error在查询中插入表名会给出 sqlite3.OperationalError: near "?": syntax error
【发布时间】:2014-10-12 19:07:27
【问题描述】:

我想动态选择要在 SQL 查询中使用的表,但我一直收到错误,但我正在尝试格式化它。还尝试了%s 而不是?

有什么建议吗?

group_food = (group, food)
group_food_new = (group, food, 1)

with con:

    cur = con.cursor() 
    tmp = cur.execute("SELECT COUNT(Name) FROM (?) WHERE Name=?", group_food)

    if tmp == 0:
        cur.execute("INSERT INTO ? VALUES(?, ?)", group_food_new)
    else: 
        times_before = cur.execute("SELECT Times FROM ? WHERE Name=?", group_food)
        group_food_update = (group, (times_before +1), food)

        cur.execute("UPDATE ? SET Times=? WHERE Name=?", group_food_update)

【问题讨论】:

    标签: python python-2.7 sqlite


    【解决方案1】:

    您不能使用 SQL 参数作为 SQL 对象中的占位符;使用 SQL 参数的原因之一是转义值,以便数据库永远不会将内容误认为数据库对象。

    您必须单独插入数据库对象;通过将任何 " 双引号参数加倍并使用

    来转义您的标识符
    cur.execute('SELECT COUNT(Name) FROM "{}" WHERE Name=?'.format(group.replace('"', '""')), (food,))
    

    cur.execute('INSERT INTO "{}" VALUES(?, ?)'.format(group.replace('"', '""')), (food, 1))
    

    cur.execute('UPDATE "{}" SET Times=? WHERE Name=?'.format(group.replace('"', '""')),
                (times_before + 1, food))
    

    ".." 双引号用于正确标记标识符,即使该标识符也是有效的关键字;名称中任何现有的" 字符必须加倍;这也有助于化解 SQL 注入尝试。

    但是,如果您的对象名称是用户来源的,则您必须对对象名称进行自己的(严格)验证,以防止此处的 SQL 注入攻击。在这种情况下,请始终根据现有对象验证它们。

    你真的应该考虑使用像SQLAlchemy 这样的项目来生成你的SQL;它可以负责验证对象名称并严格保护您免受 SQL 注入风险。它可以load your table definitions up front,所以它会知道什么名字是合法的:

    from sqlalchemy import create_engine, func, select, MetaData
    
    engine = create_engine('sqlite:////path/to/database')
    meta = MetaData()
    meta.reflect(bind=engine)
    conn = engine.connect()
    
    group_table = meta.tables[group]  # can only find existing tables
    count_statement = select([func.count(group_table.c.Name)], group_table.c.Name == food)
    count, = conn.execute(count_statement).fetchone()
    if count:
        # etc.
    

    【讨论】:

    • 啊!所以 '?'占位符仅用于 SQL - 不能用于 SQL 表名等?
    • @colminator:完全正确。否则会有一个巨大的安全漏洞,有人会添加'; DROP TABLE VALUES -- 或其他类似的恶作剧。这是使用 SQL 参数的主要原因之一(始终使用 SQL 参数,这样做很安全!)。
    • values 的角度理解,但我指的是表和列名。我一直想以一种通用的方式将 SQL 参数化的便利性扩展到表名和列。举个例子,当比较两个具有不同模式的数据库(即列不匹配)时,我希望以编程方式确定导入和导出列。虽然列名中不应该有任何特殊字符,但最好安全地转义它们以消除任何潜在的 SQL 错误。
    • @colminator:不,对象名称确实无法使用参数。使用 SQLAlchemy 或 PsycoPG sql module 之类的 SQL 生成器来完成此类任务。
    • @HelloGoodbye:因为参数的用例是转义数据,并且没有简单的方法可以检测到某个东西是对象名称。例如,您无法从上下文中确定是否将某些内容用作数据或对象名称。例如,WHERE colname = ? 可以同时接受对象名称和数据。
    【解决方案2】:

    团体和食物的价值是什么?指导方针说要提出这样的问题,其他人可以从中受益,在这种情况下,我们需要群体和食物的价值观。

    您似乎使用 Python 字符串格式化程序而不是 SQL 参数作为表名,尽管 https://docs.python.org/3/library/sqlite3.html#module-sqlite3 说在不安全的情况下使用字符串格式化程序。

    # Never do this -- insecure!
    symbol = 'RHAT'
    c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
    
    # Do this instead
    t = ('RHAT',)
    c.execute('SELECT * FROM stocks WHERE symbol=?', t)
    

    使用%s 作为占位符,然后将% 与Python2 中的变量一起放在字符串之外,在Python3 中已被替换为.format() 函数并将变量作为参数。

    【讨论】:

      【解决方案3】:

      我发现将 SQL 查询构建为文本字符串,然后将此字符串传递给 c.execute() 函数。

      querySelect = "SELECT * FROM " + str(your_table_variable)
      queryWhere = " WHERE " + str(variableName) + " = " str(variableValue)
      query = querySelect + queryWhere
      c.execute(query)
      

      我不知道它周围的安全情况(重新注入),我确信可能有更好的方法来做到这一点。

      【讨论】:

      • 不要这样做。就是注入的定义。
      • 尝试在该代码之前添加your_table_variable = your_table_variable + '; DELETE FROM ' + your_table_variable。 (不,说真的,不要。)
      猜你喜欢
      • 1970-01-01
      • 2019-12-20
      • 1970-01-01
      • 1970-01-01
      • 2017-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多