本文目录导读:

- 基础类型:子域名枚举(基于搜索引擎/证书透明度)
- 高级类型:IP 存活探测与端口扫描
- 被动资产发现(外部 API 集成)
- 主动资产发现(系统调用)
- 任务调度与管理(队列)
- 网络设备/主机发现(ARP 扫描 / 网段探测)
- 核心工具库推荐(Composer)
- 总结与最佳实践
在 PHP 中进行资产发现(Asset Discovery),通常是指识别、枚举和管理与目标相关的数字资产,包括但不限于子域名、IP 地址、端口、服务、GitHub 仓库、应用等,这更贴近于 安全测试(情报收集) 或 IT 资产管理 的场景。
由于 PHP 本身没有内置的“资产发现”函数,它通常作为调度脚本,调用系统命令(如 dig、nslookup、curl、masscan)或调用第三方 API(如 Censys、Shodan、SecurityTrails)来完成任务。
下面是 PHP 实现资产发现的几种核心思路和代码示例:
基础类型:子域名枚举(基于搜索引擎/证书透明度)
这是最常用的资产发现方式之一,通过 Google、Bing 或 crt.sh(证书透明度)查询目标域名的所有子域名。
<?php
function discoverSubdomainsViaCrtsh($domain) {
$url = "https://crt.sh/?q=%25." . urlencode($domain) . "&output=json";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 注意:不要在生产环境关闭证书验证
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (AssetDiscovery)');
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || $response === false) {
return [];
}
$certs = json_decode($response, true);
$subdomains = [];
foreach ($certs as $cert) {
$names = explode("\n", $cert['name_value']);
foreach ($names as $name) {
$name = trim($name);
// 过滤掉通配符和邮件
if (strpos($name, '*') !== false) continue;
if (strpos($name, '@') !== false) continue;
// 只保留属于该域名的
if (preg_match('/^([a-zA-Z0-9._-]+\.)?' . preg_quote($domain, '/') . '$/i', $name)) {
$subdomains[] = $name;
}
}
}
return array_unique($subdomains);
}
// 使用示例
$subs = discoverSubdomainsViaCrtsh('example.com');
echo "发现子域名:" . count($subs) . " 个\n";
print_r($subs);
高级类型:IP 存活探测与端口扫描
发现子域名后,通常需要解析其 IP 地址,并检测哪些端口是开放的(服务发现)。
a) 解析 IP 地址(使用内置函数)
<?php
// 解析域名获取 IP
$ip = gethostbyname('subdomain.example.com');
echo $ip;
// 解析所有 IP(如果启用DNS多IP)
$ips = dns_get_record('subdomain.example.com', DNS_A);
foreach ($ips as $record) {
echo $record['ip'] . "\n";
}
b) 并发端口扫描(只检测 TCP 握手)
单纯用 PHP 的 fsockopen 串行扫描会很慢,建议使用 curl 或 stream_socket_client + non-blocking 模式。
<?php
function portScan($ip, $ports = [80, 443, 22, 21, 3389, 8080, 3306, 6379]) {
$openPorts = [];
foreach ($ports as $port) {
$conn = @fsockopen($ip, $port, $errno, $errstr, 1.5); // 1.5秒超时
if (is_resource($conn)) {
$openPorts[] = $port;
fclose($conn);
}
}
return $openPorts;
}
// 使用
print_r(portScan('8.8.8.8'));
被动资产发现(外部 API 集成)
如果只是用 PHP 做 API 代理或聚合,这是最有效率的,调用 Shodan、SecurityTrails 等系统可以获得全网扫描数据。
<?php
function shodanDnsLookup($domain, $apiKey) {
$url = "https://api.shodan.io/dns/resolve?hostnames=" . $domain . "&key=" . $apiKey;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
return json_decode($result, true);
// 返回 ['example.com' => '93.184.216.34']
}
// 调用示例
$apiKey = '你的SHODAN_KEY';
echo shodanDnsLookup('example.com', $apiKey)['example.com'];
主动资产发现(系统调用)
很多时候,直接用系统自带工具更快,PHP 可以安全地调用这些命令(但务必注意安全,避免命令注入)。
<?php // 使用 Masscan 或 Nmap 工具(需要系统装有这些软件) $targetIp = '192.168.1.1'; // 1. 使用 nmap 进行端口扫描 $cmd = "nmap -sS -p 1-65535 -T4 --min-rate 1000 " . $targetIp; $output = shell_exec($cmd . " 2>&1"); echo $output;
⚠️ 安全警告:当用户输入需要进入 shell 命令时,必须使用
escapeshellarg()过滤,否则会产生极其严重的命令注入漏洞。
任务调度与管理(队列)
资产发现通常耗时较长(扫描全网段),不建议直接在 Web 请求里同步执行,可以使用 PHP 结合 Workerman 或 Redis 队列 来实现异步扫描。
使用 Redis 队列 + 多进程 Worker 示例:
<?php
// 主进程:初始化任务(这里演示把任务放入队列)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->lpush('asset_scan_queue', json_encode(['domain' => 'example.com', 'scan_type' => 'subdomain']));
// Worker 进程:后台执行扫描(每秒钟取一个任务)
while ($task = $redis->rpop('asset_scan_queue')) {
$data = json_decode($task, true);
// 调用第 1 步的子域名发现函数
$subdomains = discoverSubdomainsViaCrtsh($data['domain']);
// 将结果存入数据库中(MySQL)
// ...
}
网络设备/主机发现(ARP 扫描 / 网段探测)
如果你需要发现局域网内的资产(物联网、办公设备),需要生成 IP 列表并发包探测。
<?php
// 定义网段
$network = '192.168.1';
$aliveHosts = [];
for ($i = 1; $i <= 255; $i++) {
$ip = "$network.$i";
// 使用 ICMP Ping(Linux 下需要 sudo 权限,或使用 socket)
$ping = exec("ping -c 1 -W 1 " . $ip . " 2>&1", $output, $status);
if ($status === 0) { // 存活
$aliveHosts[] = $ip;
}
}
print_r($aliveHosts);
核心工具库推荐(Composer)
在实际生产项目中,不建议从零开始写,可以借用成熟的库:
spatie/dns:用于 DNS 记录解析和枚举。php-secure-spider:针对特定目标的 Web 爬虫,用于发现暴露的页面和资产。league/uri:用于处理复杂的 URL 解析和分类。Google Cloud DNS API或AWS Route53 SDK:如果你有云环境资产,直接调用 API 拉取记录比扫描更快更准。
总结与最佳实践
在 PHP 中实现资产发现,关键是组合使用各种数据源(DNS、证书、搜索引擎、外部API)。
- 如果是做网络安全(红队):推荐用 PHP 调用
masscan+crt.sh,因为 PHP 写业务逻辑很快。 - 如果是做运维(CMDB):推荐配置定时任务(如 Laravel 的 Scheduler),每天自动跑脚本,将结果入库,保持资产清单最新。
- 性能优化:遇到并发大量网络请求时,使用
curl_multi_*函数或 Swoole/OpenSwoole 建立协程,避免串行等待。
快速起步建议:先跑通 crt.sh 的子域名发现,然后用 gethostbyname 解析 IP,最后用 curl_multi 批量检测常见端口(如 80/443/8080)的 Web 服务,这已经能覆盖 80% 的资产发现需求。