首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

CodeIgniter 3 Security 類源碼深度解析:XSS 過濾、CSRF 防護與文件名淨化核心邏輯

     編輯:PHP 綜合知識

Security 類定位

`CI_Security` 是 CI3 內置的**安全防護核心**,提供:

  • **全局 XSS 過濾** (配置 `global_xss_filtering = TRUE`)
  • **CSRF 令牌驗證** (配置 `csrf_protection = TRUE`)
  • **手動調用方法**:`xss_clean()`、`sanitize_filename()`、`csrf_verify()` 等
  • **安全輔助函數**:`do_hash()`、`strip_image_tags()`、`encode_php_tags()`

核心屬性與初始化

<?php
class CI_Security {
    // XSS 令牌 (用於表單隱藏域)
    protected $_xss_hash = '';

    // CSRF 核心
    protected $_csrf_hash = '';          // 當前令牌值
    protected $_csrf_expire = 7200;      // 過期秒數 (2小時)
    protected $_csrf_token_name = 'ci_csrf_token'; // POST 字段名
    protected $_csrf_cookie_name = 'ci_csrf_token'; // Cookie 名
    protected $_csrf_protection = FALSE; // 是否啟用

    // XSS 黑名單:絕對禁止的字符串 (直接替換為 [removed])
    protected $_never_allowed_str = [
        'document.cookie' => '[removed]', 'document.write' => '[removed]',
        '.parentNode' => '[removed]', '.innerHTML' => '[removed]',
        'window.location' => '[removed]', '-moz-binding' => '[removed]',
        '<!--' => '<!--', '-->' => '-->', '<![CDATA[' => '<![CDATA[', '<comment>' => '<comment>'
    ];

    // XSS 黑名單:禁止的正則模式
    protected $_never_allowed_regex = [
        'javascript\s*:', 'expression\s*(\(|()', 'vbscript\s*:',
        'Redirect\s+302', '([\'"])?data\s*:[^\\1]*?base64[^\\1]*?,[^\\1]*?\\1?'
    ];

    public function __construct()
    {
        // 讀取配置啟用 CSRF
        if (config_item('csrf_protection') === TRUE) {
            foreach (['csrf_expire', 'csrf_token_name', 'csrf_cookie_name'] as $key) {
                if (($val = config_item($key)) !== FALSE) $this->{'_' . $key} = $val;
            }
            // Cookie 名加前綴
            if (config_item('cookie_prefix')) $this->_csrf_cookie_name = config_item('cookie_prefix') . $this->_csrf_cookie_name;
            $this->_csrf_set_hash(); // 生成/讀取令牌
        }
        log_message('debug', 'Security Class Initialized');
    }
}

CSRF 防護核心流程 (`csrf_verify()`)

<?php
public function csrf_verify()
{
    // 非 POST 請求:只設置 Cookie,不驗證
    if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') {
        return $this->csrf_set_cookie();
    }

    // 1. 檢查 POST 和 Cookie 中是否都有令牌
    if (!isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) {
        $this->csrf_show_error();
    }

    // 2. 比對令牌值
    if ($_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) {
        $this->csrf_show_error();
    }

    // 3. 驗證通過:清理 POST、銷毀舊 Cookie、生成新令牌、設置新 Cookie
    unset($_POST[$this->_csrf_token_name]);
    unset($_COOKIE[$this->_csrf_cookie_name]);
    $this->_csrf_set_hash();
    $this->csrf_set_cookie();

    log_message('debug', 'CSRF token verified');
    return $this;
}

// 設置 Cookie (HttpOnly, Secure, SameSite)
public function csrf_set_cookie()
{
    $expire = time() + $this->_csrf_expire;
    $secure = (config_item('cookie_secure') === TRUE) ? 1 : 0;
    // HTTPS 環境下強制 Secure
    if ($secure && (empty($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) === 'off')) return FALSE;

    setcookie(
        $this->_csrf_cookie_name,
        $this->_csrf_hash,
        $expire,
        config_item('cookie_path'),
        config_item('cookie_domain'),
        $secure,
        TRUE // HttpOnly
    );
    log_message('debug', 'CSRF cookie Set');
    return $this;
}

// 令牌生成策略:優先用 Cookie 中的合法令牌,否則生成新隨機 MD5
protected function _csrf_set_hash()
{
    if ($this->_csrf_hash === '') {
        if (isset($_COOKIE[$this->_csrf_cookie_name]) &&
            preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name])) {
            $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name]; // 復用合法令牌
        } else {
            $this->_csrf_hash = md5(uniqid(rand(), TRUE)); // 新生成
        }
    }
    return $this->_csrf_hash;
}

