存储过程不能真正隐藏表结构。可通过SHOW CREATE PROCEDURE或查询information_schema.ROUTINES查看定义;若用户有SELECT权限,还能直接查mysql.proc或performance_schema获取源码。

存储过程真能隐藏表结构吗
不能。调用 SHOW CREATE PROCEDURE 或查询 information_schema.ROUTINES 就能看到定义;如果用户有 SELECT 权限,还能直接查 mysql.proc(旧版本)或通过 performance_schema 获取源码。所谓“隐藏”,只是让普通 SELECT * FROM users 这类直连方式不暴露字段名和关联逻辑,但前提是——你得先剥夺用户对基表的直接访问权限。
- 必须显式回收用户对底层表的
SELECT、INSERT等权限,只授予对存储过程的EXECUTE - MySQL 8.0+ 支持角色(
ROLE),建议用角色统一管理权限,避免漏掉某张表 - 存储过程内部若用动态 SQL(
PREPARE/EXECUTE),且拼接了用户输入,反而会放大注入风险
怎么写一个防注入的存储过程参数处理
核心是别让参数进 SQL 字符串拼接。MySQL 不支持参数化查询语句(PREPARE 的 USING 只支持值,不支持列名/表名),所以字段名、排序方向、分页偏移这些不能由用户控制的部分,必须硬编码或白名单校验。
- 用户可控的值(如搜索关键词、状态码)一律用
IN参数 +WHERE col = p_keyword方式,MySQL 会自动做类型绑定 - 用户想控制排序字段?用
CASE白名单映射:ORDER BY CASE p_sort_field WHEN 'name' THEN name WHEN 'created_at' THEN created_at ELSE id END - 拒绝这种写法:
SET @sql = CONCAT('SELECT * FROM t ORDER BY ', p_sort_field)—— 即使加了引号也拦不住p_sort_field = 'id ASC, (SELECT SLEEP(5))'
为什么用存储过程后还是被拖走全部数据
常见于两类疏漏:
存储过程里用了
SELECT *且没加LIMIT,攻击者反复调用就能分页扫库过程返回结果集过大,而客户端(比如 PHP 的
mysqli_multi_query)默认不设超时或行数限制,导致一次调用拉走百万行所有返回数据的过程,必须显式加
LIMIT,且上限要根据业务卡死(比如最多 1000 行)若需导出全量,改用定时任务生成脱敏文件,而非开放实时查询接口
检查 MySQL 的
max_allowed_packet和应用层连接超时,防止大结果集阻塞连接池
PostgreSQL 或 SQL Server 用户别照搬
MySQL 的存储过程权限模型和 SQL Server 的 EXECUTE AS、PostgreSQL 的 SECURITY DEFINER 函数完全不同。后者可实现真正的“以函数所有者身份执行”,即使调用者无表权限也能查;而 MySQL 的存储过程执行权限检查发生在调用时,且依赖调用者是否具备过程中每条语句所需的权限(除非用 SQL SECURITY DEFINER 并确保 definer 账户权限最小化)。
- MySQL 必须显式声明
SQL SECURITY DEFINER,否则默认是INVOKER,等于没设防 -
DEFINER账户不能是root或拥有FILE权限的高危账号 - PostgreSQL 中同功能应优先用
SECURITY DEFINER函数 +REVOKE基表权限,而不是过程
真正难的不是写过程,是持续维护权限边界和过程内逻辑的封闭性。每次加字段、改关联、新增输出列,都得同步检查权限、白名单、LIMIT 和错误信息是否泄露细节。

















