4

我有一些通过 python mysqldb 到我的 mysql 数据库工作的 sql 查询,但我想让它们少一点 sql-injection 漏洞,所以小 bobby 表不会尝试添加数据..

例如:

原创:(
这行得通,所以 ListID 等绝对有效)

sql="SELECT NAME FROM ListsTable WHERE ID=%s"%(ListID)  
c.execute(sql)  

尝试使用元组:

sql="SELECT NAME FROM ListsTable WHERE ID=%s"  
c.execute(sql,(ListID,))  

作品:

sql="SELECT NAME FROM ListsTable WHERE ID=%s"  
c.execute(sql, ListID)  

我不知道为什么第二次尝试不能作为元组工作,而是将其作为单个参数接受,但是对于另一个语句,无论哪种方式,我都需要传递多个参数,所以这不起作用:

原来的:

sql="SELECT * FROM ListsTable ORDER BY ID DESC LIMIT %s,%s"%(Page, (Page*20)+20)  
c.execute(sql)

哪个有效,但是如果我尝试将参数作为元组发送,则再次无效:

sql="SELECT * FROM ListsTable ORDER BY ID DESC LIMIT %s,%s"  
var1=Page  
var2=(Page*20)+20  
params=(var1,var2)              
c.execute(sql,params)  

甚至只是

sql="SELECT * FROM ListsTable ORDER BY ID DESC LIMIT %s,%s"  
c.execute(sql,(Page,(Page*20)+20))  

我最近在我的网络服务器日志上遇到了这个错误,但请注意,由于我一直在尝试许多不同的事情,因为它之前没有出错,这可能是一个重头戏:(错误是指上述尝试传递“参数”变量)

File "process.py", line 98, in main  
c.execute(sql,params)  
File "/var/www/cgi-bin/MySQLdb/cursors.py", line 159, in execute  
query = query % db.literal(args)  
TypeError: not enough arguments for format string  

编辑:如果有帮助,我正在使用 mysqldb 版本 1.2.3,以防它不接受该版本的元组,但不要让我开始了解 mysqldb 文档有多垃圾。

4

4 回答 4

3

你的mysqldb版本一定是问题所在。

http://mysql-python.hg.sourceforge.net/hgweb/mysql-python/MySQLdb-2.0/file/5a7c30cd9de2/MySQLdb/cursors.py#l180

改为:

1.51 -        if args is not None:
1.52 -            query = query % self.connection.literal(args)
1.53          try:
1.54 +            if args is not None:
1.55 +                query = query % tuple(map(self.connection.literal, args))

http://mysql-python.hg.sourceforge.net/hgweb/mysql-python/MySQLdb-2.0/diff/98d968f5af11/MySQLdb/cursors.py

后来改为:

1.144 -                query = query % tuple(map(self.connection.literal, args))
1.145 +                query = query % tuple(( get_codec(a, self.encoders)(db, a) for a in args ))

http://mysql-python.hg.sourceforge.net/hgweb/mysql-python/MySQLdb-2.0/diff/d9bb912776a5/MySQLdb/cursors.py#l1.144

于 2011-04-27T04:02:55.847 回答
1

在 MySQLdb 1.2.3 中,查询来自字符串替换,使用第一个参数作为模型字符串,第二个参数作为参数列表或单个参数,通过 con.literal 传递。con.literal 获取您提供的内容并尝试将其转换为 MySQL 正确解析的字符串。它在字符串中添加撇号。它输出不带引号的数字。或者例如,如果你给它一个带撇号的字符串,它会引用撇号。

在您的特定情况下,当您将一个只有一个项目的元组交给它时,MySQLdb 似乎总是会做错事(con 是任何打开的连接):

>>> con.literal((10,))
('10',)
>>>

这就是 Python 期望能够创建一个包含单个项目的元组,但它会导致 MySQL 出错(向右滚动以查看出错情况):

mysql> select distinct sites.SiteCode, sites.SiteName, Latitude, Longitude, County, SourceID from sites, seriescatalog where sites.SiteID = seriescatalog.SiteID and sites.SiteID in (82,);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
mysql> 

