对接仅支持接收原生SQL的ERP API:无预编译语句/参数化时Node与PostgreSQL的SQL安全处理方案
对接仅支持接收原生SQL的ERP API:无预编译语句/参数化时Node与PostgreSQL的SQL安全处理方案
这种只能传原生SQL的ERP API确实挺棘手的——没法用常规的参数化查询,还要兼顾不同角色的权限控制,尤其是搜索场景的注入风险。结合PostgreSQL的特性和Node.js的工具,给你几个亲测有效的思路:
1. 用PostgreSQL原生函数做安全转义
PostgreSQL自带的quote_literal()和quote_ident()是专门用来处理SQL注入的利器,比自己手动加$符号靠谱多了:
quote_literal():会把输入的字符串转义成安全的SQL字符串常量,自动处理单引号、换行等危险字符quote_ident():用来转义表名、字段名这类标识符,防止标识符注入
比如你要构造一个搜索用户姓名的SQL,低权限角色的查询可以这么写:
const userSearchTerm = req.query.search; // 用quote_literal包裹用户输入的搜索词 const safeSql = `SELECT name, email FROM allowed_table WHERE name LIKE '%' || quote_literal('${userSearchTerm}') || '%' LIMIT 100`;
这样即使用户输入' OR 1=1--,PostgreSQL也会把它当成普通字符串处理,不会触发注入。
2. 本地SQL语法校验+权限白名单
既然没法用数据库权限控制,那就把校验逻辑放在你的Node云函数里:
- 用
pg-query-parser这个Node库解析你构造好的SQL,提前检查语句类型:比如低权限角色只能执行SELECT,禁止DELETE/UPDATE/DROP/ALTER这类危险操作 - 限定查询的表和字段:比如低权限用户只能查
customer表的name/phone字段,解析SQL后检查是否访问了其他表/字段 - 禁止危险语法:比如子查询、
UNION、EXECUTE这类可能被用来注入的语法
示例代码片段:
const parse = require('pg-query-parser').parse; function validateLowPrivilegeSql(sql) { try { const parsed = parse(sql); // 检查是不是SELECT语句 if (parsed.type !== 'SELECT') return false; // 检查是否访问了允许的表(这里假设只允许allowed_table) const tables = parsed.from?.map(item => item.name); if (!tables || !tables.every(t => t === 'allowed_table')) return false; // 检查是否包含危险关键字 if (sql.match(/(DELETE|UPDATE|DROP|UNION|EXECUTE)/i)) return false; return true; } catch (e) { // 解析失败的SQL直接拒绝 return false; } }
只有通过校验的SQL才发给ERP API,相当于在本地加了一道安全闸。
3. 增强版输入清洗(兜底方案)
如果ERP API不允许你用quote_literal(),那可以做针对性的输入清洗:
- 把用户输入里的单引号替换成双引号(PostgreSQL里单引号的转义方式是两个单引号)
- 过滤掉控制字符(比如换行、制表符)
- 限制输入长度,比如搜索词最多64个字符
工具函数示例:
function sanitizeSearchInput(input) { if (typeof input !== 'string') return ''; // 转义单引号,过滤控制字符,截断过长内容 return input .replace(/'/g, "''") .replace(/[\x00-\x1F\x7F]/g, '') .slice(0, 64); }
然后把清洗后的字符串用单引号包裹进SQL:
const safeTerm = sanitizeSearchInput(userSearchTerm); const safeSql = `SELECT name FROM allowed_table WHERE name LIKE '%${safeTerm}%' LIMIT 100`;
这个方法比单纯限制字母数字灵活很多,毕竟用户可能需要搜索带特殊字符的内容。
4. 强制添加LIMIT限制
不管用哪种方法,给低权限角色的所有查询加上LIMIT(比如LIMIT 100),就算真的出现注入,也能避免被拖库,把危害降到最低。
额外提醒
- 永远不要让低权限用户直接输入SQL片段,所有动态内容都必须经过处理
- 测试的时候用常见的注入语句(比如
' OR 1=1--、'; DROP TABLE test--)验证你的防护逻辑 - 如果ERP API支持日志,开启日志监控低权限用户的查询,及时发现异常
备注:内容来源于stack exchange,提问作者Luke Weston
相关产品推荐
相关产品推荐

