The Background
The python documentation for the sqlite3 module (here) says:
Usually your SQL operations will need to use values from Python variables. You shouldn’t assemble your query using Python’s string operations because doing so is insecure; it makes your program vulnerable to an SQL injection attack (see http://xkcd.com/327/ for humorous example of what can go wrong).
Instead, you are supposed to let the sqlite3 library do the substitution for you, using question marks for placeholders, relying on that library's implementation of the DB-API's parameter substitution mechanism.
The documentation follows this up with an example:
# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
The Question
Using DB-API’s parameter substitution instead of building the query yourself using python string operations is:
a) inherently more secure in some way (please explain)
or
b) "merely" a really great idea because it uses an existing, well-tested library that will do the sanitization for me, saving me from countless hours of debugging and countless bugs and exploits?
Comments and thoughts on the question
I suspect b (not least because of answers like this one). If the answer is a, there is a subtle source of insecurity here that I do not understand, and this insecurity needs to be explained somewhere.
The confusing point is that there is absolutely nothing insecure about the example given. It very clearly reduces (100% of the time) to:
c.execute("SELECT * FROM stocks WHERE symbol = 'RHAT'")
which is perfectly good.
It is bad programming style, because a later user might so easily change symbol to come from an external source, and that would be insecure, like this:
symbol = raw_input("enter a stock symbol")
#symbol comes straight from the user! what horrors it could contain...
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
and even this could be argued to be insecure:
def get_stock(symbol):
#goodness knows where symbol comes from or whether it has been
#sanitized properly yet...
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
I am looking for confirmation that the original example is not insecure, but is instead bad programming practice because it might easily become insecure. Those are two different things, and it is important to understand the difference between them.