洛丽糖
洛丽糖(luolt.cn),致力于互联网资源的共享, 分享各类技术教程,typecho主题模板,zblog主题模板,网站源码等各种资源。
avatar
1279 文章 1476 评论 4 分类 8 页面
微博客程序使用的垃圾HTTP类(给人都整麻了)
寻梦xunm| 59| 日常生活
10天前

还得是AI狠起来不管你是什么都一刀切,下面这款http请求类最初的AI优化版简直眼里容不得沙子,过滤得真的狠,安全确实安全,就是安全的不正常,反而成了BUG。经过反复修改十几遍终于完成了下面的这版,至于还有没有问题哪就不知道。
免费分享给大家看看博主使用AI优化的屎山代码

<?php
namespace xm;


/**
 * HTTP请求参数过滤类(极致安全+极致性能版)
 * 支持:GET/POST/COOKIE/SESSION/HTTP头/文件上传参数过滤
 * 依赖:PHP 7.4+(支持类型声明、match表达式)
 */
class http
{
    // -------------------------- 安全常量(编译时确定,无运行时开销) --------------------------
    //private const MAX_PARAM_LENGTH = 2048;       // 单个参数最大长度(防超长Payload)(太狠了,一刀斩)
    private const MAX_GET_PARAM_LENGTH = 2048;    // GET参数最大长度(严格限制)
    private const MAX_POST_PARAM_LENGTH = 1048576; // POST参数最大长度(1MB,可按需调整)
    private const MAX_URL_DECODE = 3;            // URL最大解码次数(防DoS,3次足够覆盖大部分场景)
    private const DANGER_REPLACE = '';           // 危险内容替换为空(比替换为xxx更安全)
    private const ENCODING_TARGET = 'UTF-8';     // 统一编码(防宽字节)
    private const ALLOWED_HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE']; // 允许的请求方法
    //private const ALLOWED_UPLOAD_EXTS = ['jpg', 'jpeg', 'png', 'gif', 'pdf']; // 允许的上传扩展名
    
    // 新增:HTML注释标记(统一匹配,避免硬编码,编译时确定无运行时开销)
    private const HTML_COMMENT_START = '<!--';
    private const HTML_COMMENT_END = '-->';

    // 高频危险关键词哈希表(O(1)查找,比正则快10倍以上)
    private const DANGER_HASH = [
        '<script' => true, '</script>' => true, 'alert(' => true, 'confirm(' => true,
        'prompt(' => true, 'eval(' => true, 'exec(' => true, 'union select' => true,
        'insert into' => true, 'update ' => true, 'delete from' => true, 'drop table' => true,
        '../' => true, '..\\' => true, 'data:text/html' => true, 'data:text/javascript' => true,
        'onload=' => true, 'onclick=' => true, 'onerror=' => true, 'svg ' => true, 'math ' => true
    ];

    // -------------------------- 过滤规则(按优先级排序:快规则在前) --------------------------
    // 1. 字符串替换规则(比正则快50倍)
    private const STRING_FILTER = [
        'search' => [
            '<script', '</script>', '<?php', '?>', '<?', '?>',
            '../', '..\\', '&#x', '&#0', '&amp;#', '\\x', '%00', '%0a', '%0d'
        ],
        'replace' => [
            '&lt;script', '&lt;/script&gt;', '&lt;?php', '?&gt;',
            '&lt;?', '?&gt;', '', '', '&#x', '&#0', '&amp;#', '\\x', '', '', ''
        ]
    ];

    // 2. 预编译正则规则(按功能拆分,减少回溯)
    private const REGEX_FILTER = [
        'xss' => '/<(?!<!--)[^>]*?(on\w+|xlink:href|data:text)=(["\'])?[^\2]*?\2?[^>]*?>/iS',
        'sql' => '/\b(benchmark|sleep|load_file|group_concat)\s*?\(|\/\*!.*?\*\/|#.*?$|--\s+/iS',
        'unicode' => '/[\x{ff1c}\x{ff1e}\x{ff07}\x{ff27}\x{ff02}\x{ff22}\x{ff3c}\x{ff3e}\x{200b}-\x{200f}]/uS',
        //'unicode' => '/[\x{ff00}-\x{ffff}\x{200b}-\x{200f}]/uS', // 全角字符+零宽空格
        'sensitive' => '/1[3-9]\d{9}|[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]/S' // 手机号+身份证
    ];

