PHP的易用性使其成为攻击者的重点目标。SQL注入、跨站脚本(XSS)、跨站请求伪造(CSRF)、文件包含漏洞、反序列化攻击等安全问题在PHP应用中屡见不鲜。理解攻击的原理和防御策略,是每个PHP开发者的必修课。
参考:https://rvxif.cn/category/yellow-tea.html
SQL注入是最著名的Web安全漏洞。攻击者在用户输入中插入SQL代码,改变原SQL语句的语义,从而读取、修改甚至删除数据库数据。防御SQL注入的第一原则是:永远不要直接拼接用户输入到SQL字符串中。
参数化查询(预处理语句)是防御SQL注入的金标准。使用PDO或MySQLi的预处理语句,SQL语句和用户数据分开发送给数据库,数据永远不会被当作SQL代码执行。$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$id]);。预处理语句不仅安全,而且在多次执行时性能更好。
转义函数(mysqli_real_escape_string)是次优选择。转义函数只能防御特定字符集的注入,而且容易遗漏(如忘记转义、字符集配置错误)。转义函数不应作为主要防御手段,仅在预处理语句不可用时使用。
XSS(跨站脚本)攻击发生在应用程序将用户输入作为HTML输出时。攻击者注入恶意JavaScript代码,在受害者浏览器中执行,从而窃取Cookie、劫持会话、或篡改页面内容。
防御XSS的核心是输出编码。根据输出位置选择正确的编码函数:HTML正文中使用htmlspecialchars($string, ENT_QUOTES, 'UTF-8');HTML属性中使用htmlspecialchars;JavaScript字符串中使用json_encode或addslashes;CSS中使用preg_replace过滤危险字符;URL中使用urlencode。上下文感知的编码是关键——在错误上下文中使用错误的编码函数同样危险。
内容安全策略(CSP)是XSS的深度防御。CSP通过HTTP头告诉浏览器只允许加载指定来源的脚本、样式、图片等资源。Content-Security-Policy: default-src 'self'; script-src 'self' trusted-cdn.com。CSP可以阻止内联脚本的执行,即使攻击者注入了脚本,浏览器也会拒绝执行。
参考:https://rvxif.cn/category/white-tea.html
CSRF(跨站请求伪造)利用用户的已登录身份,诱使用户在不知情的情况下执行非本意的操作。攻击者诱导用户点击恶意链接或访问恶意网站,该网站向目标应用发起请求,浏览器会自动携带用户的Cookie。
防御CSRF的标准方法是CSRF Token。在表单中嵌入一个随机生成的Token,服务器验证Token的有效性。Token应绑定到用户会话,且每次请求都不同。现代PHP框架(Laravel、Symfony)内置了CSRF保护。
SameSite Cookie是CSRF的另一道防线。设置SameSite=Strict或Lax,浏览器在跨站请求时不会发送Cookie。session_set_cookie_params(['samesite' => 'Strict'])。SameSite是现代浏览器支持的特性,不能完全依赖,但可以作为补充。
文件包含漏洞发生在使用用户输入构建文件路径时。攻击者可以通过../../路径遍历读取敏感文件,或通过包含远程文件(如果允许)执行任意代码。
防御文件包含:禁用allow_url_include和allow_url_fopen;使用白名单控制可包含的文件;规范化路径(realpath)并检查是否在预期目录内;以及避免直接使用用户输入作为文件路径。
文件上传漏洞:攻击者上传可执行文件(如PHP脚本),然后通过访问上传文件触发执行。防御措施包括:验证文件类型(使用finfo_file检查MIME类型,不要依赖扩展名);将上传文件存储在Web根目录之外;为上传文件生成随机文件名;限制文件大小;以及禁用上传目录的脚本执行(使用.htaccess)。
反序列化攻击是PHP特有的严重漏洞。当unserialize()处理不可信数据时,攻击者可以构造恶意序列化字符串,在反序列化过程中触发wakeup、destruct等魔术方法,导致任意代码执行。
防御反序列化攻击:永远不要对不可信数据使用unserialize();使用json_decode()替代序列化(JSON没有代码执行风险);使用PHP 7的allowed_classes选项限制可反序列化的类;以及使用签名验证序列化数据的完整性。
命令注入:攻击者在系统命令中注入额外命令。exec("ping " . $ip)如果$ip是8.8.8.8; rm -rf /,后果不堪设想。
防御命令注入:使用escapeshellarg()和escapeshellcmd()转义参数;尽可能使用PHP内置函数替代系统命令(如使用filesystem函数代替ls);以及使用白名单验证输入。
会话安全:会话ID应该使用安全的随机数生成器(PHP 7的random_bytes());使用session_regenerate_id()在登录后重新生成会话ID;设置HttpOnly防止JavaScript读取Cookie;设置Secure标志确保Cookie仅通过HTTPS传输;设置合理的会话超时;以及避免将会话数据存储在可预测的位置。
密码存储:永远不要明文存储密码,也不要使用MD5或SHA1(太快,易被暴力破解)。使用password_hash()和password_verify(),它们使用bcrypt算法,自动加盐,且随硬件升级可调整成本因子。
错误处理与信息披露:生产环境应关闭display_errors,记录错误到日志而不是输出到浏览器。错误信息可能泄露数据库结构、文件路径、甚至密码。使用自定义错误处理器捕获错误,输出友好的错误页面。
HTTPS强制:所有生产应用应该强制使用HTTPS,通过.htaccess或应用中间件重定向HTTP到HTTPS。启用HSTS(HTTP Strict Transport Security)告诉浏览器永远使用HTTPS访问。
输入验证与输出编码的区别:输入验证(如验证邮箱格式)用于业务逻辑,不依赖它防御安全漏洞(因为攻击者可以绕过客户端验证)。输出编码才是防御XSS和注入的根本。
安全开发生命周期:安全不是事后的补丁,而是开发流程的一部分。包括:威胁建模(识别潜在攻击面)、安全编码规范、代码审查(关注安全漏洞)、自动化安全测试(SAST、DAST)、以及漏洞赏金计划。
PHP安全是个庞大而复杂的领域。没有银弹——只有多层次防御(深度防御)才能构建相对安全的系统。安全是过程,不是状态;是持续的努力,不是一劳永逸的配置。
参考:https://rvxif.cn