生产环境php上线前必须关闭display_errors=off、register_globals=off(旧项目迁移需防兼容层激活)、error_reporting=e_all & ~e_notice & ~e_deprecated;否则错误日志将暴露路径、框架版本甚至数据库信息,等同于公开api。

生产环境 PHP 代码上线前,不检查 display_errors、register_globals、error_reporting 这三项,等于把错误日志当公开 API 暴露给攻击者——轻则泄露路径和框架版本,重则直接拖库。
php.ini 关键安全项必须关闭
很多团队只改了应用层配置,却忘了底层 php.ini 是全局生效的“默认开关”。尤其在容器或共享主机环境下,这个文件往往被忽略。
-
display_errors = Off:必须关。开启后会把致命错误堆栈、数据库连接信息、绝对路径原样输出到浏览器 -
register_globals = Off:PHP 5.4+ 已移除,但旧项目迁移时若用兼容层模拟,仍可能激活变量污染漏洞 -
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED:生产环境不需提示级错误;但注意别写成0,否则连error_log()都可能失效 - 额外建议:
expose_php = Off防止响应头暴露X-Powered-By: PHP/8.2.12,减少指纹识别面
PDO 预处理语句是否真正在用?
光有 $pdo->prepare() 不代表安全。常见误用是“假预处理”:拼接变量进 SQL 字符串后再 prepare,或者对表名/字段名这类无法参数化的部分也试图用 ? 占位。
- 正确姿势:所有用户可控值(
$_GET、$_POST、$_COOKIE)必须作为execute()的参数传入,不能出现在 SQL 字符串里 - 危险模式示例:
"SELECT * FROM {$table} WHERE id = ?"——$table来自用户输入,? 占位毫无意义 - 检查方法:全局搜索
mysql_query(、mysqli_query(、->query((非 prepare),这些调用必须为 0 - 注意扩展差异:MySQLi 的
bind_param()要求类型声明(i/s),PDO 则靠execute()自动推断,但PDO::ATTR_EMULATE_PREPARES = false必须显式设置,否则仍走模拟模式
htmlspecialchars() 是否覆盖全部输出上下文?
只在 HTML body 里用 htmlspecialchars() 不够。属性值、JavaScript 内联、CSS、URL 参数等不同上下文,需要不同编码策略,混用会失效。
- HTML 标签内文本:
htmlspecialchars($s, ENT_QUOTES, 'UTF-8')✅ - HTML 属性值(如
value="..."):htmlspecialchars($s, ENT_QUOTES | ENT_HTML5, 'UTF-8'),避免双引号逃逸失败 - JS 字符串内嵌:
json_encode($s, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG),别用addslashes() - URL 参数:
urlencode($s),不是htmlspecialchars() - 富文本场景:必须用
HTMLPurifier或类似白名单过滤器,strip_tags()和正则替换不可信
敏感配置是否真的脱离 Web 根目录?
所谓“配置文件放外面”,常被理解为移到 ../config.php,但若 Web 服务器未禁用该路径解析,或存在 .htaccess 失效、Nginx alias 配置错误,依然可被直接下载。
- 验证方式:用 curl 直接请求
http://yoursite.com/../config.php,看是否返回 403 或 404,而非 PHP 源码 - 更稳妥做法:配置中禁止执行 PHP 的目录(如 Nginx 的
location ~ \.php$ { deny all; }),并确保配置文件后缀不是.php(例如用.ini+parse_ini_file()) - 环境变量优先:用
getenv('DB_PASSWORD')替代include '../config.php',密钥由容器或 systemd 传入,代码里不留痕迹 - 硬编码检测:grep -r "password\|secret\|key" --include="*.php" .,任何匹配都必须立即处理
最容易被跳过的其实是日志行为本身——error_log() 默认写到系统 syslog 或 Apache 错误日志,如果没配 log_errors = On 且 error_log 指向可控路径,你以为关了 display_errors 就安全了,其实错误早已悄悄落盘到可被遍历的目录里。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











