Using psycopg2 query parameters in the execute function is safest, and it is easy to use when the parameters are being used as literals.
cursor.mogrify("select * from foo where bar = %s", ('example',))
# yields "select * from foo where bar = 'example'"
(Note that cursor.mogrify() acts like execute, but just shows the formatted SQL without actually executing it)
However, it is a little trickier to do when you want the parameter to be a table, schema, or other identifier. You could use AsIs to wrap your parameter, but that still leaves the door open to SQL injection.
from psycopg2.extensions import AsIs
cur.mogrify('select %s from foo;', (AsIs('* from dual; drop table students; --'),))
# yields 'select * from dual; drop table students; -- from foo;'
It looks like new development of psycopg2 (>=2.7) will have an Identifier class that you can wrap parameters in and hopefully be safe. If it's not released yet, or if you don't have it, here's a way to create your own class. I'll give some snippets below, but you can also see my gist.
import re
import psycopg2.extensions
class NotSqlIdentifierError(Exception):
pass
valid_pattern = r'^[a-zA-Z_][a-zA-Z0-9_\$]*$'
class QuotedIdentifier(object):
def __init__(self, obj_str):
self.obj_str = obj_str
def getquoted(self):
if re.match(valid_pattern, self.obj_str):
return self.obj_str
else:
raise NotSqlIdentifierError(repr(self.obj_str))
psycopg2.extensions.register_adapter(QuotedIdentifier, lambda x: x)
If you have a psycopg2 cursor instance already, you can test/use it this way:
# Test that a valid identifier formats into string
cursor.mogrify('select %s from foo;', (QuotedIdentifier('bar'),))
# returns 'select bar from foo;'
# Test formatting both an identifier and a literal
cursor.mogrify(
'select * from foo where %s = %s;',
(
QuotedIdentifier('bar'),
'example'
)
)
# returns "select * from foo where bar = 'example';"
# Test that a non-valid identifier fails with exception
cursor.mogrify('select %s from foo;', (QuotedIdentifier('* from dummy; drop table students; --'),))
"""Returns following:
---------------------------------------------------------------------------
NotSqlIdentifierError Traceback (most recent call last)
<ipython-input-14-d6a960dc458a> in <module>()
----> 1 cur.mogrify('select %s from foo;', (QuotedIdentifier('* from dummy; drop table students; --'),))
<ipython-input-12-0a1327cbaf78> in getquoted(self)
18 return self.obj_str
19 else:
---> 20 raise NotSqlIdentifierError(repr(self.obj_str))
21
22 psycopg2.extensions.register_adapter(QuotedIdentifier, lambda x: x)
NotSqlIdentifierError: '* from dummy; drop table students; --'
"""
For more info on the mechanics of custom classes to wrap SQL parameters, see this section in the docs.
tablecoming from somewhere else in your real code? Because in this example,tableis coming from a constant in your code, so there's no security issue.