如何在SQLModel的JSONPATH查询中避免SQL注入?
解决SQLModel中JSON路径查询的SQL注入问题
问题根源
你的代码直接将用户输入的value字符串拼接进JSON路径表达式,导致攻击者可以通过构造恶意输入篡改查询逻辑。比如传入value='"' || ""=="'"时,最终的JSON路径会变成:
$[*] ? (@.id == "stg") ? (@.val == "" || ""=="")
这个表达式会恒为真,完全绕过原本的查询限制。
最优解决方案:使用参数化JSON路径
PostgreSQL的jsonb_path_exists支持参数化JSON路径,可以通过传递JSON格式的参数对象安全引用外部值,彻底避免字符串拼接带来的注入风险。SQLModel完全兼容这种用法,具体实现如下:
代码示例
from sqlmodel import Session, func, select def some_fn(session: Session, value: str) -> list[DbItem]: statement = select(DbItem).where( func.jsonb_path_exists( DbItem.json_field, # 在JSON路径中用$val作为参数占位符 '$[*] ? (@.id == "stg") ? (@.val == $val)', # 构建参数对象,将value绑定到$val变量 func.jsonb_build_object('val', value) ), ) return session.exec(statement).all()
原理说明
- 参数化占位符:在JSON路径中用
$val标记需要外部传入的参数,替代直接字符串拼接。 - 安全参数传递:通过
func.jsonb_build_object('val', value)构造JSON参数对象,将用户输入的value作为数据库参数传递。数据库会自动对参数进行转义和绑定,不会将其解析为JSON路径的执行逻辑。 - 注入防护:无论用户输入什么内容,都会被当作普通字符串处理,无法篡改原有的查询逻辑。
备选方案:显式绑定参数(复杂场景适用)
如果需要更清晰地控制参数绑定逻辑,可以使用sqlmodel.bindparam显式声明参数:
from sqlmodel import Session, func, select, bindparam def some_fn(session: Session, value: str) -> list[DbItem]: # 显式绑定参数 value_param = bindparam("target_val", value=value) statement = select(DbItem).where( func.jsonb_path_exists( DbItem.json_field, '$[*] ? (@.id == "stg") ? (@.val == $target_val)', func.jsonb_build_object('target_val', value_param) ), ) return session.exec(statement).all()
这种方式在多参数、复杂查询场景下更易维护,便于排查参数绑定问题。
内容的提问来源于stack exchange,提问作者László Ács
相关产品推荐
相关产品推荐

