Skip to content

Commit 9b3f3e2

Browse files
authored
Merge pull request #8149 from ProcessMaker/security/FOUR-22502
Security/FOUR-22502: [HIGH] Host Header Injection
2 parents 3907d95 + 277b874 commit 9b3f3e2

3 files changed

Lines changed: 102 additions & 1 deletion

File tree

‎ProcessMaker/Http/Kernel.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Kernel extends HttpKernel
1919
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
2020
Middleware\TrimStrings::class,
2121
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
22+
Middleware\TrustHosts::class,
2223
Middleware\TrustProxies::class,
2324
Middleware\BrowserCache::class,
2425
ServerTimingMiddleware::class,
@@ -88,7 +89,7 @@ class Kernel extends HttpKernel
8889
'no-cache' => Middleware\NoCache::class,
8990
'admin' => Middleware\IsAdmin::class,
9091
'etag' => Middleware\Etag\HandleEtag::class,
91-
'file_size_check' => Middleware\FileSizeCheck::class,
92+
'file_size_check' => Middleware\FileSizeCheck::class
9293
];
9394

9495
/**
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace ProcessMaker\Http\Middleware;
4+
5+
use Illuminate\Http\Middleware\TrustHosts as Middleware;
6+
use Closure;
7+
use Illuminate\Http\Request;
8+
9+
class TrustHosts extends Middleware
10+
{
11+
public function hosts(): array
12+
{
13+
$trustedHost = $this->allSubdomainsOfApplicationUrl();
14+
return [$trustedHost];
15+
}
16+
17+
public function handle(Request $request, $next)
18+
{
19+
if ($request->hasHeader('X-Forwarded-Host')) {
20+
$forwardedHost = $request->header('X-Forwarded-Host');
21+
$trustedPattern = $this->allSubdomainsOfApplicationUrl();
22+
23+
if (!$this->hostIsValid($forwardedHost, $trustedPattern)) {
24+
\Log::warning('Rejected request with untrusted X-Forwarded-Host', [
25+
'forwarded_host' => $forwardedHost,
26+
'trusted_pattern' => $trustedPattern
27+
]);
28+
abort(400, 'Invalid Host Header');
29+
}
30+
}
31+
32+
return parent::handle($request, $next);
33+
}
34+
35+
protected function hostIsValid(string $host, string $pattern): bool
36+
{
37+
return preg_match('/' . str_replace('/', '\/', $pattern) . '/', $host) === 1;
38+
}
39+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace Tests\Feature\Http\Middleware;
4+
5+
use Tests\TestCase;
6+
use ProcessMaker\Http\Middleware\TrustHosts;
7+
use Illuminate\Http\Request;
8+
use Illuminate\Support\Facades\Config;
9+
10+
class TrustHostsTest extends TestCase
11+
{
12+
protected $middleware;
13+
14+
protected function setUp(): void
15+
{
16+
parent::setUp();
17+
$this->middleware = new TrustHosts($this->app);
18+
}
19+
20+
public function test_valid_trusted_host()
21+
{
22+
// Set app URL for testing
23+
Config::set('app.url', 'https://example.processmaker.net');
24+
25+
$request = Request::create('https://subdomain.example.processmaker.net');
26+
$request->headers->set('X-Forwarded-Host', 'subdomain.example.processmaker.net');
27+
28+
$response = $this->middleware->handle($request, function ($req) {
29+
return response()->json(['status' => 'success']);
30+
});
31+
32+
$this->assertEquals(200, $response->status());
33+
}
34+
35+
public function test_invalid_trusted_host()
36+
{
37+
// Set app URL for testing
38+
Config::set('app.url', 'https://example.processmaker.net');
39+
40+
$request = Request::create('https://malicious-site.com');
41+
$request->headers->set('X-Forwarded-Host', 'malicious-site.com');
42+
43+
$this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class);
44+
$this->expectExceptionMessage('Invalid Host Header');
45+
46+
$this->middleware->handle($request, function ($req) {
47+
return response()->json(['status' => 'success']);
48+
});
49+
}
50+
51+
public function test_missing_forwarded_host()
52+
{
53+
$request = Request::create('https://example.processmaker.net');
54+
55+
$response = $this->middleware->handle($request, function ($req) {
56+
return response()->json(['status' => 'success']);
57+
});
58+
59+
$this->assertEquals(200, $response->status());
60+
}
61+
}

0 commit comments

Comments
 (0)