Slim-4.x php审计 报错分析

00x1 错误链分析

cpp 复制代码
[Thu Mar 12 11:40:55 2026] [::1]:49795 Accepted
//错误类型
//Uncaught Slim\Exception\HttpNotFoundException
//错误信息
//Not found.
//触发文件
//in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
[Thu Mar 12 11:40:55 2026] PHP Fatal error:  Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
//问题链
Stack trace:
//从下到上
//5-0
//0就是最终报错源,调用到这一步
//真正的异常抛出行 是堆栈末尾的 thrown in ... RoutingMiddleware.php on line 76。
//也就是说,#0 代表调用链的最后一环,而 thrown in 那一行才是异常真正被 throw 的位置。
//也就是说0是最终报错的最后入口,最底下的位置才是报错点
#0 C:\Users\jc\Desktop\Slim-4.x\Slim\Routing\RouteRunner.php(62): Slim\Middleware\RoutingMiddleware->performRouting()
#1 C:\Users\jc\Desktop\Slim-4.x\Slim\MiddlewareDispatcher.php(73): 

//启动前自动注入RouteRunner
//$this->tip:这是 MiddlewareDispatcher 类的一个属性,它存储了一个对象。这个对象在程序启动时被设置为 RouteRunner 的实例。
Slim\Routing\RouteRunner->handle()
#2 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(209): Slim\MiddlewareDispatcher->handle()
#3 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(193): Slim\App->handle()
//错误链
#4 C:\Users\jc\Desktop\Slim-4.x\public\index.php(15): Slim\App->run()

//最开始,入口,thrown in错误抛出,[Thu Mar 12 11:40:55 2026] [::1]:44730 [500]: GET /favicon.ico触发错误请求
//没有favicon.ico,in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76触发错误
#5 {main}
  thrown in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php on line 76
[Thu Mar 12 11:40:55 2026] [::1]:44730 [500]: GET /favicon.ico - Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76


[Thu Mar 12 11:40:55 2026] [::1]:44730 Closing
[Thu Mar 12 11:40:55 2026] [::1]:49795 [200]: GET /
php 复制代码
 public function performRouting(ServerRequestInterface $request): ServerRequestInterface
    {//这里
//把路由解析器对象存入请求
        $request = $request->withAttribute(RouteContext::ROUTE_PARSER, $this->routeParser);
//执行真正的路由解析:根据当前请求的路径、方法等信息,匹配定义好的路由,返回一个包含匹配结果的对象。
        $routingResults = $this->resolveRoutingResultsFromRequest($request);
//从结果对象中取出状态码(例如 FOUND、NOT_FOUND、METHOD_NOT_ALLOWED),为下一步的流程分支提供依据。
        $routeStatus = $routingResults->getRouteStatus();
//存档
//调用当前对象
        $request = $request->withAttribute(RouteContext::ROUTING_RESULTS, $routingResults);

        switch ($routeStatus) {
            case RoutingResults::FOUND:
                $routeArguments = $routingResults->getRouteArguments();
                $routeIdentifier = $routingResults->getRouteIdentifier() ?? '';
                $route = $this->routeResolver
                    ->resolveRoute($routeIdentifier)
                    ->prepare($routeArguments);
                return $request->withAttribute(RouteContext::ROUTE, $route);
//出错
//当客户端尝试访问网站的某个页面或资源时,如果服务器无法找到并提供相应的信息,就会返回"Not Found"错误,即404状态码。这意味着所请求的URL可能已失效或者路径已更改,导致服务器无法正常响应。
//::访问常量
            case RoutingResults::NOT_FOUND:
//抛出错误
                throw new HttpNotFoundException($request);

            case RoutingResults::METHOD_NOT_ALLOWED:
                $exception = new HttpMethodNotAllowedException($request);
                $exception->setAllowedMethods($routingResults->getAllowedMethods());
                throw $exception;

            default:
                throw new RuntimeException('An unexpected error occurred while performing routing.');
        }
    }

00x1 xss

php 复制代码
$app->get('/xss', function ($request, $response) {
    $name = $request->getQueryParams()['name'] ?? 'guest';
    $response->getBody()->write("Hello, " . $name);
    return $response;
});

后台

