fix(back): 拒绝 nested back 参数避免回退 URL 膨胀(plans/platform-orders)
This commit is contained in:
@@ -149,7 +149,12 @@ class PlanController extends Controller
|
||||
|
||||
$back = (string) $request->query('back', '');
|
||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
||||
$safeBack = (str_starts_with($back, '/') && !preg_match('/["\'<>]/', $back)) ? $back : '';
|
||||
$safeBack = (str_starts_with($back, '/')
|
||||
&& !preg_match('/["\'<>]/', $back)
|
||||
// back 本身不应再包含 back(避免无限嵌套导致 URL 膨胀)
|
||||
&& !preg_match('/(?:^|[?&])back=/', $back))
|
||||
? $back
|
||||
: '';
|
||||
|
||||
return view('admin.plans.form', [
|
||||
'plan' => new Plan(),
|
||||
@@ -169,7 +174,12 @@ class PlanController extends Controller
|
||||
|
||||
$back = (string) $request->input('back', '');
|
||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
||||
$safeBack = (str_starts_with($back, '/') && !preg_match('/["\'<>]/', $back)) ? $back : '';
|
||||
$safeBack = (str_starts_with($back, '/')
|
||||
&& !preg_match('/["\'<>]/', $back)
|
||||
// back 本身不应再包含 back(避免无限嵌套导致 URL 膨胀)
|
||||
&& !preg_match('/(?:^|[?&])back=/', $back))
|
||||
? $back
|
||||
: '';
|
||||
|
||||
$plan = Plan::query()->create($data);
|
||||
|
||||
@@ -186,7 +196,12 @@ class PlanController extends Controller
|
||||
|
||||
$back = (string) $request->query('back', '');
|
||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
||||
$safeBack = (str_starts_with($back, '/') && !preg_match('/["\'<>]/', $back)) ? $back : '';
|
||||
$safeBack = (str_starts_with($back, '/')
|
||||
&& !preg_match('/["\'<>]/', $back)
|
||||
// back 本身不应再包含 back(避免无限嵌套导致 URL 膨胀)
|
||||
&& !preg_match('/(?:^|[?&])back=/', $back))
|
||||
? $back
|
||||
: '';
|
||||
|
||||
return view('admin.plans.form', [
|
||||
'plan' => $plan,
|
||||
@@ -226,7 +241,12 @@ class PlanController extends Controller
|
||||
|
||||
$back = (string) $request->input('back', '');
|
||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
||||
$safeBack = (str_starts_with($back, '/') && !preg_match('/["\'<>]/', $back)) ? $back : '';
|
||||
$safeBack = (str_starts_with($back, '/')
|
||||
&& !preg_match('/["\'<>]/', $back)
|
||||
// back 本身不应再包含 back(避免无限嵌套导致 URL 膨胀)
|
||||
&& !preg_match('/(?:^|[?&])back=/', $back))
|
||||
? $back
|
||||
: '';
|
||||
|
||||
$plan->update($data);
|
||||
|
||||
|
||||
@@ -45,7 +45,10 @@ class PlatformOrderController extends Controller
|
||||
// back 安全阀:必须为站内相对路径,并拒绝引号/尖括号。
|
||||
// 说明:form 页会把 defaults.back 透传到 hidden input 与返回按钮;因此这里提前清洗,避免 unsafe back 在页面中出现。
|
||||
$incomingBack = (string) ($defaults['back'] ?? '');
|
||||
$defaults['back'] = (str_starts_with($incomingBack, '/') && !preg_match('/["\'<>]/', $incomingBack))
|
||||
$defaults['back'] = (str_starts_with($incomingBack, '/')
|
||||
&& !preg_match('/["\'<>]/', $incomingBack)
|
||||
// back 本身不应再包含 back(避免无限嵌套导致 URL 膨胀)
|
||||
&& !preg_match('/(?:^|[?&])back=/', $incomingBack))
|
||||
? $incomingBack
|
||||
: '';
|
||||
|
||||
@@ -134,7 +137,12 @@ class PlatformOrderController extends Controller
|
||||
|
||||
$back = (string) ($data['back'] ?? '');
|
||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免在后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
||||
$safeBack = (str_starts_with($back, '/') && !preg_match('/["\'<>]/', $back)) ? $back : '';
|
||||
$safeBack = (str_starts_with($back, '/')
|
||||
&& !preg_match('/["\'<>]/', $back)
|
||||
// back 本身不应再包含 back(避免无限嵌套导致 URL 膨胀)
|
||||
&& !preg_match('/(?:^|[?&])back=/', $back))
|
||||
? $back
|
||||
: '';
|
||||
|
||||
$redirectUrl = '/admin/platform-orders/' . $order->id;
|
||||
if ($safeBack !== '') {
|
||||
|
||||
@@ -33,6 +33,19 @@ class AdminPlanControllerBackValidationTest extends TestCase
|
||||
$res->assertDontSee($unsafeBack, false);
|
||||
}
|
||||
|
||||
public function test_create_should_not_echo_back_when_back_contains_nested_back_param(): void
|
||||
{
|
||||
$this->loginAsPlatformAdmin();
|
||||
|
||||
$nestedBack = '/admin/platform-orders?status=pending&back=/admin/plans';
|
||||
|
||||
$res = $this->get('/admin/plans/create?back=' . urlencode($nestedBack));
|
||||
$res->assertOk();
|
||||
|
||||
$res->assertDontSee('name="back"', false);
|
||||
$res->assertDontSee($nestedBack, false);
|
||||
}
|
||||
|
||||
public function test_store_should_ignore_unsafe_back_and_redirect_to_index(): void
|
||||
{
|
||||
$this->loginAsPlatformAdmin();
|
||||
|
||||
@@ -35,4 +35,18 @@ class AdminPlatformOrderCreateBackValidationTest extends TestCase
|
||||
// 返回按钮应回退到默认列表
|
||||
$res->assertSee('href="/admin/platform-orders"', false);
|
||||
}
|
||||
|
||||
public function test_create_should_not_echo_back_when_back_contains_nested_back_param(): void
|
||||
{
|
||||
$this->loginAsPlatformAdmin();
|
||||
|
||||
$nestedBack = '/admin/site-subscriptions?status=activated&back=/admin/platform-orders';
|
||||
|
||||
$res = $this->get('/admin/platform-orders/create?back=' . urlencode($nestedBack));
|
||||
$res->assertOk();
|
||||
|
||||
$res->assertDontSee('name="back"', false);
|
||||
$res->assertDontSee($nestedBack, false);
|
||||
$res->assertSee('href="/admin/platform-orders"', false);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user