Skip to content

Commit 53fd624

Browse files
authored
Merge pull request #1467 from KodeStar/2.x
Fixes to reduce the SSRF attack vector plus display image names
2 parents 0388ee9 + 69bc8cb commit 53fd624

14 files changed

Lines changed: 4192 additions & 8442 deletions

File tree

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,5 @@ AUTH_ROLES_HEADER="remote-groups"
6565
AUTH_ROLES_HTTP_HEADER="HTTP_REMOTE_GROUPS"
6666
AUTH_ROLES_ADMIN="admin"
6767
AUTH_ROLES_DELIMITER=","
68+
69+
ALLOW_INTERNAL_REQUESTS=false

app/Http/Controllers/ItemController.php

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Illuminate\Validation\ValidationException;
2222
use Psr\Http\Message\ResponseInterface;
2323
use Psr\Http\Message\StreamInterface;
24+
use Illuminate\Http\Response;
2425
use enshrined\svgSanitize\Sanitizer;
2526

2627
class ItemController extends Controller
@@ -490,25 +491,48 @@ public function testConfig(Request $request)
490491
*/
491492
public function execute($url, array $attrs = [], $overridevars = false): ?ResponseInterface
492493
{
493-
$vars = ($overridevars !== false) ?
494-
$overridevars : [
495-
'http_errors' => false,
496-
'timeout' => 15,
497-
'connect_timeout' => 15,
498-
'verify' => false,
499-
];
494+
// Default Guzzle client configuration
495+
$clientOptions = [
496+
'http_errors' => false,
497+
'timeout' => 15,
498+
'connect_timeout' => 15,
499+
'verify' => false, // In production, set this to `true` and manage certs.
500+
];
501+
502+
// If the user provided overrides, use them.
503+
if ($overridevars !== false) {
504+
$clientOptions = $overridevars;
505+
}
506+
507+
// Resolve the hostname to an IP address
508+
$host = parse_url($url, PHP_URL_HOST);
509+
$ip = gethostbyname($host);
500510

501-
$client = new Client($vars);
511+
// Check if the IP is private or reserved
512+
$allowInternalIps = env('ALLOW_INTERNAL_REQUESTS', false);
513+
if (!$allowInternalIps && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
514+
Log::warning('Blocked access to private or reserved IPs.', ['ip' => $ip, 'host' => $host]);
515+
abort(Response::HTTP_FORBIDDEN, 'Access to private or reserved IPs is not allowed.');
516+
}
517+
518+
// Force Guzzle to use the resolved IP address
519+
$clientOptions['curl'][CURLOPT_RESOLVE] = ["{$host}:80:{$ip}", "{$host}:443:{$ip}"];
502520

521+
$client = new Client($clientOptions);
503522
$method = 'GET';
504523

505524
try {
506525
return $client->request($method, $url, $attrs);
507526
} catch (ConnectException $e) {
508-
Log::error('Connection refused');
509-
Log::debug($e->getMessage());
527+
Log::warning('SSRF Attempt Blocked: Connection to a private IP was prevented.', [
528+
'url' => $url,
529+
'error' => $e->getMessage()
530+
]);
531+
return null;
510532
} catch (ServerException $e) {
511533
Log::debug($e->getMessage());
534+
} catch (\Exception $e) {
535+
Log::error('General error: ' . $e->getMessage());
512536
}
513537

514538
return null;
@@ -520,10 +544,22 @@ public function execute($url, array $attrs = [], $overridevars = false): ?Respon
520544
*/
521545
public function websitelookup($url): StreamInterface
522546
{
523-
$url = base64_decode($url);
524-
$data = $this->execute($url);
547+
$decodedUrl = base64_decode($url);
548+
549+
// Validate the URL format.
550+
if (filter_var($decodedUrl, FILTER_VALIDATE_URL) === false) {
551+
abort(Response::HTTP_BAD_REQUEST, 'Invalid URL format provided.');
552+
}
553+
554+
$response = $this->execute($decodedUrl);
525555

526-
return $data->getBody();
556+
// If execute() returns null, it means the connection failed.
557+
// This can happen for many reasons, including our SSRF protection kicking in.
558+
if ($response === null) {
559+
abort(Response::HTTP_FORBIDDEN, 'Access to the requested resource is not allowed or the resource is unavailable.');
560+
}
561+
562+
return $response->getBody();
527563
}
528564

529565
/**

config/app.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
'appsource' => env('APP_SOURCE', 'https://appslist.heimdall.site/'),
1111

12+
'allow_internal_requests' => env('ALLOW_INTERNAL_REQUESTS', false),
1213

1314
'aliases' => Facade::defaultAliases()->merge([
1415
'EnhancedApps' => App\EnhancedApps::class,

0 commit comments

Comments
 (0)