You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用SQLAlchemy Core创建用户时如何避免SQL注入?

SQLAlchemy中安全创建PostgreSQL用户的推荐实现方式

先回顾Psycopg的安全写法

在Psycopg中,我们可以通过SQL、Identifier和Literal的组合避免SQL注入,确保用户名被正确引号包裹,密码被安全处理:

from psycopg.sql import SQL, Identifier, Literal

username = "test_user"
password = "test_password"
query = SQL(
    "CREATE USER {username} WITH ENCRYPTED PASSWORD {password};"
).format(username=Identifier(username), password=Literal(password))

在Connection范围内测试输出:

print(query.as_string(connection))  # CREATE USER "test_user" WITH ENCRYPTED PASSWORD 'test_password';

遇到的问题:SQLAlchemy中quoted_name未生效

尝试用quoted_name和绑定参数实现,但用户名没有被正确添加引号:

from sqlalchemy import quoted_name, text

query = text(
    f"CREATE USER {quoted_name(username, True)} WITH ENCRYPTED PASSWORD :password;"
).bindparams(password=password).compile(compile_kwargs={"literal_binds": True})

print(query)  # CREATE USER test_user WITH ENCRYPTED PASSWORD 'test_password';

(修改quoted_name的第二个参数为False或None,结果一致)

正确的实现方式

方法1:使用text结合Identifier与绑定参数

通过SQLAlchemy的Identifier处理用户名,密码用绑定参数传递,确保两者都被安全处理:

from sqlalchemy import text, create_engine
from sqlalchemy.sql import Identifier

username = "test_user"
password = "test_password"

# 构造查询:用户名用Identifier类型传入,密码用绑定参数
query = text("CREATE USER :username WITH ENCRYPTED PASSWORD :password").bindparams(
    username=Identifier(username),
    password=password
)

# 编译时指定PostgreSQL方言,确保语法正确
compiled_query = query.compile(
    dialect=create_engine("postgresql:///").dialect,
    compile_kwargs={"literal_binds": True}
)
print(compiled_query)  # 输出:CREATE USER "test_user" WITH ENCRYPTED PASSWORD 'test_password'

方法2:使用DDL对象构造语句

SQLAlchemy的DDL对象更适合处理数据定义语言语句,配合quoted_name使用:

from sqlalchemy import DDL, create_engine, quoted_name

username = quoted_name("test_user", quote=True)
password = "test_password"

# 构造DDL语句并绑定参数
ddl_stmt = DDL("CREATE USER :username WITH ENCRYPTED PASSWORD :password").bindparams(
    username=username,
    password=password
)

# 编译输出
compiled_stmt = ddl_stmt.compile(
    dialect=create_engine("postgresql:///").dialect,
    compile_kwargs={"literal_binds": True}
)
print(compiled_stmt)  # 输出:CREATE USER "test_user" WITH ENCRYPTED PASSWORD 'test_password'

为什么之前的写法失效?

直接用f-string插入quoted_name对象时,Python会直接将其转换为普通字符串,丢失了SQLAlchemy对它的引号处理逻辑。必须通过绑定参数或text的格式化方法传递这类SQL元素,才能让SQLAlchemy在编译时正确处理引号。

内容的提问来源于stack exchange,提问作者Sebastian Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:55:19