php 复制代码
[Thu Mar 12 15:57:19 2026] [::1]:20841 Accepted
[Thu Mar 12 15:57:19 2026] [::1]:20841 [200]: GET /xss?name=%3Cscript%3Ealert(%27%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E5%BC%B9%E7%AA%97%27);%3C/script%3E
[Thu Mar 12 15:57:19 2026] [::1]:20841 Closing
[Thu Mar 12 15:57:19 2026] [::1]:45458 Accepted
//与之前一样
[Thu Mar 12 15:57:19 2026] PHP Fatal error:  Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76
Stack trace:
#0 C:\Users\jc\Desktop\Slim-4.x\Slim\Routing\RouteRunner.php(62): Slim\Middleware\RoutingMiddleware->performRouting()
#1 C:\Users\jc\Desktop\Slim-4.x\Slim\MiddlewareDispatcher.php(73): Slim\Routing\RouteRunner->handle()
#2 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(209): Slim\MiddlewareDispatcher->handle()
#3 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(193): Slim\App->handle()
#4 C:\Users\jc\Desktop\Slim-4.x\public\index.php(20): Slim\App->run()
//浏览器行为
#5 {main}
  thrown in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php on line 76
[Thu Mar 12 15:57:19 2026] [::1]:45458 [500]: GET /.well-known/appspecific/com.chrome.devtools.json - Uncaught Slim\Exception\HttpNotFoundException: Not found. in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php:76

Stack trace:
#0 C:\Users\jc\Desktop\Slim-4.x\Slim\Routing\RouteRunner.php(62): Slim\Middleware\RoutingMiddleware->performRouting()
#1 C:\Users\jc\Desktop\Slim-4.x\Slim\MiddlewareDispatcher.php(73): Slim\Routing\RouteRunner->handle()
#2 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(209): Slim\MiddlewareDispatcher->handle()
#3 C:\Users\jc\Desktop\Slim-4.x\Slim\App.php(193): Slim\App->handle()
#4 C:\Users\jc\Desktop\Slim-4.x\public\index.php(20): Slim\App->run()
//两次是因为浏览器插件拓展
#5 {main}
  thrown in C:\Users\jc\Desktop\Slim-4.x\Slim\Middleware\RoutingMiddleware.php on line 76
[Thu Mar 12 15:57:19 2026] [::1]:45458 Closing
[Thu Mar 12 15:57:21 2026] [::1]:32562 Accepted
//收到的请求
[Thu Mar 12 15:57:21 2026] [::1]:32562 [200]: GET /xss?name=%3Cscript%3Ealert(%27%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E5%BC%B9%E7%AA%97%27);%3C/script%3E
[Thu Mar 12 15:57:21 2026] [::1]:32562 Closing

xss是浏览器的渲染导致的,这里没有防护直接渲染

00x2 this规则

this中的值是这个类的全部属性,如上

00x3 跳转规则

复制代码
serverRequest来自

指向Slim\Psr7\Request(前面有配置,路由(实例化)调用类)

跳转后到了这里,Request是跳转类名,

00x4 debug name = request->getQueryParams()['name'] ?? 'guest';

php 复制代码
public function getQueryParams(): array
    {
//serverRequest成员变量,保存了PSR-7的request对象
//调用serverRequest对象的getQueryParams()方法
//地址指向
//获取xss后面的值以数组形式返回
        $queryParams = $this->serverRequest->getQueryParams();
//检查是否为数组类型,是否为空
//PHP的empty()函数可判断标记是否为空字符串、0、"0"、NULL、FALSE或空数组
//正常返回
        if (is_array($queryParams) && !empty($queryParams)) {
//返回
            return $queryParams;
        }
//
        $parsedQueryParams = [];
//
        parse_str($this->serverRequest->getUri()->getQuery(), $parsedQueryParams);

        return $parsedQueryParams;
    }

这是

  1. string $method

请求类型

存储的值:"GET"

含义:HTTP 请求方法,如 GET、POST、PUT 等。它决定了请求的意图。

  1. UriInterface $uri

uri实例化地址

存储的值:$lim\Psr7\Uri 对象

含义:封装了请求的 URI(统一资源标识符),包括 scheme(如 https)、host、port、path、query 字符串等。通过该对象可以获取 URL 的各个部分。

  1. string $requestTarget

请求目标查询部分

存储的值:null

