3个涉及Python和SQLite的问题

发布于 2024-10-01 07:39:48 字数 1002 浏览 1 评论 0原文

我正在创建与游戏服务器一起使用的函数。该服务器使用插件。我有这些使用 SQLite 数据库的函数,以及 apsw 来检索另一个函数存储的项目。我对此有3个问题。

问题一:我不断收到错误“SQLError:near “?”:语法错误” 由于我的语句具有多个 ?,因此很难找出到底是什么错误。那么有什么问题吗?

问题二:我知道 SQL 注入,但这些函数只接受脚本运行者的输入,而他唯一会破坏的东西就是他自己的东西。即便如此,有没有一种简单的方法可以证明 SQL 注入?

问题三:有什么办法可以让这个功能更加高效吗?

现在看起来是这样的:

def readdb(self,entry,column,returncolumn = "id,matbefore,matafter,name,date"):
    self.memwrite
    if isinstance(entry, int) or isinstance(entry, str):
        statement = 'SELECT {0} FROM main WHERE {1} IN {2}'.format(returncolumn,column,entry)
        self.memcursor.execute(statement)
        blockinfo = self.memcursor.fetchall()
        return(blockinfo)
    if isinstance(entry, tuple) or isinstance(entry, list):
        statement = '''SELECT {0} FROM main WHERE {1} IN (%s)'''.format(returncolumn,column)
        self.memcursor.execute(statement % ("?," * len(entry))[:-1], entry)
        blockinfo = self.memcursor.fetchall()
        return(blockinfo

I'm creating functions for use with a game server. This server uses plugins. I have these functions which use an SQLite database, along with apsw to retrieve items stored by another function. I have 3 questions on this.

Question One: I keep getting the error "SQLError: near "?": syntax error" Since my statement features multiple ?, it's proving hard to track down what is exactly wrong. So what is wrong?

Question Two: I know about SQL-Injection, but these functions only take input from the runner of the script, and the only stuff he would be damaging is his own. Even so, is there an easy way to make this SQL-injection proof?

Question Three: Is there any way to make this function more efficient?

Here's what it looks like now:

def readdb(self,entry,column,returncolumn = "id,matbefore,matafter,name,date"):
    self.memwrite
    if isinstance(entry, int) or isinstance(entry, str):
        statement = 'SELECT {0} FROM main WHERE {1} IN {2}'.format(returncolumn,column,entry)
        self.memcursor.execute(statement)
        blockinfo = self.memcursor.fetchall()
        return(blockinfo)
    if isinstance(entry, tuple) or isinstance(entry, list):
        statement = '''SELECT {0} FROM main WHERE {1} IN (%s)'''.format(returncolumn,column)
        self.memcursor.execute(statement % ("?," * len(entry))[:-1], entry)
        blockinfo = self.memcursor.fetchall()
        return(blockinfo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

淡写薰衣草的香 2024-10-08 07:39:48

这很有趣(请继续阅读以了解原因)。

您的第一个语句实际上使用了 sqlite3 模块的值绑定机制(我假设这就是您使用的)。因此,*(默认列)被转义,使得语句无效。这是 SQL 注入证明,您自己的代码会尝试注入 SQL(现在看到有趣的事情了吗?)。

第二次使用 Python 字符串替换来构建查询字符串,这不是 SQL 注入证明。

This is funny (read on to learn why).

The first statement you have actually uses the value binding mechanism of the sqlite3-module (I assume that is what you use). Hence, the * (which is the default column) gets escaped, making the statement invalid. This is SQL-injection proof, and your own code tries to inject SQL (see the funny now?).

The second time you use Pythons string replacement in order to build the query string, which is not SQL-injection proof.

几味少女 2024-10-08 07:39:48

使用名称来获取更多信息性错误消息。例如,我故意省略了一个逗号:

 cur.execute("select ? ?,?", (1,2,3))
 SQLError: near "?": syntax error

现在使用名称:

 cur.execute("select :1 :2,:3", (1,2,3))
 SQLError: near ":2": syntax error

如果您有很多绑定,我建议您切换到命名绑定样式并传递绑定本身的字典。

cur.execute("select :artist, :painting, :date", 
  {"artist": "Monster", "painting": "The Duck", "date": "10/10/2010" })

您只能对值使用绑定,而不能对列名或表名使用绑定。有几种可能的方法。尽管 SQLite 支持任意列/表名称,但您可以要求它们仅为 ASCII 字母数字文本。如果您想减少限制,则需要引用名称。在包含双引号的名称周围使用方括号,在包含方括号的名称周围使用双引号。一个名字不能兼而有之。

所有这些的替代方法是使用授权者机制。有关 API,请参阅 Connection.setauthorizer和一个指向示例的指针。简而言之,您的回调将被调用,并执行将要执行的操作,因此您可以拒绝任何写入数据库的操作。

在效率方面,您可以根据调用者如何使用结果来改进事情。游标非常便宜。没有必要尝试一遍又一遍地重复使用同一个,否则可能会导致细微的错误。 SQLite 仅在您请求时获取结果的下一行。通过使用 fetchall,您坚持构建所有结果的列表。如果列表可能很大或者您可能中途停止,则只需返回 db.cursor().execute("... query ...")。然后调用者应该使用你的函数进行迭代:

for id,matbefore,matafter,name,date in readdb(...):
    ... do something ...

在你的地方,我会完全废弃这个 readdb 函数,因为它不会添加任何值并直接编写查询:

for id,foo,bar in db.cursor().execute("select id,foo,bar from .... where ...."):
    ... do something ...

你的编码风格表明你对 Python 相当陌生。我强烈建议查找迭代器和生成器。这是一种更简单的编码风格,可以根据需要生成和使用结果。

顺便说一句,此 SQL 创建一个名称长度为零的表,以及名为双引号和分号的列。 SQLite 功能很好,但不要这样做:-) 然而它对于测试很有用。

create table "" (["], ";");

披露:我是 APSW 作者

Use names to get more informative error messages. For example I deliberately left out a comma with this:

 cur.execute("select ? ?,?", (1,2,3))
 SQLError: near "?": syntax error

Now with names:

 cur.execute("select :1 :2,:3", (1,2,3))
 SQLError: near ":2": syntax error

If you have a lot of bindings I'd recommend you switch to the named bindings style and pass a dictionary for the bindings themselves.

cur.execute("select :artist, :painting, :date", 
  {"artist": "Monster", "painting": "The Duck", "date": "10/10/2010" })

You can only use bindings for values, not for column or table names. There are several possible approaches. Although SQLite supports arbitrary column/table names, you can require that they are only ASCII alphanumeric text. If you want to be less restrictive then you need to quote the names. Use square brackets around a name that has double quotes in it and double quotes around a name that has square brackets in it. A name can't have both.

The alternative to all that is using the authorizer mechanism. See Connection.setauthorizer for the API and a pointer to an example. In brief your callback is called with the actions that will be taken so for example you can reject anything that would write to the database.

In terms of efficiency, you can improve things depending on how the caller uses the results. Cursors are very cheap. There is no need to try to reuse the same one over and over again and doing so can lead to subtle errors. SQLite only gets the next row of a result when you ask for it. By using fetchall you insist of building a list of all the results. If the list could be large or if you may stop part way through then just return db.cursor().execute("... query ..."). The caller should then use your function to iterate:

for id,matbefore,matafter,name,date in readdb(...):
    ... do something ...

In your place I would just junk this readdb function completely as it doesn't add any value and write queries directly:

for id,foo,bar in db.cursor().execute("select id,foo,bar from .... where ...."):
    ... do something ...

Your coding style indicates that you are fairly new to Python. I strongly recommend looking up iterators and generators. It is a far simpler coding style producing and consuming results as they are needed.

BTW this SQL creates a table with a zero length name, and columns named double quote and semicolon. SQLite functions just fine, but don't do this :-) It is however useful for testing.

create table "" (["], ";");

Disclosure: I am the APSW author

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文