| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- <?php
- namespace App\Services;
- use GuzzleHttp\Client;
- use Illuminate\Support\Facades\Log;
- /**
- * IP风险检测服务
- *
- * 检测逻辑:
- * 1. 通过 ip-api.com 获取 IP 的 ASN 和国家信息
- * 2. 如果 ASN/ISP 关键字包含常见云厂商 → 标记为可疑
- * 3. 如果 IP 不属于美国 → 标记为可疑
- */
- class IpRiskService
- {
- // 常见云厂商 ASN/ISP 关键字(小写)
- const CLOUD_KEYWORDS = [
- 'amazon',
- 'aws',
- 'google cloud',
- 'gcp',
- 'microsoft azure',
- 'microsoft corporation',
- 'digitalocean',
- 'linode',
- 'vultr',
- 'alibaba',
- 'tencent',
- 'oracle cloud',
- 'ovh',
- 'hetzner',
- 'cloudflare',
- 'akamai',
- 'fastly',
- ];
- /**
- * 检测 IP 是否存在风险
- *
- * @param string $ip
- * @return array{is_risky: bool, reason: string, ip: string}
- */
- public function detect($ip)
- {
- $result = ['is_risky' => false, 'reason' => '', 'ip' => $ip];
- if (empty($ip) || $ip === '127.0.0.1' || $ip === '::1') {
- return $result;
- }
- // 私有 IP 范围不检测
- if ($this->isPrivateIp($ip)) {
- return $result;
- }
- $info = $this->queryIpInfo($ip);
- if (empty($info)) {
- return $result;
- }
- $countryCode = strtoupper($info['countryCode'] ?? '');
- $as = strtolower($info['as'] ?? '');
- $org = strtolower($info['org'] ?? '');
- $isp = strtolower($info['isp'] ?? '');
- $combined = $as . ' ' . $org . ' ' . $isp;
- // 检测1: 云厂商 ASN/ISP 关键字
- $matchedKeyword = $this->matchCloudKeyword($combined);
- if ($matchedKeyword) {
- $result['is_risky'] = true;
- $result['reason'] = "cloud_asn:{$matchedKeyword}";
- Log::info("IpRisk: cloud ASN detected", ['ip' => $ip, 'keyword' => $matchedKeyword]);
- return $result;
- }
- // 检测2: 非美国 IP
- if ($countryCode !== '' && $countryCode !== 'US') {
- $result['is_risky'] = true;
- $result['reason'] = "non_us:{$countryCode}";
- Log::info("IpRisk: non-US IP detected", ['ip' => $ip, 'country' => $countryCode]);
- return $result;
- }
- return $result;
- }
- /**
- * 调用 ip-api.com 查询 IP 信息
- *
- * @param string $ip
- * @return array|null
- */
- protected function queryIpInfo($ip)
- {
- try {
- $client = new Client(['timeout' => 5]);
- $response = $client->get("http://ip-api.com/json/{$ip}", [
- 'query' => ['fields' => 'countryCode,as,org,isp'],
- ]);
- $data = json_decode($response->getBody()->getContents(), true);
- if (isset($data['status']) && $data['status'] === 'fail') {
- Log::warning("IpRisk: ip-api query failed", ['ip' => $ip, 'msg' => $data['message'] ?? '']);
- return null;
- }
- return $data;
- } catch (\Exception $e) {
- Log::warning("IpRisk: ip-api request error", ['ip' => $ip, 'error' => $e->getMessage()]);
- return null;
- }
- }
- /**
- * 检查是否命中云厂商关键字
- *
- * @param string $text
- * @return string|false 命中的关键字,或 false
- */
- protected function matchCloudKeyword($text)
- {
- foreach (self::CLOUD_KEYWORDS as $keyword) {
- if (strpos($text, $keyword) !== false) {
- return $keyword;
- }
- }
- return false;
- }
- /**
- * 判断是否为私有 IP
- *
- * @param string $ip
- * @return bool
- */
- protected function isPrivateIp($ip)
- {
- return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
- }
- }
|