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 = (string) $request->query('back', '');
|
||||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
// 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', [
|
return view('admin.plans.form', [
|
||||||
'plan' => new Plan(),
|
'plan' => new Plan(),
|
||||||
@@ -169,7 +174,12 @@ class PlanController extends Controller
|
|||||||
|
|
||||||
$back = (string) $request->input('back', '');
|
$back = (string) $request->input('back', '');
|
||||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
// 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);
|
$plan = Plan::query()->create($data);
|
||||||
|
|
||||||
@@ -186,7 +196,12 @@ class PlanController extends Controller
|
|||||||
|
|
||||||
$back = (string) $request->query('back', '');
|
$back = (string) $request->query('back', '');
|
||||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
// 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', [
|
return view('admin.plans.form', [
|
||||||
'plan' => $plan,
|
'plan' => $plan,
|
||||||
@@ -226,7 +241,12 @@ class PlanController extends Controller
|
|||||||
|
|
||||||
$back = (string) $request->input('back', '');
|
$back = (string) $request->input('back', '');
|
||||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
// 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);
|
$plan->update($data);
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ class PlatformOrderController extends Controller
|
|||||||
// back 安全阀:必须为站内相对路径,并拒绝引号/尖括号。
|
// back 安全阀:必须为站内相对路径,并拒绝引号/尖括号。
|
||||||
// 说明:form 页会把 defaults.back 透传到 hidden input 与返回按钮;因此这里提前清洗,避免 unsafe back 在页面中出现。
|
// 说明:form 页会把 defaults.back 透传到 hidden input 与返回按钮;因此这里提前清洗,避免 unsafe back 在页面中出现。
|
||||||
$incomingBack = (string) ($defaults['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
|
? $incomingBack
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
@@ -134,7 +137,12 @@ class PlatformOrderController extends Controller
|
|||||||
|
|
||||||
$back = (string) ($data['back'] ?? '');
|
$back = (string) ($data['back'] ?? '');
|
||||||
// back 需为站内相对路径,并拒绝引号/尖括号,避免在后续页面以 `{!! !!}` 原样输出时引入 XSS 风险。
|
// 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;
|
$redirectUrl = '/admin/platform-orders/' . $order->id;
|
||||||
if ($safeBack !== '') {
|
if ($safeBack !== '') {
|
||||||
|
|||||||
@@ -33,6 +33,19 @@ class AdminPlanControllerBackValidationTest extends TestCase
|
|||||||
$res->assertDontSee($unsafeBack, false);
|
$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
|
public function test_store_should_ignore_unsafe_back_and_redirect_to_index(): void
|
||||||
{
|
{
|
||||||
$this->loginAsPlatformAdmin();
|
$this->loginAsPlatformAdmin();
|
||||||
|
|||||||
@@ -35,4 +35,18 @@ class AdminPlatformOrderCreateBackValidationTest extends TestCase
|
|||||||
// 返回按钮应回退到默认列表
|
// 返回按钮应回退到默认列表
|
||||||
$res->assertSee('href="/admin/platform-orders"', false);
|
$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