微博客程序使用的垃圾HTTP类(给人都整麻了)
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', '�', '&#', '\\x', '%00', '%0a', '%0d'
],
'replace' => [
'<script', '</script>', '<?php', '?>',
'<?', '?>', '', '', '&#x', '�', '&#', '\\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);
}
}
没有了