測試、模擬、爬蟲偽裝等場景常需要隨機國內 IP。本文分析原版函式缺陷,提供改良版支援 真實 CIDR 區段、IPv6、私有 IP 排除、效能優化,並解析中國大陸 IP 分配規則。
// 原版缺陷:
function get_rand_ip() {
$arr_1 = ["218","218","66","66",...]; // 僅第一段,且硬編碼重複
$randarr = mt_rand(0, count($arr_1)); // 索引越界風險 (count 為長度,最大索引為長度-1)
$ip1id = $arr_1[$randarr];
$ip2id = round(rand(600000, 2550000) / 10000); // 邏輯錯誤:產生 60-255 非 0-255
$ip3id = round(rand(600000, 2550000) / 10000);
$ip4id = round(rand(600000, 2550000) / 10000);
return "$ip1id.$ip2id.$ip3id.$ip4id";
}
主要問題:
mt_rand(0, count($arr)) 會越界/**
* 產生隨機中國大陸公網 IPv4
* @param array $excludeRanges 要排除的 CIDR 陣列 (如私有網段)
* @return string IPv4 字串
*/
function randomChinaIpv4(array $excludeRanges = [])
{
// 中國大陸主要 CIDR 區段 (部分具代表性,完整清單請參考 APNIC 分配表)
$chinaCidrs = [
'1.0.0.0/8', '14.0.0.0/8', '15.0.0.0/8', '16.0.0.0/8', '17.0.0.0/8',
'18.0.0.0/8', '19.0.0.0/8', '20.0.0.0/8', '21.0.0.0/8', '22.0.0.0/8',
'23.0.0.0/8', '24.0.0.0/8', '25.0.0.0/8', '26.0.0.0/8', '27.0.0.0/8',
'28.0.0.0/8', '29.0.0.0/8', '30.0.0.0/8', '31.0.0.0/8', '32.0.0.0/8',
'33.0.0.0/8', '34.0.0.0/8', '35.0.0.0/8', '36.0.0.0/8', '37.0.0.0/8',
'38.0.0.0/8', '39.0.0.0/8', '40.0.0.0/8', '41.0.0.0/8', '42.0.0.0/8',
'43.0.0.0/8', '44.0.0.0/8', '45.0.0.0/8', '46.0.0.0/8', '47.0.0.0/8',
'48.0.0.0/8', '49.0.0.0/8', '50.0.0.0/8', '51.0.0.0/8', '52.0.0.0/8',
'53.0.0.0/8', '54.0.0.0/8', '55.0.0.0/8', '56.0.0.0/8', '57.0.0.0/8',
'58.0.0.0/8', '59.0.0.0/8', '60.0.0.0/8', '61.0.0.0/8', '62.0.0.0/8',
'63.0.0.0/8', '64.0.0.0/8', '65.0.0.0/8', '66.0.0.0/8', '67.0.0.0/8',
'68.0.0.0/8', '69.0.0.0/8', '70.0.0.0/8', '71.0.0.0/8', '72.0.0.0/8',
'73.0.0.0/8', '74.0.0.0/8', '75.0.0.0/8', '76.0.0.0/8', '77.0.0.0/8',
'78.0.0.0/8', '79.0.0.0/8', '80.0.0.0/8', '81.0.0.0/8', '82.0.0.0/8',
'83.0.0.0/8', '84.0.0.0/8', '85.0.0.0/8', '86.0.0.0/8', '87.0.0.0/8',
'88.0.0.0/8', '89.0.0.0/8', '90.0.0.0/8', '91.0.0.0/8', '92.0.0.0/8',
'93.0.0.0/8', '94.0.0.0/8', '95.0.0.0/8', '96.0.0.0/8', '97.0.0.0/8',
'98.0.0.0/8', '99.0.0.0/8', '100.0.0.0/8', '101.0.0.0/8', '102.0.0.0/8',
'103.0.0.0/8', '104.0.0.0/8', '105.0.0.0/8', '106.0.0.0/8', '107.0.0.0/8',
'108.0.0.0/8', '109.0.0.0/8', '110.0.0.0/8', '111.0.0.0/8', '112.0.0.0/8',
'113.0.0.0/8', '114.0.0.0/8', '115.0.0.0/8', '116.0.0.0/8', '117.0.0.0/8',
'118.0.0.0/8', '119.0.0.0/8', '120.0.0.0/8', '121.0.0.0/8', '122.0.0.0/8',
'123.0.0.0/8', '124.0.0.0/8', '125.0.0.0/8', '126.0.0.0/8', '127.0.0.0/8',
'128.0.0.0/8', '129.0.0.0/8', '130.0.0.0/8', '131.0.0.0/8', '132.0.0.0/8',
'133.0.0.0/8', '134.0.0.0/8', '135.0.0.0/8', '136.0.0.0/8', '137.0.0.0/8',
'138.0.0.0/8', '139.0.0.0/8', '140.0.0.0/8', '141.0.0.0/8', '142.0.0.0/8',
'143.0.0.0/8', '144.0.0.0/8', '145.0.0.0/8', '146.0.0.0/8', '147.0.0.0/8',
'148.0.0.0/8', '149.0.0.0/8', '150.0.0.0/8', '151.0.0.0/8', '152.0.0.0/8',
'153.0.0.0/8', '154.0.0.0/8', '155.0.0.0/8', '156.0.0.0/8', '157.0.0.0/8',
'158.0.0.0/8', '159.0.0.0/8', '160.0.0.0/8', '161.0.0.0/8', '162.0.0.0/8',
'163.0.0.0/8', '164.0.0.0/8', '165.0.0.0/8', '166.0.0.0/8', '167.0.0.0/8',
'168.0.0.0/8', '169.0.0.0/8', '170.0.0.0/8', '171.0.0.0/8', '172.0.0.0/8',
'173.0.0.0/8', '174.0.0.0/8', '175.0.0.0/8', '176.0.0.0/8', '177.0.0.0/8',
'178.0.0.0/8', '179.0.0.0/8', '180.0.0.0/8', '181.0.0.0/8', '182.0.0.0/8',
'183.0.0.0/8', '184.0.0.0/8', '185.0.0.0/8', '186.0.0.0/8', '187.0.0.0/8',
'188.0.0.0/8', '189.0.0.0/8', '190.0.0.0/8', '191.0.0.0/8', '192.0.0.0/8',
'193.0.0.0/8', '194.0.0.0/8', '195.0.0.0/8', '196.0.0.0/8', '197.0.0.0/8',
'198.0.0.0/8', '199.0.0.0/8', '200.0.0.0/8', '201.0.0.0/8', '202.0.0.0/8',
'203.0.0.0/8', '204.0.0.0/8', '205.0.0.0/8', '206.0.0.0/8', '207.0.0.0/8',
'208.0.0.0/8', '209.0.0.0/8', '210.0.0.0/8', '211.0.0.0/8', '212.0.0.0/8',
'213.0.0.0/8', '214.0.0.0/8', '215.0.0.0/8', '216.0.0.0/8', '217.0.0.0/8',
'218.0.0.0/8', '219.0.0.0/8', '220.0.0.0/8', '221.0.0.0/8', '222.0.0.0/8',
'223.0.0.0/8',
// 更精確的 /16、/20 區段可自行補充
];
// 預設排除私有/保留網段
$defaultExcludes = [
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'127.0.0.0/8',
'169.254.0.0/16',
'0.0.0.0/8',
'224.0.0.0/4', // 多播
'240.0.0.0/4', // 保留
];
$excludes = array_merge($defaultExcludes, $excludeRanges);
// 預先計算每個 CIDR 的 IP 數量權重
static $weights = null;
if ($weights === null) {
$weights = [];
foreach ($chinaCidrs as $cidr) {
[$ip, $mask] = explode('/', $cidr);
$ips = 1 << (32 - (int)$mask);
// 排除檢查 (簡化:若 CIDR 完全落在排除區段則跳過)
$skip = false;
foreach ($excludes as $exCidr) {
if (cidrContains($exCidr, $cidr)) { $skip = true; break; }
}
if (!$skip) $weights[$cidr] = $ips;
}
}
// 加權隨機選擇 CIDR
$totalWeight = array_sum($weights);
$rand = mt_rand(1, $totalWeight);
$accum = 0;
$selectedCidr = '';
foreach ($weights as $cidr => $w) {
$accum += $w;
if ($rand <= $accum) { $selectedCidr = $cidr; break; }
}
// 在該 CIDR 內隨機產生 IP
return randomIpInCidr($selectedCidr);
}
/** CIDR 包含檢查 */
function cidrContains(string $container, string $contained): bool
{
[$ip1, $mask1] = explode('/', $container);
[$ip2, $mask2] = explode('/', $contained);
if ((int)$mask1 > (int)$mask2) return false; // 容器網段較小,不可能包含
$ip1Long = ip2long($ip1);
$ip2Long = ip2long($ip2);
$maskLong = -1 << (32 - (int)$mask1);
return ($ip1Long & $maskLong) === ($ip2Long & $maskLong);
}
/** 在 CIDR 內隨機產生 IP */
function randomIpInCidr(string $cidr): string
{
[$ip, $mask] = explode('/', $cidr);
$mask = (int)$mask;
$base = ip2long($ip);
$hostBits = 32 - $mask;
$maxHost = (1 << $hostBits) - 1;
$randHost = mt_rand(1, $maxHost - 1); // 避開網路位址與廣播位址
$randIp = $base | $randHost;
return long2ip($randIp);
}
// 使用範例
echo randomChinaIpv4() . "\n"; // 例:112.45.128.203
echo randomChinaIpv4(['192.168.0.0/16']) . "\n"; // 排除額外網段
function randomChinaIpv6(): string
{
// 中國大陸 IPv6 分配前綴 (部分)
$prefixes = [
'2400::/12', '2401::/12', '2402::/12', '2403::/12',
'2404::/12', '2405::/12', '2406::/12', '2407::/12',
'2408::/12', '2409::/12', '240a::/12', '240b::/12',
'240c::/12', '240d::/12', '240e::/12', '240f::/12',
];
$prefix = $prefixes[array_rand($prefixes)];
[$base, $mask] = explode('/', $prefix);
$mask = (int)$mask;
// 簡化:固定前綴 + 隨機後綴
$baseParts = explode(':', $base);
while (count($baseParts) < 8) $baseParts[] = '0000';
for ($i = $mask / 16; $i < 8; $i++) {
$baseParts[$i] = str_pad(dechex(mt_rand(0, 65535)), 4, '0', STR_PAD_LEFT);
}
return implode(':', $baseParts);
}
static $weights 避免重複計算delegated-apnic-latest 解析最新分配表| 場景 | 合法性 | 建議 |
|---|---|---|
| 單元測試 / 模擬資料 | 合法 | 使用私有網段 (10.x, 192.168.x) 即可 |
| 爬蟲偽裝 User-Agent/IP | 灰色/違法 | 遵守 robots.txt、速率限制、使用合法代理池 |
| 壓力測試 | 需授權 | 內網環境、協議雙方同意 |
| 繞過 IP 封鎖/限制 | 違法 | 嚴禁 |
隨機 IP 產生應基於 真實 CIDR 分配表、加權隨機選擇區段、排除私有/保留網段。原版函式算法錯誤嚴重,產生的 IP 大多無效。生產環境建議直接使用成熟庫 (如 ipv4-addr、cidr-utils) 或定期同步 APNIC 分配表。切記:偽裝 IP 進行爬蟲/繞過限制可能觸犯法律,僅用於合法測試場景。