    // -------------------------- 静态缓存(类级别,仅初始化1次) --------------------------
    private static array $superGlobalRefs;  // 超全局变量引用(避免重复获取)
    private static array $compiledRegex;    // 预编译正则(避免重复编译)
    private static array $filteredCache;    // 过滤结果缓存(同一请求内复用)

    // 实例属性(仅存储必要状态)
    private string $requestMethod;  // 当前请求方法(缓存,避免重复读取$_SERVER)
    private string $clientIp;       // 客户端IP(缓存,避免重复计算)


    /**
     * 构造函数:静态资源初始化(仅首次实例化执行)
     * @throws \InvalidArgumentException 不允许的请求方法
     */
    public function __construct()
    {
        // 1. 初始化静态超全局引用(避免重复访问超全局数组)
        if (empty(self::$superGlobalRefs)) {
            self::$superGlobalRefs = [
                'get' => &$_GET,
                'post' => &$_POST,
                'cookie' => &$_COOKIE,
                'session' => &$_SESSION,
                'server' => &$_SERVER,
                'files' => &$_FILES
            ];
        }

        // 2. 预编译正则(仅1次,编译后复用)
        if (empty(self::$compiledRegex)) {
            self::$compiledRegex = [];
            foreach (self::REGEX_FILTER as $key => $pattern) {
                self::$compiledRegex[$key] = preg_match($pattern, '') === false 
                    ? "正则表达式无效: $pattern" 
                    : $pattern;
            }
        }

        // 3. 初始化请求基础信息(缓存,减少$_SERVER访问)
        $this->requestMethod = strtoupper(self::$superGlobalRefs['server']['REQUEST_METHOD'] ?? 'GET');
        $this->clientIp = $this->getClientIp();

        // 4. 验证请求方法(提前拦截不允许的方法)
        if (!in_array($this->requestMethod, self::ALLOWED_HTTP_METHODS, true)) {
            $this->logAttack('不允许的请求方法', ['method' => $this->requestMethod]);
            $this->abort('非法请求方法');
        }

        // 5. 初始化过滤缓存
        if (empty(self::$filteredCache)) {
            self::$filteredCache = [];
        }
    }