關鍵設計亮點:

  1. 雙重提交 Cookie 模式:令牌同時存在 POST 隱藏域和 HttpOnly Cookie,驗證兩者一致。
  2. 每次請求輪換令牌:驗證通過後立即生成新令牌並設置 Cookie,防止令牌重放。
  3. 非 POST 自動下發 Cookie**:GET 頁面自動種下令牌 Cookie,表單提交時自動攜帶。
  4. 支持 SameSite/HttpOnly/Secure**:現代瀏覽器防禦屬性全覆蓋。

XSS 過濾核心 (`xss_clean()`)

採用**多層次、迭代式**過濾鏈,核心步驟:

<?php
public function xss_clean($str, $is_image = FALSE)
{
    // 1. 數組遞歸處理
    if (is_array($str)) { /* 遞歸調用 */ return $str; }

    // 2. 移除不可見控制字符
    $str = remove_invisible_characters($str);

    // 3. 實體驗證/修復 (& -> &amp; 等)
    $str = $this->_validate_entities($str);

    // 4. URL 解碼 (防雙重編碼繞過)
    $str = rawurldecode($str);

    // 5. 屬性值實體轉換 (針對標籤內屬性)
    $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", [$this, '_convert_attribute'], $str);
    $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", [$this, '_decode_entity'], $str);

    // 6. 再次移除不可見字符
    $str = remove_invisible_characters($str);

    // 7. Tab 轉空格 (防 ja vascript 繞過)
    $str = str_replace("\t", ' ', $str);

    // 8. 記錄轉換後字符串 (用於圖片模式比對)
    $converted_string = $str;

    // 9. 直接替換絕對禁止字符串/正則
    $str = $this->_do_never_allowed($str);

    // 10. PHP 標簽安全化
    if ($is_image) {
        $str = preg_replace('/<\?(php)/i', '<?\\1', $str); // 只處理長標簽
    } else {
        $str = str_replace(['<?', '?>'], ['<?', '?>'], $str); // 實體化
    }

    // 11. 壓縮分散字符 (j a v a s c r i p t -> javascript)
    $words = ['javascript','expression','vbscript','script','base64','applet','alert','document','write','cookie','window'];
    foreach ($words as $word) {
        $pattern = '#' . implode('\s*', str_split($word)) . '(\W)#is';
        $str = preg_replace_callback($pattern, [$this, '_compact_exploded_words'], $str);
    }

    // 12. 迭代清理標籤屬性中的 JS (href/src/on*)
    do {
        $original = $str;
        if (preg_match("/<a/i", $str)) $str = preg_replace_callback("#<a\s+([^>]*?)(>|$)#si", [$this, '_js_link_removal'], $str);
        if (preg_match("/<img/i", $str)) $str = preg_replace_callback("#<img\s+([^>]*?)(\s?/?>|$)#si", [$this, '_js_img_removal'], $str);
        if (preg_match("/script/i", $str) || preg_match("/xss/i", $str)) $str = preg_replace("#<(/*)(script|xss)(.*?)>#si", '[removed]', $str);
    } while ($original !== $str);

    // 13. 移除危險屬性 (on*, style, xmlns, formaction)
    $str = $this->_remove_evil_attributes($str, $is_image);

    // 14. 標籤白名單機制:危險標籤實體化
    $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss';
    $str = preg_replace_callback('#<(/*\s*)(' . $naughty . ')([^><]*)([><]*)#is', [$this, '_sanitize_naughty_html'], $str);

    // 15. 危險函數調用括號實體化 (eval( -> eval()
    $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', '\\1\\2(\\3)', $str);

    // 16. 最後再跑一遍絕對禁止列表
    $str = $this->_do_never_allowed($str);

    // 17. 圖片模式:比對是否有實質變化
    if ($is_image === TRUE) {
        return ($str === $converted_string) ? TRUE : FALSE;
    }

    log_message('debug', 'XSS Filtering completed');
    return $str;
}

關鍵子方法解析

1. `_remove_evil_attributes` - 移除危險屬性

  • 目標:`on*` (所有事件)、`style` (CSS expression)、`xmlns`、`formaction`
  • 圖片模式下保留 `xmlns` (Photoshop JFIF 元數據)
  • 使用複雜正則匹配屬性=值 (帶引號/不帶引號),在標籤內替換為空

2. `_js_link_removal` / `_js_img_removal` - 鏈接/圖片屬性 JS 清理

3. `_sanitize_naughty_html` - 危險標籤實體化

將 `

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved