-
背景
最近在对接某大厂,部署差不多了,但是在漏洞扫描环节有问题,前端是用vue代码写的。后端是php。发现前端路由可以拦截未登录的url。但是后端php接口不用登录就能访问,很危险 -
解决方法
一、创建 Auth 中间件
首先创建一个专门用于验证 Session 的中间件: 里面可以放开不用登录的接口
<?php
// 目录位置 app/middleware/Auth.php
namespace app\middleware;
use think\facade\Session;
use think\Response;
class Auth
{
// 定义不需要验证的接口路径
protected $exceptPaths = [
'Auth/login', // 登录接口
'Auth/logout', // 注册接口
'Auth/getCode', // 注册接口
'Index/getTitle', // 注册接口
// 可添加更多无需验证的接口...
];
public function handle($request, \Closure $next)
{
// 获取当前请求的路径(不含域名和参数)
$path = $request->pathinfo();
// 检查是否为排除的路径
if ($this->shouldPassThrough($path)) {
return $next($request); // 跳过验证,直接继续
}
// 检查Session中是否有登录用户信息
if (!Session::has(SESSION_LOGIN_KEY)) {
// 未登录,返回JSON错误响应
return json([
'code' => 0,
'msg' => '请先登录',
'data' => 401,
'url' => null,
'wait' => 3
]);
}
// 已登录,将用户信息注入请求对象,方便后续使用
$request->user = Session::get(SESSION_LOGIN_KEY);
// 继续执行后续请求处理
return $next($request);
}
// 判断请求是否应跳过验证
protected function shouldPassThrough($path)
{
foreach ($this->exceptPaths as $except) {
if (strpos($path, $except) === 0) {
return true;
}
}
return false;
}
}
二、注册中间件
有两种方式注册中间件,根据你的需求选择:
- 全局中间件(所有请求都验证)
打开 目录位置app/middleware.php 文件,添加中间件:
// app/middleware.php
return [
// 其他中间件...
\app\middleware\Auth::class,
];
- 路由中间件(按需验证)
如果你只想验证部分接口,在路由定义中使用中间件:
// app/route/route.php
use think\facade\Route;
// 应用Auth中间件到整个Video控制器组
Route::group('video', function () {
Route::get('getIqiyiLists', 'Video/getIqiyiLists');
Route::get('getSohuLists', 'Video/getSohuLists');
// 其他接口...
})->middleware(\app\middleware\Auth::class);
// 或者只应用到特定接口
Route::get('video/getIqiyiLists', 'Video/getIqiyiLists')
->middleware(\app\middleware\Auth::class);
--------------------------------分割线------------------------------
针对ThinkPHP 3.2.x 老版本的实现
行为扩展的实现步骤
- 创建行为类文件
在项目目录下创建以下文件:
/application
/Common
/Behavior
- CheckLoginBehavior.php # 行为类文件
/Conf
- behavior.php # 行为配置文件
- 编写行为类代码
CheckLoginBehavior.php 文件内容:
<?php
namespace Common\Behavior;
use Think\Behavior;
class CheckLoginBehavior extends Behavior {
public function run(&$params) {
// 检查是否登录
if(empty(session(SESSION_LOGIN_KEY))) {
// 获取当前请求的控制器和操作
$controller = CONTROLLER_NAME;
$action = ACTION_NAME;
// 定义白名单(无需登录即可访问的控制器/操作)
$whiteList = [
'Auth/login',
'Auth/logout',
'User/login',
'User/logout',
'User/getCode'
];
// 检查当前请求是否在白名单中
$currentUrl = "$controller/$action";
if(!in_array($currentUrl, $whiteList)) {
// 设置响应头为JSON格式
header('Content-Type:application/json;charset=utf-8');
// 输出统一的错误响应
exit(json_encode([
'code' => 401,
'msg' => '未登录,请先登录',
'data' => null
]));
}
}
}
}
- 配置行为扩展
在 application/Conf/behavior.php 中添加以下内容:
<?php
// 行为扩展配置
return [
// 在应用结束时执行登录检查
'app_end' => ['Common\\Behavior\\CheckLoginBehavior']
];
行为扩展增加XSS
也是在行为扩展里面增加的
<?php
// application/Behavior/XssCheckBehavior.php
namespace Behavior;
use Think\Behavior;
class XssCheckBehavior extends Behavior {
// 行为执行入口
public function run(&$params) {
// 只对POST请求进行XSS检测
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 获取原始请求数据
$rawData = file_get_contents('php://input');
// 强制解析JSON数据(无论Content-Type如何)
$postData = json_decode($rawData, true);
// 检查JSON解析是否成功
if (json_last_error() !== JSON_ERROR_NONE || empty($postData)) {
// JSON解析失败,尝试从$_POST获取
$postData = $_POST;
// 如果$_POST也为空,使用原始数据
if (empty($postData)) {
$postData = ['raw_data' => $rawData];
}
}
// 检查数据
if (!empty($postData) && !$this->checkPostData($postData)) {
$this->returnError();
}
}
}
// 检查POST数据
private function checkPostData($data, $parentKey = '') {
foreach ($data as $key => $value) {
$fullKey = $parentKey ? "{$parentKey}[{$key}]" : $key;
if (is_array($value)) {
// 递归检查数组
if (!$this->checkPostData($value, $fullKey)) {
return false;
}
} else {
// 检查字符串值
if ($this->containsXss($value)) {
\Think\Log::record("XSS检测到风险: {$fullKey} => {$value}", 'ERR');
return false;
}
}
}
return true;
}
// 检测是否包含XSS风险
private function containsXss($str) {
// 空值不检测
if (empty($str)) {
return false;
}
// 检测常见的XSS模式
$patterns = [
'/<script/i', // 直接的script标签
'/<[^>]*on\w+ *=/i', // 事件处理函数
'/javascript:/i', // javascript协议
'/vbscript:/i', // vbscript协议
'/<iframe/i', // iframe标签
'/<style/i', // style标签
'/<meta/i', // meta标签
'/<link/i', // link标签
'/data:text\/html/i', // data URI
'/<svg/i', // SVG注入
'/&#[xX]?[0-9a-fA-F]+;/i', // HTML实体编码
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $str)) {
return true;
}
}
return false;
}
// 返回错误信息
private function returnError() {
if (IS_AJAX) {
// AJAX请求返回JSON错误
$result = [
'status' => 0,
'info' => '提交内容包含不安全字符,请检查!'
];
exit(json_encode($result));
} else {
// 普通请求跳转
$this->error('提交内容包含不安全字符,请检查!');
}
}
}
- 总结
大概就是这样玩的。ThinkPHP 的中间件机制还是挺不错。后续你还可以加入权限的控制之类的。如果发现博文有问题,欢迎老鸟指点一二
629

被折叠的 条评论
为什么被折叠?