这是我防止 Bobby Tableling 的解决方法:

siteinfoquery = """select distinct sites.SiteCode, sites.SiteName, Latitude, Longitude, County, SourceID from sites, seriescatalog where sites.SiteID = seriescatalog.SiteID and sites.SiteID in (%s);"""

cur.execute(siteinfoquery % ",".join([str(int(i)) for i in SiteID]))

顺便说一句,以下内容不起作用,因为 con.literal (在 MySQLdb 的 execute() 函数中调用)引用了结果字符串,将其从数字列表转换为字符串,然后变成与任何 SiteID 都不匹配的单个值:

cur.execute(siteinfoquery , ",".join([str(int(i)) for i in SiteID]))

>>> "(%s)" % con.literal(",".join([str(float(i)) for i in SiteID]))
"('10.0,20.0')"
>>> 

我没有查看这是否是更高版本中修复的错误、当前问题、我的错误或他们不知道的问题。

于 2015-12-21T18:34:52.843 回答
0

我无法用 MySQLdb 复制它。我正在使用 1.2.2 最终版。也许尝试一个简单的调试,以确保问题出在您指出的位置:

In [13]: cur.execute('select %s + %s', (1,2))
Out[13]: 1L

In [14]: cur.fetchall()
Out[14]: ((3L,),)

更新 1:所以我抓取并安装了 1.2.3 final,这是我的成绩单:

In [1]: import MySQLdb
In [2]: MySQLdb.version_info
Out[2]: (1, 2, 3, 'final', 0)
In [3]: con = MySQLdb.connect(user='root', db='inventory')
In [4]: cur = con.cursor()
In [5]: cur.execute('select %s + %s', (1,2))
Out[5]: 1L
In [6]: cur.fetchall()
Out[6]: ((3L,),)

如果只有我可以复制您的问题,那么也许我可以提供解决方案!?那么我们的两个环境之间还有什么不同呢?

$ mysql --version
mysql  Ver 14.14 Distrib 5.1.41, for debian-linux-gnu (x86_64) using readline 6.1

更新 2:您也许可以将事情分解得比上面更多。请注意以下来自 MySQLdb-1.2.3 的源代码片段:

139     def execute(self, query, args=None):
...
158         if args is not None:
159             query = query % db.literal(args)
160         try:
161             r = self._query(query)
162         except TypeError, m:
163             if m.args[0] in ("not enough arguments for format string",
164                              "not all arguments converted"):
165                 self.messages.append((ProgrammingError, m.args[0]))

第 159 行是您的 args 被转换/转义然后插入到查询字符串中的地方。所以也许你可以做这样的事情:

In [24]: con.literal((1,2,))
Out[24]: ('1', '2')

在与查询字符串合并之前,查看驱动程序如何转换参数。

In [26]: "select %s + %s" % con.literal((1,2,))
Out[26]: 'select 1 + 2'
于 2011-04-27T02:14:53.153 回答
0

工作与非工作示例:

import MySQLdb as mysql
from MySQLdb.constants import FIELD_TYPE
In [9]: conn = mysql.connect(host=hostname, user=user_name, passwd=password, port=port_number, db=schema_name)
In [10]: cursor = conn.cursor()
In [11]: cursor.execute('select %s + %s', (1, 3))
Out[11]: 1L

In [16]: conn = mysql.connect(host=hostname, user=user_name, passwd=password, port=port_number, db=schema_name, conv={FIELD_TYPE.LONG: long})
In [17]: cursor = conn.cursor()
In [18]: cursor.execute('select %s + %s', (1, 3))---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-08423fe5373f> in <module>()
----> 1 cursor.execute('select %s + %s', (1, 3))
/usr/lib64/python2.6/site-packages/MySQLdb/cursors.pyc in execute(self, query, args)
    156             query = query.encode(charset)
    157         if args is not None:
--> 158             query = query % db.literal(args)
    159         try:
    160             r = self._query(query)
TypeError: not enough arguments for format string

conv参数 to对mysql.connect我来说是个问题。没有它,我的查询参数将按预期解释。有了它,我把头发扯掉了。

于 2017-05-17T20:07:24.113 回答