PHP 8.1+ 知识体系¶
本目录面向希望系统学习现代 PHP、Web 开发与安全编码的读者。 全文以 PHP 8.1 及以上版本为基线,示例默认开启严格类型并采用安全写法。 PHP 是动态类型语言,但支持标量类型、联合类型、属性类型和静态分析。 它常由 Web 服务器为每个请求执行入口脚本,也可作为 CLI 程序运行。
使用边界¶
安全章节只面向本地实验、授权 CTF、代码审计和防御学习。 不得将示例用于未获授权的系统,也不要构造或传播武器化 payload。 面对真实漏洞,应保留证据、停止扩散并按组织流程负责任披露。
阅读路线¶
- 环境、语法与类型
- 数组、控制流与函数
- Web 请求、Cookie、Session 与上传
- 数据库与 PDO
- PHP 安全与 CTF 建议按顺序阅读;已有 PHP 基础的读者可从请求生命周期或 PDO 开始。 安全篇依赖前文对比较、包含、文件路径、数据库和 HTTP 状态的理解。
确认运行环境¶
检查版本与配置文件位置:
本体系要求版本输出至少为8.1。
生产环境应使用仍在官方安全支持期内的 PHP 小版本。
不要依赖教程中已经移除的 mysql_*、each() 或魔术引号等旧机制。
最小程序¶
保存为hello.php 后运行:
纯 PHP 文件通常省略结尾 ?>,避免其后的空白意外进入响应体。
本地内置服务器¶
在仅含学习代码的目录运行:
浏览器访问http://127.0.0.1:8000/。
内置服务器面向开发和调试,不具备生产服务器所需的并发、隔离和加固能力。
绑定 127.0.0.1 可避免无意向局域网暴露实验服务。
PHP 的执行模型¶
典型 Web 请求经历以下阶段: 1. 客户端向 Web 服务器发送 HTTP 请求。 2. Web 服务器或 PHP-FPM 将脚本、请求元数据和请求体交给 PHP。 3. PHP 初始化配置、扩展、超全局变量和会话等请求状态。 4. 入口脚本加载依赖,验证输入,执行业务逻辑并访问外部资源。 5. 程序设置状态码和响应头,再输出响应体。 6. 请求结束后执行清理;普通请求内变量不会自动跨请求保留。 PHP-FPM 工作进程可以复用,但应用不能把普通全局变量当作可靠持久存储。 跨请求状态应放入数据库、缓存或正确配置的 Session 存储。
核心心智模型¶
值、类型与转换¶
变量名以 $ 开头,值在运行时具有类型:
declare(strict_types=1) 主要影响当前文件中的标量参数与返回值调用检查,
不会把 PHP 变成静态类型语言,也不会禁止所有隐式转换。
数组是有序映射¶
PHP 的 array 同时承担列表和字典角色:
array_is_list() 检查。
输入永远先是不可信数据¶
$_GET、$_POST、Cookie、请求头、上传文件和数据库旧数据都可能被操控。
安全处理不是“统一过滤”,而是按用途执行:
- 先验证输入是否符合业务格式和范围。
- 数据库值通过参数化查询绑定。
- HTML 输出按 HTML 上下文转义。
- 文件名与路径通过固定根目录和规范化策略约束。
- 外部命令优先改用 PHP API;无法避免时严格固定程序和参数边界。
推荐工程工具¶
Composer 是 PHP 生态常用依赖管理器:
只从可信来源安装 Composer,提交composer.json 与应用的 composer.lock。
常见质量工具包括 PHPUnit、PHPStan 或 Psalm,以及 PHP-CS-Fixer。
它们是第三方工具,应由项目统一锁定版本和配置。
错误与异常基线¶
开发环境可提高错误可见性,但生产响应不能展示堆栈和路径:
配置应由部署环境管理,不要在公共入口中随意覆盖生产策略。 现代 PHP 中许多引擎错误实现Throwable,业务代码通常抛出 Exception 子类。
捕获异常时应记录必要上下文,但不得记录密码、令牌、完整 Cookie 或敏感请求体。
编码与输出¶
源码通常使用 UTF-8。字符串本质上是字节序列,不自动保证有效 UTF-8。
处理 Unicode 长度和截取时使用 mb_* 扩展并明确编码。
HTML 输出应指定字符集:
header('Content-Type: text/html; charset=UTF-8');
echo htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
JSON_THROW_ON_ERROR,让编码失败成为明确异常。
配置与秘密¶
数据库密码、Session 密钥和第三方令牌不应写入源码或提交 Git。 使用部署平台的秘密管理、受权限保护的环境配置或专用配置服务。 环境变量仍可能被进程、日志或错误页泄露,读取后同样要验证。 生产配置应关闭目录列表,限制上传大小,并设置合理的执行与资源上限。
最低安全要求¶
- 使用仍受支持的 PHP、框架、扩展和依赖版本。
- 所有 SQL 都使用 PDO 参数化查询,不拼接外部输入。
- 所有输出根据 HTML、URL、JSON 等具体上下文编码。
- Session Cookie 设置
Secure、HttpOnly和合适的SameSite。 - 上传文件限制大小、类型和数量,并存放在 Web 根目录之外。
- 文件路径采用允许列表和固定根目录,不直接包含用户提供的路径。
- 不对不可信数据调用
unserialize(),优先使用 JSON。 - 不把用户文本拼接进 shell 命令、代码或模板表达式。
- 日志避免敏感数据,并为异常响应使用统一错误页。
常见误区¶
filter_input() 不是万能净化器;验证规则必须与业务字段语义一致。
htmlspecialchars() 防的是特定 HTML 文本/属性上下文,不防 SQL 注入。
预处理语句防 SQL 结构注入,但不能代替授权检查和数据范围验证。
隐藏报错只减少信息泄露,不会修复漏洞。
扩展名、客户端 MIME 和 Content-Type 都不能单独证明上传文件安全。
弱类型不是漏洞本身;宽松比较与错误验证组合时才常形成安全问题。
学习方法¶
每个示例先在本地最小目录运行,再修改一个条件观察行为。
使用 var_dump() 同时观察值与类型:
完成后的自检¶
- CLI 与内置服务器分别适合什么场景?
strict_types能保证什么,又不能保证什么?- PHP 数组为什么既像列表又像映射?
- 一个 HTTP 请求如何形成超全局变量和响应?
- Cookie 与 Session 的数据分别保存在哪里?
- 上传文件为什么必须重新命名并移出 Web 根目录?
- PDO 参数为什么不能代替表名或排序关键字?
==与===的差异如何影响认证和校验?- 文件包含、命令执行和反序列化为什么必须有明确数据边界? 能准确回答这些问题后,再结合具体框架学习路由、中间件、模板和 ORM。