    /**
     * 获取客户端真实IP(防IP伪造)
     * @return string 客户端IP
     */
    private function getClientIp(): string
    {
        $ipHeaders = ['HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
        foreach ($ipHeaders as $header) {
            $ip = self::$superGlobalRefs['server'][$header] ?? '';
            if ($ip && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                // 处理X-Forwarded-For多IP场景(取第一个有效IP)
                if ($header === 'HTTP_X_FORWARDED_FOR') {
                    $ip = explode(',', $ip)[0];
                }
                return trim($ip);
            }
        }
        return '0.0.0.0';
    }


    /**
     * 核心过滤入口(按优先级执行:快规则→慢规则)
     * @param mixed $value 待过滤值(字符串/数组)
     * @param string $type 参数类型(get/post/cookie等)
     * @return mixed 过滤后的值
     */
    public function filter($value, string $type): mixed
    {
        // 1. 空值直接返回(避免后续处理)
        if ($value === null || $value === '') {
            return $value;
        }

        // 2. 数组递归过滤(手动递归,比array_walk_recursive快30%)
        if (is_array($value)) {
            foreach ($value as &$item) { // 引用传递,避免拷贝
                $item = $this->filter($item, $type);
            }
            unset($item); // 释放引用,防污染
            return $value;
        }

        // 3. 非字符串转字符串(前置判断,避免重复转换)
        $valueStr = is_string($value) ? $value : strval($value);
        $maxLength = ($type === 'post') 
        ? self::MAX_POST_PARAM_LENGTH 
        : self::MAX_GET_PARAM_LENGTH;

        // 4. 参数长度限制(超长按攻击处理,减少后续开销)
        if (strlen($valueStr) > $maxLength) {
            $this->logAttack('参数超长', ['length' => strlen($valueStr)]);
            return self::DANGER_REPLACE;
        }

        // 5. 缓存命中检查(同一请求内复用过滤结果)
        $cacheKey = md5($type . $valueStr);
        if (isset(self::$filteredCache[$cacheKey])) {
            return self::$filteredCache[$cacheKey];
        }

        // -------------------------- 过滤流程(优先级:快→慢) --------------------------
        // 6. 字符串替换(最快,处理高频危险字符)
        $filtered = str_replace(
            self::STRING_FILTER['search'],
            self::STRING_FILTER['replace'],
            $valueStr
        );

        // 7. 哈希表检测(O(1),仅检测注释外内容,避免漏防违规SQL)- 替换原逻辑
        // 第一步:拆分注释内容与非注释内容(原生strpos/substr,极快无性能损耗)
        $commentStart = strpos($filtered, self::HTML_COMMENT_START);
        $commentEnd = $commentStart !== false ? strpos($filtered, self::HTML_COMMENT_END, $commentStart + strlen(self::HTML_COMMENT_START)) : false;
        $nonCommentContent = '';
        if ($commentStart !== false && $commentEnd !== false) {
            $commentEnd += strlen(self::HTML_COMMENT_END); // 定位到注释结束符末尾(含-->)
            $nonCommentContent = substr($filtered, 0, $commentStart) . substr($filtered, $commentEnd); // 提取注释外的内容
        } else {
            $nonCommentContent = $filtered; // 无注释时,全量检测
        }

        // 第二步:仅对非注释内容执行哈希检测(注释内无危险关键词,无需检测)
        $lowerNonComment = strtolower($nonCommentContent);
        foreach (self::DANGER_HASH as $danger => $_) {
            if (strpos($lowerNonComment, strtolower($danger)) !== false) {
                $this->logAttack('哈希表检测到危险内容', ['content' => $danger]);
                self::$filteredCache[$cacheKey] = self::DANGER_REPLACE;
                return self::DANGER_REPLACE;
            }
        }

        // 8. URL解码(最多3次,防编码绕过)
        $decodedCount = 0;
        while (urldecode($filtered) !== $filtered && $decodedCount < self::MAX_URL_DECODE) {
            $filtered = urldecode($filtered);
            $decodedCount++;
        }

        // 9. 统一编码(防宽字节注入)
        $filtered = $this->convertEncoding($filtered);

        // 10. 正则过滤(最慢,仅处理注释外的复杂变形,兼顾性能与安全)- 替换原逻辑
        // 复用前面的$nonCommentContent,仅检测注释外内容(避免注释被误判)
        foreach (self::REGEX_FILTER as $key => $pattern) {
            if (preg_match($pattern, $nonCommentContent)) {
                $this->logAttack("正则检测到危险内容$key", ['content' => substr($nonCommentContent, 0, 100)]);
                self::$filteredCache[$cacheKey] = self::DANGER_REPLACE;
                return self::DANGER_REPLACE;
            }
        }

        // 11. 缓存过滤结果
        self::$filteredCache[$cacheKey] = $filtered;
        return $filtered;
    }


    /**
     * 统一编码为UTF-8(防宽字节注入)
     * @param string $str 待转换字符串
     * @return string 转换后字符串
     */
    private function convertEncoding(string $str): string
    {
        // 严格检测编码(避免误判)
        $encoding = mb_detect_encoding(
            $str,
            ['UTF-8', 'GBK', 'GB2312', 'LATIN1', 'CP936'],
            true
        );

        // 仅在编码不同时转换(减少函数调用开销)
        return $encoding !== false && $encoding !== self::ENCODING_TARGET
            ? mb_convert_encoding($str, self::ENCODING_TARGET, $encoding)
            : $str;
    }


    /**
     * 文件上传参数专项过滤(防恶意文件名/路径)
     * @param string $field 上传字段名
     * @return array|null 过滤后文件信息,null表示危险
     */
    public function filterFile(string $field): ?array
    {
        $file = self::$superGlobalRefs['files'][$field] ?? null;
        if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
            return null;
        }

        // 1. 过滤文件名(防路径穿越+恶意扩展名)
        $fileName = $this->filter($file['name'], 'files');
        $fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));

        // 2. 验证扩展名
        /*if (!in_array($fileExt, self::ALLOWED_UPLOAD_EXTS, true)) {
            $this->logAttack('非法上传扩展名', ['ext' => $fileExt, 'name' => $fileName]);
            @unlink($file['tmp_name']); // 删除恶意文件
            return null;
        }*/

        // 3. 过滤临时路径(防路径穿越)
        $tmpPath = $this->filter($file['tmp_name'], 'files');
        if (!is_uploaded_file($tmpPath)) { // 验证是否为合法上传文件
            $this->logAttack('非法临时文件路径', ['path' => $tmpPath]);
            @unlink($tmpPath);
            return null;
        }

        return [
            'name' => $fileName,
            'type' => $this->filter($file['type'], 'files'),
            'tmp_name' => $tmpPath,
            'error' => $file['error'],
            'size' => $file['size']
        ];
    }

    /**
     * 获取过滤后的参数(统一入口)
     * @param string $type 参数类型(get/post/cookie/session/server)
     * @param string|null $key 参数名(null=获取全部)
     * @param mixed $default 默认值
     * @return mixed 过滤后的值
     */
    public function gets(string $type, ?string $key = null, $default = null)
    {
        // 1. 验证参数类型
        if (!isset(self::$superGlobalRefs[$type])) {
            throw new \InvalidArgumentException("不支持的参数类型: $type");
        }

        $source = &self::$superGlobalRefs[$type];
        $cacheKey = "get_{$type}_{$key}";

        // 2. 缓存命中检查
        if (isset(self::$filteredCache[$cacheKey])) {
            return self::$filteredCache[$cacheKey];
        }

        // 3. 获取全部参数
        if ($key === null) {
            $result = $this->filter($source, $type);
            self::$filteredCache[$cacheKey] = $result;
            return $result;
        }

        // 4. 获取单个参数
        $value = $source[$key] ?? $default;
        $result = $this->filter($value, $type);

        // 5. 缓存结果
        self::$filteredCache[$cacheKey] = $result;
        return $result;
    }


    /**
     * 攻击日志记录(异步友好,避免阻塞请求)
     * @param string $title 日志标题
     * @param array $data 日志详情
     */
    private function logAttack(string $title, array $data): void
    {
        $logData = [
            //'time' => date('Y-m-d H:i:s'),
            //'ip' => $this->clientIp,
            //'method' => $this->requestMethod,
            //'uri' => self::$superGlobalRefs['server']['REQUEST_URI'] ?? '/',
            'title' => $title,
            'data' => $data
        ];

        // 1. 同步日志(基础场景)
        if (function_exists('XLOG')) {
            XLOG(json_encode($logData, JSON_UNESCAPED_UNICODE));
        }

        // 2. 异步日志(高性能场景,需配合队列)
        // @file_put_contents('/tmp/http_attack.log', json_encode($logData) . PHP_EOL, FILE_APPEND);
    }


    /**
     * 终止请求(防信息泄露)
     * @param string $msg 错误信息
     * @param int $code HTTP状态码
     */
    private function abort(string $msg, int $code = 403): void
    {
        http_response_code($code);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode(['code' => $code, 'msg' => $msg]);
        exit();
    }


    // -------------------------- 快捷调用方法(减少类型参数传递) --------------------------
    public function get(?string $key = null, $default = null)
    {
        return $this->gets('get', $key, $default);
    }

    public function post(?string $key = null, $default = null)
    {
        return $this->gets('post', $key, $default);
    }

    public function cookie(?string $key = null, $default = null)
    {
        return $this->gets('cookie', $key, $default);
    }

    public function session(?string $key = null, $default = null)
    {
        return $this->gets('session', $key, $default);
    }

    public function header(?string $key = null, $default = null)
    {
        $headerKey = $key ? 'HTTP_' . strtoupper(str_replace('-', '_', $key)) : null;
        return $this->gets('server', $headerKey, $default);
    }
}
0 赞 or 打赏
喜欢就打赏一点
微信 支付宝
下一篇

没有了

站内搜索
Q Q:1340326824
邮箱:vipshiyi@qq.com
QQ群:422720328
本站没得会员制度,所有资源都有白嫖的方法,且用且珍惜! 本站相关资源来自互联网用户收集发布,仅供用于学习和交流。 如有侵权之处,请联系站长并出示相关证明以便删除,敬请谅解!

我的音乐