Access: Hardened usage of referring URLs via login

Adds a more substantial URL check, via a new class which is shared and
used in other parts of the app for consistency.

Thanks to mfk25 for reporting.
This commit is contained in:
Dan Brown
2026-07-01 10:45:59 +01:00
parent fe39b69c1f
commit caeea658d1
5 changed files with 114 additions and 9 deletions

View File

@@ -70,10 +70,18 @@ class AuthTest extends TestCase
config()->set('app.url', 'http://localhost');
$this->setSettings(['app-public' => true]);
$this->get('/login', ['referer' => 'https://example.com']);
$login = $this->post('/login', ['email' => 'admin@admin.com', 'password' => 'password']);
$testCases = [
'https://example.com',
'http://localhost.example.com',
'http://localhost:ab@example.com',
];
$login->assertRedirect('http://localhost');
foreach ($testCases as $testCase) {
$this->get('/login', ['Referer' => $testCase]);
$login = $this->post('/login', ['email' => 'admin@admin.com', 'password' => 'password']);
$login->assertRedirect('http://localhost');
auth()->logout();
}
}
public function test_login_intended_redirect_does_not_factor_mfa_routes()

View File

@@ -0,0 +1,60 @@
<?php
namespace Tests\Util;
use BookStack\Util\UrlComparison;
use PHPUnit\Framework\TestCase;
class UrlComparisonTest extends TestCase
{
public function test_origins_match()
{
$good = [
['http://localhost/a/b', 'http://localhost#cat'],
['example.com/a/b', 'example.com?cat=dog'],
['https://example.com:5050/a/b', 'https://example.com:5050/a/b'],
];
$bad = [
['http://localhost/a/b', 'http://localh0st#cat'],
['https://example.com/a/b', 'http://example.com?cat=dog'],
['https://example.com:5051/a/b', 'https://example.com:5050/a/b'],
];
foreach ($good as [$a, $b]) {
$comparison = new UrlComparison($a, $b);
$this->assertTrue($comparison->originsMatch());
}
foreach ($bad as [$a, $b]) {
$comparison = new UrlComparison($a, $b);
$this->assertFalse($comparison->originsMatch());
}
}
public function test_paths_overlap()
{
$good = [
['https://example.com', 'https://example.com/a/b/c'],
['https://example.com/', 'https://example.com/a/b/c'],
['https://example.com/a/b', 'https://example.com/a/b/c'],
['https://example.com/a/b/c', 'https://example.com/a'],
['http://donk.com/a/b/c?a=b#cat', 'https://example.com:5005/a/b#hello'],
];
$bad = [
['https://example.com/a/c', 'https://example.com/a/b/c/d'],
['https://example.com/a/c', 'https://example.com/d/a/c'],
];
foreach ($good as [$a, $b]) {
$comparison = new UrlComparison($a, $b);
$this->assertTrue($comparison->pathsOverlap());
}
foreach ($bad as [$a, $b]) {
$comparison = new UrlComparison($a, $b);
$this->assertFalse($comparison->pathsOverlap());
}
}
}