1. You need a query that can't be handled by your ORM, since in general ORMs can't expose all functions of a database. (This condition is also met if you have no ORM.)
2. You need a query that is going to have a varying number of parameters, so you need to manually assemble the query.
When that happens, there's a temptation in almost all systems to try to write the moral equivalent of:
sql_query = "WHERE condition = '" + param1 + "'
if something:
sql_query += " AND other_thing = '" + param2 + "' "
and so on and so on. This is where I see most of the SQL injection, even in language communities that have otherwise thoroughly internalized the need for query parameters (for example, perhaps surprisingly, the Perl community for a very long time).Programmers who are not really fluent yet and are still having to think about every line either don't think about, or are trying to avoid the complexity of,
sql_query = "WHERE condition = ?"
params.append(param1)
if something:
sql_query += " AND other_thing = ? "
params.append(param2)
are the source. This pattern is, broadly speaking, safe. But it has just that extra bit of the sort of complexity that a lot of programmers shy away from. (As opposed to the sorts that programmers almost seem to chase, like "adding 15 boolean parameters to a function over the course of a year".)There's also usually a library in the target language that is a pretty good reflection of SQL that avoids this problem (such as SQLAlchemy in Python), but I think that most people consider this "too complicated".