SQLAlchemy's ORM is one of the better defenses against SQL injection available to Python developers. Normal ORM calls — filter(), filter_by(), query() — compile to parameterized queries. The database receives the value separately from the SQL string. String-formatted SQL injection is structurally impossible through that path.
The problem is text().
Why the ORM Usually Protects You
BrassCoders finds very few SQL injection findings in codebases that stay within SQLAlchemy's ORM layer because the ORM compiles to parameterized queries by design — values travel through bind parameters, not string concatenation.
When an AI assistant writes session.query(User).filter(User.id == user_id).first(), SQLAlchemy compiles this to SELECT * FROM users WHERE id = ? and passes user_id as a separate value. The database driver never sees the value merged into the SQL string. No amount of SQL syntax in user_id can modify the query structure.






