PHP常量一旦定义不可修改,若值来自用户输入则污染从定义起即存在,影响路由、权限等逻辑;必须仅用硬编码、可信变量定义,禁用$_GET等外部数据及动态常量名。

PHP系统常量一旦被定义(define() 或 const),默认不可修改,但**若常量值来源于用户输入,就等于在定义阶段已将污染数据固化进运行环境**——这不是“被篡改”,而是“从一开始就错了”。这种污染会持续影响后续所有依赖该常量的逻辑,比如路由分发、权限标识、路径拼接等,风险隐蔽且难以追溯。
常量必须由开发者完全可控
只有硬编码字符串、计算结果或内部可信变量(如配置数组中的固定键值)可参与常量定义。任何来自 $_GET、$_POST、$_COOKIE、$_SERVER、数据库读取、文件读取、会话数据等外部源的数据,都不得直接或间接用于 define() 的第二个参数。
- ❌ 危险示例:
define('SITE_NAME', $_POST['site_name']); - ✅ 安全做法:
define('SITE_NAME', 'MyCompany'); // 纯手工写死,或从白名单配置中取值 - ⚠️ 注意:即使加了
filter_var()或htmlspecialchars(),也不应定义为常量——常量不是输出环节,不承担转义职责;它的作用是提供确定性上下文,一旦掺入过滤逻辑,反而模糊语义、增加误用可能
禁止动态构造常量名
用用户输入拼接常量名(如 define($_GET['key'], 'value'))属于高危行为,等同于开放任意常量注册接口。PHP虽不支持变量名直接作常量名,但通过 eval()、constant() 配合反射等手段仍可能触发不可控行为。
- ❌ 绝对禁止:
$name = $_GET['const_name']; define($name, 'val'); - ✅ 替代方案:用关联数组代替动态常量,例如
$config['theme'] = $_POST['theme'] ?? 'default';,后续统一通过数组访问 - ? 强化措施:在自动加载或初始化阶段,对所有
define()调用做静态扫描,拦截含变量参数的调用
警惕“伪常量”陷阱
有些开发者误将类常量(class::CONST)、配置类属性或全局变量当作“安全常量”,但若其值在运行时被用户输入覆盖(如构造函数注入、setter赋值、反序列化还原),同样构成污染。
立即学习“PHP免费学习笔记(深入)”;
- ❌ 风险场景:
class Config { public const DB_HOST = $_ENV['DB_HOST'] ?? 'localhost'; }——$_ENV可被恶意设置 - ✅ 正确方式:环境变量需在入口处校验并转换为真正不可变值,再传入常量或配置对象,例如:
define('DB_HOST', filter_var(getenv('DB_HOST'), FILTER_VALIDATE_IP) ?: '127.0.0.1'); - ? 检查点:搜索项目中所有
define(、const和static $声明,确认右侧表达式不含任何超全局变量或外部读取操作
构建常量安全检查机制
仅靠人工审查易遗漏。建议在开发流程中嵌入自动化防护:
- ? 编码规范强制要求:所有
define()必须附带注释说明来源,非硬编码项需标注“经白名单校验”并给出校验逻辑行号 - ? CI/CD 中加入 PHPStan 或自定义 AST 扫描规则,检测
define()第二参数是否含$_开头变量、getenv()、file_get_contents()等敏感调用 - ?️ 运行时防御:启用
disable_functions = define(仅限调试环境),或重写define()函数做日志审计(生产慎用)



















