Postgres.js 动态建表时如何安全插入列类型?
数据阿明
结论
Postgres.js 的 sql(value) 用来安全引用标识符,例如 schema、表名和列名。它不会将字符串作为 SQL 语法直接插入。因此,sql(column.type) 会被当作标识符处理,而普通模板插值会生成参数占位符。列类型属于 SQL 语法片段,不能作为普通值参数传入。
可以将允许的类型名映射到预先定义的 SQL 片段,再把对应片段插入查询。Postgres.js 文档说明,sql.unsafe 可以在安全模板中嵌入原始 SQL 片段,但动态内容必须受到严格限制。Postgres.js 文档:Unsafe raw string queries
示例
const typeFragments = new Map([
['uuid', sql.unsafe('uuid')],
['boolean', sql.unsafe('boolean')],
['text', sql.unsafe('text')],
]);
const columns = table.columns.map((column) => {
const type = typeFragments.get(column.type);
if (!type) {
throw new Error(`Unsupported column type: ${column.type}`);
}
return sql`${sql(column.name)} ${type} ${
column.pk ? sql`primary key` : sql``
}`;
});
await sql`
create table if not exists ${sql(table.schema)}.${sql(table.name)} (
${sql.join(columns, sql`, `)}
)
`;
sql(column.name)、sql(table.schema) 和 sql(table.name) 会将动态内容作为标识符处理。类型片段从固定映射中选取,未列入映射的输入会被拒绝。示例使用 sql.join,并以分隔符组合各列定义。
注意事项
不要直接将来自请求、文件或其他不可信来源的 column.type 传给 sql.unsafe(column.type)。类型名不能作为参数绑定;如果允许任意输入直接进入 SQL,就可能引入 SQL 注入风险。需要支持更多类型时,应将明确允许的类型加入映射。若要支持带 schema 的自定义类型,也应先验证各部分,再构造对应片段。
备注:内容仅供参考。