含义:请求目标(通常指 URI 的路径和查询部分,如 /index.php?foo=bar)。PSR-7 允许显式设置请求目标,若为 null 则通常从 $uri 推导。

  1. array|null $queryParams

解析后存储数组

存储的值:null

含义:解析后的查询参数(来自 URL 中 ? 后面的部分)。如果尚未解析或设置,则为 null。一旦通过 withQueryParams() 设置或内部解析,就会变成一个关联数组(如 ['foo' => 'bar'])。

  1. array $cookies

存储的值:array[5](包含 5 个 cookie)

含义:从请求头 Cookie 中解析出的所有 cookie 键值对。数组的键是 cookie 名,值是 cookie 值。

  1. array $serverParams

存储的值:array[31](包含 31 个元素)

含义:服务器环境变量,通常对应 PHP 的 _SERVER 超全局数组。它包含了诸如请求方法、URI、客户端 IP、脚本路径等底层信息。PSR-7 要求将 _SERVER 数据传入请求对象。

  1. array $attributes

存储的值:array[4](包含 4 个属性)

含义:用户自定义的请求属性。这些属性不是 HTTP 协议的一部分,而是框架或中间件用来在请求生命周期中传递额外数据的"附件"。例如,可以存储路由参数、身份验证结果等。可以通过 withAttribute() 添加,getAttribute() 获取。

  1. array|null|object $parsedBody

存储的值:null

含义:解析后的请求体。对于 POST、PUT 等包含请求体的请求,可以解析为数组或对象。例如,JSON 请求体解析后可能是一个数组或 stdClass 对象。未解析或没有请求体时为 null。

  1. array $uploadedFiles

存储的值:array[0](空数组)

含义:上传的文件信息,符合 PSR-7 UploadedFileInterface 的对象数组。它对应于 $_FILES 的结构,但经过规范化处理。这里为空表示没有文件上传。

返回值

php 复制代码
 public function getQueryParams(): array
    {
//获取get信息
        if (is_array($this->queryParams)) {
            return $this->queryParams;
        }

        if ($this->uri === null) {
            return [];
        }

        //处理
        //2
        //3 
//真正执行
//$this->uri->getQuery()的值是name=xxxxxxx
        parse_str($this->uri->getQuery(), $this->queryParams);
//返回数组
        return is_array($this->queryParams) ? $this->queryParams : [];
    }
php 复制代码
//2转化string
//:string返回类型
public function getQuery(): string
    {
//返回xss后面的值
        return $this->query;
    }
php 复制代码
 public function getQueryParams(): array
    {
//3
//url转义
        $queryParams = $this->serverRequest->getQueryParams();
//是数组,不为空
        if (is_array($queryParams) && !empty($queryParams)) {
            return $queryParams;
        }

        $parsedQueryParams = [];
        parse_str($this->serverRequest->getUri()->getQuery(), $parsedQueryParams);

        return $parsedQueryParams;
    }

由上可见,以上代码毫无过滤

00x5 debug response-\>getBody()-\>write("Hello, " . name);

php 复制代码
public function getBody(): StreamInterface
    {
        return $this->response->getBody();
    }
php 复制代码
public function getBody(): StreamInterface
    {
        return $this->body;
    }

常量 FSTAT_MODE_S_IFIFO

· 值:0010000(八进制)

· 含义:这是一个文件模式常量,来自 Unix/Linux 的文件类型定义。它代表 FIFO(命名管道) 类型,用于 fstat() 返回的模式中,以判断流是否为管道。这个常量通常在内部用来检测流类型,以便正确处理(例如管道不可 seek)。


属性

protected $stream

· 当前值:resource id='130' type='stream'

resource id='130',唯一流编号,stream资源类型声明

· 含义:底层的 PHP 流资源。这是该对象包装的核心,一个通过 fopen()、popen() 等创建的流资源。所有实际的 I/O 操作最终都通过这个资源进行。

protected ?array $meta

· 当前值:未显示(可能为 null)

· 含义:流的元数据,通过 stream_get_meta_data($this->stream) 获得。它包含流的信息,如模式、是否阻塞、超时设置、包装器类型等。延迟加载,首次调用 getMetadata() 时填充。

protected ?bool $readable = null

· 当前值:null

