十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CodeIgniter Security 类实战:XSS 过滤、CSRF 防护与安全工具方法深度解析

CodeIgniter Security 类实战:XSS 过滤、CSRF 防护与安全工具方法深度解析 CodeIgniter Security 类实战XSS 过滤、CSRF 防护与安全工具方法深度解析【免费下载链接】CodeIgniterOpen Source PHP Framework (originally from EllisLab)项目地址: https://gitcode.com/gh_mirrors/co/CodeIgniter本篇指南以 CodeIgniter 框架的 Security Class安全类为核心讲解如何基于 system/core/Security.php 构建安全应用包括跨站脚本XSS过滤、跨站请求伪造CSRF防护、文件名净化、HTML 实体解码与安全随机字节生成。读者读完可掌握CI_Security全部公开方法的正确调用方式、application/config/config.php中相关配置项的取值与影响以及底层实现原理与官方测试的验证结论。Security Class 概览Security Class 是 CodeIgniter 的框架核心类之一职责是对输入数据进行安全处理帮助开发者构建安全的应用。它封装了两大类能力输入净化XSS 过滤、文件名净化、实体解码等防御恶意输入请求防伪CSRF Token 的生成、验证与 Cookie 管理。在控制器中Security 类以单例属性$this-security的形式自动加载无需手动实例化$this-security-xss_clean($data);框架同时提供了 system/helpers/security_helper.php 作为全局函数包装层加载该辅助函数文件后可直接调用xss_clean()、sanitize_filename()、strip_image_tags()、encode_php_tags()它们内部都通过get_instance()-security委托给CI_Security实例执行例如function xss_clean($str, $is_image FALSE) { return get_instance()-security-xss_clean($str, $is_image); }XSS 过滤XSS跨站脚本攻击试图在应用中注入并执行 JavaScript 或其他恶意代码用以劫持 Cookie、篡改页面或执行其他恶意操作。CodeIgniter 内置的 XSS 过滤机制会识别常见攻击技术一旦发现被禁止的内容就将其转换为 HTML 字符实体character entities使其在浏览器中变为无害文本。基本用法xss_clean()通过xss_clean()方法过滤数据$data $this-security-xss_clean($data);该方法接受字符串或字符串数组作为输入数组会递归处理每个元素返回净化后的数据。以官方测试 tests/codeigniter/core/Security_test.php 为例输入Hello, i try to scriptalert(Hack);/script your site过滤结果为Hello, i try to [removed]alert#40;Hack#41;;[removed] your site可以看到script标签被整体替换为[removed]alert(中的左括号被转义为#40;使攻击载荷彻底失去可执行能力。图片上传安全检测is_image 参数xss_clean()的可选第二参数$is_image专用于上传文件的 XSS 检测。当设为TRUE时方法不再返回改写后的字符串而是返回布尔值图片安全返回TRUE若包含浏览器可能尝试执行的恶意信息则返回FALSEif ($this-security-xss_clean($file, TRUE) FALSE) { // file failed the XSS test }从源码 system/core/Security.php 可以看出其判定原理处理完成后将净化结果与转换前的字符串快照做全等比较若内容在过滤过程中发生了任何改动说明其中存在被清除的恶意代码即判定为不安全。测试用例 test_xss_clean_image_invalid 验证了img srcjavascript:alert(...)这类注入会被拦截并返回FALSE。另外图片模式下对 PHP 标签的转义策略也不同普通文本会同时转义?和?而图片因常含 PHP 短标签只转义长标签?php源码见 system/core/Security.php。重要xss_clean()不适合用于过滤 HTML 属性值对属性值请改用框架提供的全局函数html_escape()以避免破坏属性结构与引入二次注入风险。底层过滤流程源码级从 system/core/Security.php 可以看到xss_clean()的执行链相当严密依次执行数组递归若输入是数组逐个元素递归过滤移除不可见字符调用remove_invisible_characters()清除控制字符与十六进制编码字符URL 解码用rawurldecode()循环解码保留加号语义拆穿%77%77%77之类的编码伪装同时用_urldecodespaces()处理被空格打断的百分号编码实体转 ASCII仅在标签内部把字符实体解码为可识别的 ASCII使后续黑名单匹配可靠Tab 转空格防止ja\tvascript这类用制表符拆词的绕过黑白名单替换_do_never_allowed()依次应用$_never_allowed_str如document.cookie、.innerHTML、!--、%等字符串替换与$_never_allowed_regex如javascript\s*:、vbscript\s*:、data:...base64...等正则替换为[removed]PHP 标签转义?、?转为实体压缩拆散单词用_compact_exploded_words()将j a v a s c r i p t这类被空格拆散的关键词重新拼回便于后续规则命中JS 链接与图片清理_js_link_removal()与_js_img_removal()负责清掉href/src中的javascript:、window.、.cookie等危险内容净化 naughty HTML_sanitize_naughty_html()对alert、iframe、object、svg、style等危险标签整体转义对on\w、style、formaction等邪恶属性替换为xssremoved危险函数转义将eval(...)、alert(...)、system(...)等函数调用的括号转为#40;/#41;并同样处理eval...这类模板字符串形式的tag functions最终清理再次执行_do_never_allowed()兜底。该过滤器基于 Bitflux 的 XSS 防护思路并参考了 ha.ckers.org 的 XSS 漏洞清单编写源码注释也坦承没有任何过滤器是 100% 万无一失的因此建议只用于数据提交阶段而非一般性的运行时处理。与 Input 类配合使用过滤器的典型使用场景是与 Input 类结合。查看 system/core/Input.php 可知get()、post()、cookie()、server()、input_stream()等方法都支持第二参数$xss_clean传入TRUE时数据会先经$this-security-xss_clean($value)净化再返回$this-input-post(some_data, TRUE); // 取 POST 值并做 XSS 过滤 $this-input-get(page, TRUE); // 取 GET 值并做 XSS 过滤相关辅助方法CI_Security还提供两个与 XSS 配套的方法xss_hash()生成并缓存一个 32 位十六进制随机串源码 system/core/Security.php 显示它由get_random_bytes(16)生成失败时退化为md5(uniqid(mt_rand(), TRUE))。它在内部用于 URL 中 GET 参数的保护标记_decode_entity()一般不直接在业务中使用strip_image_tags($str)从字符串中提取img标签的src值返回图片 URL。在security_helper.php中同样有同名全局函数。跨站请求伪造CSRF防护CSRF 攻击利用浏览器自动携带 Cookie 的特性诱导已登录用户在不知情的情况下向目标站点提交伪造请求。CodeIgniter 通过同步令牌模式防护为每次会话生成随机 Token写入 Cookie 并注入表单提交时校验两者一致。启用与核心配置在application/config/config.php中修改以下配置即可启用默认关闭$config[csrf_protection] TRUE;配置文件 application/config/config.php 中定义了整套 CSRF 参数及其默认值配置项默认值说明csrf_protectionFALSE是否启用 CSRF 防护接受用户数据时强烈建议开启csrf_token_namecsrf_test_name隐藏表单字段名Token 名称csrf_cookie_namecsrf_cookie_name存放 Token 的 Cookie 名称csrf_expire7200Token 过期时间秒默认 2 小时csrf_regenerateTRUE每次提交后是否重新生成 Tokencsrf_exclude_urisarray()跳过 CSRF 校验的 URI 白名单从构造函数system/core/Security.php可以看到当csrf_protection为TRUE且非 CLI 环境时Security 类会自动读取上述配置、生成 hash 并立即执行csrf_verify()。注意csrf_cookie_name还会拼接cookie_prefix前缀。表单自动注入form_open()如果使用表单辅助函数form_open()会自动在表单内插入 CSRF 隐藏字段无需手工编写。查看 system/helpers/form_helper.php 的实现当csrf_protection开启、表单 action 指向本站base_url()且非 GET 方法时自动追加隐藏输入框。值得一提的是该实现还内建了针对BREACH 攻击的防护用get_random_bytes(1)生成一个随机的白噪声以随机数量的空格包裹 Token 输入框负值前缀、正值后缀使压缩类攻击难以从响应长度中提取 Token 信息。手动构建表单与 AJAX 请求不使用form_open()时可通过get_csrf_token_name()与get_csrf_hash()手动获取字段名和值$csrf array( name $this-security-get_csrf_token_name(), hash $this-security-get_csrf_hash() ); ... input typehidden name?$csrf[name];? value?$csrf[hash];? /这两个方法同样适用于发送合法的 AJAX POST 请求在 JavaScript 中读取 Token 名与哈希随请求体一起提交即可通过服务端校验。Token 再生策略csrf_regenerateToken 可以每次提交后重新生成默认行为也可以在 CSRF Cookie 生命周期内保持不变$config[csrf_regenerate] TRUE;默认的每次再生提供更严格的安全保障但可能带来可用性问题——旧 Token 立即失效导致前进/后退导航、多标签页/多窗口、异步请求等场景下校验失败。若你的应用对这些场景敏感可将其设为FALSE换取体验csrf_expire仍会控制 Token 的最终过期时间。白名单排除csrf_exclude_uris某些 URI 需要豁免 CSRF 校验典型场景是接收外部 POST 内容如第三方回调的 API 端点$config[csrf_exclude_uris] array(api/person/add);该配置支持大小写不敏感的正则表达式$config[csrf_exclude_uris] array( api/record/[0-9], api/title/[a-z] );源码 system/core/Security.php 显示匹配逻辑为preg_match(#^.$excluded.$#i.(UTF8_ENABLED ? u : ), $uri-uri_string())——即以^、$锚定整段 URI 字符串并附加i忽略大小写修饰符UTF-8 环境下再加u修饰符。验证流程源码级csrf_verify()system/core/Security.php的完整流程如下非 POST 请求不校验直接调用csrf_set_cookie()设置 Cookie 后返回白名单检查若当前 URI 命中csrf_exclude_uris跳过校验直接返回Token 比对校验$_POST[token_name]与$_COOKIE[cookie_name]同时存在且均为字符串并用hash_equals()做恒定时间比较防止时序侧信道攻击清理无论结果如何先从$_POST中 unset 掉 Token避免污染后续业务数据再生若csrf_regenerate为TRUE清除旧 Cookie 并重置内部 hash重新下发_csrf_set_hash()生成/复用 hashcsrf_set_cookie()重新写入 Cookie失败处理校验不通过则调用csrf_show_error()通过show_error(The action you have requested is not allowed., 403)返回 403 错误。_csrf_set_hash()system/core/Security.php还有一个细节若 Cookie 中已存在格式合法的 32 位十六进制 hashpreg_match(#^[0-9a-f]{32}$#iS)会直接复用而不是重新生成——因为页面可能内嵌子页面每次加载都重新生成会导致校验失败。新 hash 则由get_random_bytes(16)的二进制结果经bin2hex()得到。Cookie 的写入csrf_set_cookie()system/core/Security.php遵循以下规则过期时间为time() $csrf_expire若cookie_secure为TRUE但当前不是 HTTPS 连接拒绝下发并返回FALSEPHP 7.3 使用setcookie()的数组参数形式否则手工构造Set-Cookie头Cookie 的path、domain、secure、httponly均取自对应的 cookie 配置项SameSite属性被硬编码为Strict进一步缓解 CSRF 风险。文件名净化sanitize_filename()sanitize_filename()用于清理用户输入的文件名防止目录遍历directory traversal攻击及其他安全威胁特别适合处理用户上传/提交的文件名$filename $this-security-sanitize_filename($this-input-post(filename));默认情况下会剥离所有路径成分/、./均被移除只保留纯文件名。若业务允许用户输入包含相对路径如file/in/some/approved/folder.txt可将第二参数设为TRUE$filename $this-security-sanitize_filename($this-input-post(filename), TRUE);其实现system/core/Security.php基于公开属性$filename_bad_chars黑名单——包含../、!--、--、、、引号、、$、#、花括号、%20、%22、%3c、%253c等双重编码变体见 system/core/Security.php处理过程为先移除不可见字符再循环执行str_replace直至字符串不再变化最后stripslashes()去除转义。官方测试 test_sanitize_filename 验证了输入./!--foo--会输出安全的foo。HTML 实体解码entity_decode()entity_decode()与 PHP 原生html_entity_decode()在ENT_COMPAT模式下行为类似额外优势是能识别不带分号结尾的 HTML 实体——部分浏览器允许省略分号并仍能正确解析而html_entity_decode()不会转换这类实体源码注释见 system/core/Security.php。$decoded $this-security-entity_decode($encoded);第二参数$charset指定输入字符串字符集留空时使用配置的$config[charset]默认UTF-8。实现system/core/Security.php通过get_html_translation_table()建立实体映射表循环处理字母实体、数字实体与 UTF-16 双字节实体直至字符串不再变化。官方测试 test_entity_decode 覆盖了lt;divgt;、colon;、NewLine;等实体的解码并确认foo非实体不会被误转换。安全随机字节get_random_bytes()get_random_bytes($length)是获取安全随机字节的便捷方法用于生成 CSRF 与 XSS Token。文档描述其候选来源依次为mcrypt_create_iv()、/dev/urandom、openssl_random_pseudo_bytes()结合当前仓库源码 system/core/Security.php 可以确认实际优先级为PHP 7 内置的random_bytes()最优先失败则直接返回FALSE不做降级mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)读取/dev/urandom用stream_set_chunk_size()限制熵消耗openssl_random_pseudo_bytes($length)全部不可用时返回FALSE。方法会先校验$length为非空且为数字字符串否则返回FALSE测试 test_get_random_bytes 验证了非法长度返回FALSE。需要注意输出并不保证密码学安全它只是尽力而为的最佳尝试——但源码中调用方如_csrf_set_hash()、xss_hash()会在返回FALSE时退化为md5(uniqid(mt_rand(), TRUE))兼顾了极端环境下的可用性。类参考CI_Security 公开方法一览方法签名返回值用途xss_clean()xss_clean($str[, $is_image FALSE])mixed清除输入数据中的 XSS 漏洞$is_image为TRUE时对图片内容做安全检测安全返回TRUE检测到恶意数据返回FALSE。不可用于过滤 HTML 属性值请改用html_escape()sanitize_filename()sanitize_filename($str[, $relative_path FALSE])string净化文件名防御目录遍历$relative_path为TRUE时保留目录结构get_csrf_token_name()get_csrf_token_name()string返回 CSRF Token 名称即$config[csrf_token_name]get_csrf_hash()get_csrf_hash()string返回 CSRF 哈希值与get_csrf_token_name()配合用于手工构建表单或发送 AJAX POST 请求entity_decode()entity_decode($str[, $charset NULL])string类似html_entity_decode()的ENT_COMPAT模式但能识别无分号结尾的实体$charset留空时使用$config[charset]get_random_bytes()get_random_bytes($length)string获取随机字节流失败返回FALSE用于生成 CSRF 与 XSS Token输出不保证密码学安全此外CI_Security还公开了csrf_verify()、csrf_set_cookie()、csrf_show_error()、xss_hash()、strip_image_tags()等可覆写方法前两者适合在自定义扩展类中覆写以调整校验与下发逻辑。测试验证仓库在 tests/codeigniter/core/Security_test.php 中为 Security 类提供了完整的单元测试覆盖可在tests/目录下通过 PHPUnit 运行验证XSS 过滤test_xss_clean验证脚本标签与函数调用的净化test_xss_clean_string_array验证数组递归处理test_xss_clean_entity_double_encoded验证双重编码实体的拆解test_xss_clean_sanitize_naughty_html_tags与test_xss_clean_sanitize_naughty_html_attributes覆盖on*属性、fscommand、seekSegmentTime等边界场景CSRF 校验test_csrf_verifyGET 通过、test_csrf_verify_invalidPOST 无 Token 抛出 403 异常、test_csrf_verify_validPOST 携带正确 Token 通过、test_csrf_set_hash空 Cookie 名时仍能生成 hash其他方法test_entity_decode、test_sanitize_filename、test_strip_image_tags、test_get_random_bytes、test_xss_hash。这些测试既是对框架行为的权威定义也是理解各类方法边界条件的绝佳教材。小结Security Class 是 CodeIgniter 输入安全防线中的关键一环xss_clean()以多阶段、多策略的深度净化机制拦截 XSS 载荷CSRF 组件以Cookie 隐藏字段 hash_equals恒定时间比对 白名单 SameSiteStrict的组合提供开箱即用的防伪造能力sanitize_filename()、entity_decode()、get_random_bytes()则分别覆盖文件名、实体与随机数等安全细节。结合实际项目建议遵循文档与源码中反复强调的三条原则接受用户数据时开启 CSRF 防护、对提交数据使用 XSS 过滤、对 HTML 属性值一律使用html_escape()而非xss_clean()。【免费下载链接】CodeIgniterOpen Source PHP Framework (originally from EllisLab)项目地址: https://gitcode.com/gh_mirrors/co/CodeIgniter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表