Skip to content

Commit 75ce8a5

Browse files
author
Daniel Neto
committed
refactor: Enhance SSRF protection by normalizing IPv4-mapped IPv6 addresses and simplifying IP validation
https://github.com/WWBN/AVideo/security/advisories/GHSA-p3gr-g84w-g8hh#event-586396
1 parent 087dab8 commit 75ce8a5

File tree

1 file changed

+15
-54
lines changed

1 file changed

+15
-54
lines changed

objects/functions.php

Lines changed: 15 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4100,44 +4100,24 @@ function isSSRFSafeURL($url)
41004100
return false;
41014101
}
41024102

4103-
// Block private IPv4 ranges
4104-
// 10.0.0.0 - 10.255.255.255
4105-
if (preg_match('/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $ip)) {
4106-
_error_log("isSSRFSafeURL: blocked private IP (10.x.x.x): {$ip}");
4103+
// Normalize IPv4-mapped IPv6 addresses (::ffff:x.x.x.x) to their plain IPv4 form.
4104+
// Without this, dotted-decimal private-range regexes and PHP's FILTER_FLAG_NO_PRIV_RANGE
4105+
// flag both miss the mapped address — the bypass vector reported in CVE-class SSRF findings.
4106+
if (preg_match('/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $ip, $mapped)) {
4107+
_error_log("isSSRFSafeURL: normalized IPv4-mapped IPv6 {$ip} to {$mapped[1]}");
4108+
$ip = $mapped[1];
4109+
}
4110+
4111+
// Block all private and reserved IP ranges using PHP's built-in validation flags.
4112+
// FILTER_FLAG_NO_PRIV_RANGE rejects: RFC 1918 (10/8, 172.16/12, 192.168/16), fc00::/7
4113+
// FILTER_FLAG_NO_RES_RANGE rejects: 0/8, 127/8, 169.254/16, ::1, fe80::/10, and others
4114+
// This replaces the previous manual regex checks and the separate IPv6 block section.
4115+
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
4116+
_error_log("isSSRFSafeURL: blocked private/reserved IP: {$ip}");
41074117
return false;
41084118
}
41094119

4110-
// 172.16.0.0 - 172.31.255.255
4111-
if (preg_match('/^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}$/', $ip)) {
4112-
_error_log("isSSRFSafeURL: blocked private IP (172.16-31.x.x): {$ip}");
4113-
return false;
4114-
}
4115-
4116-
// 192.168.0.0 - 192.168.255.255
4117-
if (preg_match('/^192\.168\.\d{1,3}\.\d{1,3}$/', $ip)) {
4118-
_error_log("isSSRFSafeURL: blocked private IP (192.168.x.x): {$ip}");
4119-
return false;
4120-
}
4121-
4122-
// 127.0.0.0 - 127.255.255.255 (loopback)
4123-
if (preg_match('/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $ip)) {
4124-
_error_log("isSSRFSafeURL: blocked loopback IP: {$ip}");
4125-
return false;
4126-
}
4127-
4128-
// 169.254.0.0 - 169.254.255.255 (link-local, includes AWS/cloud metadata)
4129-
if (preg_match('/^169\.254\.\d{1,3}\.\d{1,3}$/', $ip)) {
4130-
_error_log("isSSRFSafeURL: blocked link-local/metadata IP: {$ip}");
4131-
return false;
4132-
}
4133-
4134-
// 0.0.0.0 - 0.255.255.255
4135-
if (preg_match('/^0\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $ip)) {
4136-
_error_log("isSSRFSafeURL: blocked zero IP range: {$ip}");
4137-
return false;
4138-
}
4139-
4140-
// Block metadata endpoints (cloud providers)
4120+
// Block cloud metadata endpoints by hostname (defence-in-depth alongside the IP check)
41414121
$metadataHosts = [
41424122
'metadata.google.internal',
41434123
'metadata.goog',
@@ -4150,25 +4130,6 @@ function isSSRFSafeURL($url)
41504130
}
41514131
}
41524132

4153-
// Block IPv6 private/local addresses
4154-
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
4155-
// Check for IPv6 loopback
4156-
if ($ip === '::1' || $ip === '0:0:0:0:0:0:0:1') {
4157-
_error_log("isSSRFSafeURL: blocked IPv6 loopback: {$ip}");
4158-
return false;
4159-
}
4160-
// Check for IPv6 link-local (fe80::/10)
4161-
if (preg_match('/^fe[89ab][0-9a-f]:/i', $ip)) {
4162-
_error_log("isSSRFSafeURL: blocked IPv6 link-local: {$ip}");
4163-
return false;
4164-
}
4165-
// Check for IPv6 unique local (fc00::/7)
4166-
if (preg_match('/^f[cd][0-9a-f]{2}:/i', $ip)) {
4167-
_error_log("isSSRFSafeURL: blocked IPv6 unique local: {$ip}");
4168-
return false;
4169-
}
4170-
}
4171-
41724133
return true;
41734134
}
41744135

0 commit comments

Comments
 (0)