· 含义:流是否可读。通过分析流模式(如 r+, w+ 等)判断,但不会立即计算,而是在首次需要时通过检测模式设置。null 表示尚未检测。

protected ?bool $writable = null

· 当前值:null

· 含义:流是否可写。类似 readable,通过流模式判断,延迟检测。

protected ?bool $seekable = null

· 当前值:null

· 含义:流是否可定位(seek)。检测流是否支持 fseek(),通常基于元数据中的 seekable 字段。null 表示未检测。

protected ?int $size = null

· 当前值:null

· 含义:流的总大小(字节数)。如果能通过 fstat() 获取大小(如普通文件),则存储该值。对于网络流或管道,可能为 null。

protected ?bool $isPipe = null

· 当前值:null

· 含义:流是否为管道(pipe)。通过 fstat() 的模式判断(与常量 FSTAT_MODE_S_IFIFO 比较)。管道通常不可 seek,且大小未知。null 表示尚未判断。

protected bool $finished = false

· 当前值:false

· 含义:流是否已结束。对于可读流,当尝试读取时检测到 EOF,此标志可能设为 true。用于某些优化或判断是否还有数据可读。

protected ?StreamInterface $cache

· 当前值:null

· 含义:缓存流。某些实现中,当需要多次读取流内容(如获取完整内容后再次读取),可能会将内容缓存到另一个流中。这个属性存储缓存的流对象,通常为 null 表示未启用缓存。


这些属性有什么用?

这些属性共同管理流的行为,避免重复调用系统函数(如 fstat()、stream_get_meta_data()),并缓存状态以提升性能。例如:

· 第一次调用 isReadable() 时,检测流模式并设置 $readable,后续直接返回该值。

· 调用 getSize() 时,若已设置 $size 则直接返回,否则尝试通过 fstat() 获取并缓存。

所有属性均为 protected,意味着它们只能在类内部访问,但调试器可以显示它们,方便开发者查看流的当前状态。

php 复制代码
public function write($string): int
    {
        $written = false;

        if ($this->isWritable() && $this->stream) {
            $written = fwrite($this->stream, $string);
        }

        if ($written !== false) {
            $this->size = null;
            return $written;
        }

        throw new RuntimeException('Could not write to stream.');
    }
php 复制代码
 public function isWritable(): bool
    {//检查流是否可写
        if ($this->writable === null) {
            $this->writable = false;
//底层的 PHP 流资源。这是该对象包装的核心,一个通过 fopen()、popen() 等创建的流资源。所有实际的 I/O 操作最终都通过这个资源进行。
//
            if ($this->stream) {
                $mode = $this->getMetadata('mode');

                if (is_string($mode) && (strstr($mode, 'w') !== false || strstr($mode, '+') !== false)) {
                    $this->writable = true;
                }
            }
        }

        return $this->writable;
    }
php 复制代码
public function getMetadata($key = null)
    {
        if (!$this->stream) {
            return null;
        }
//执行这一行
        $this->meta = stream_get_meta_data($this->stream);

        if (!$key) {
            return $this->meta;
        }
//meta,流的元数据,通过 stream_get_meta_data($this->stream) 获得。它包含流的信息,如模式、是否阻塞、超时设置、包装器类型等。延迟加载,首次调用 getMetadata() 时填充。
        return $this->meta[$key] ?? null;
    }
相关推荐
cuckooman1 小时前
Build Tools for Visual Studio 2022 如何下载
ide·visual studio
xiegwei1 小时前
Android 调起第三方导航
android
认真的小羽❅1 小时前
JavaScript完全指南:从入门到精通
开发语言·javascript·ecmascript
AirDroid_cn1 小时前
Android 15 :如何让特定应用通知仅在锁屏显示横幅?
android·智能手机
xuhaoyu_cpp_java1 小时前
JAVA线程安全类
java·开发语言
香水5只用六神2 小时前
【TIM】基本定时器定时实验(2)
c语言·开发语言·stm32·单片机·嵌入式硬件·mcu·学习
BatyTao2 小时前
Python从零起步-数据容器
开发语言·python
承渊政道2 小时前
C++学习之旅【C++伸展树介绍以及红黑树的实现】
开发语言·c++·笔记·b树·学习·visual studio
郭涤生2 小时前
C++中设置函数与回调函数设值的性能差异及示例
开发语言·c++