內容安全、SEO 審核、防灌水場景常需檢測用戶輸入是否包含外部鏈接。本文提供一個輕量級 PHP 函數 all_external_link(),通過正則提取所有鏈接域名並與當前域名比對,判斷是否存在外鏈。
<?php
/**
* 檢測字符串是否包含外部鏈接
* @param string $text 待檢測文本
* @param string $host 當前站點域名(可選,默認 $_SERVER['HTTP_HOST'])
* @return bool true=無外鏈/全內鏈, false=存在外鏈
*/
function all_external_link(string $text = '', string $host = ''): bool {
if (empty($host)) $host = $_SERVER['HTTP_HOST'];
// 匹配 http(s)://domain.tld 格式,捕獲域名部分
$reg = '/https?:\/\/((?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,4})/';
preg_match_all($reg, $text, $matches);
$domains = $matches[1] ?? [];
foreach ($domains as $domain) {
if (strtolower($domain) !== strtolower($host)) {
return false; // 發現外鏈
}
}
return true; // 無外鏈或全內鏈
}
?>
/https?:\/\/((?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,4})/
├── https?:\/\/ # 協議:https:// 或 https://
├── ( # 捕獲組 1:域名
│ (?: # 非捕獲組:子域名部分
│ [A-Za-z0-9-]+\. # 字母數字連字符 + 點
│ )+ # 一個或多個子域名
│ [A-Za-z]{2,4} # 頂級域:2-4 字母
└── )
<?php
$host = 'example.com';
$text1 = '歡迎訪問 https://example.com/about 和 https://example.com/contact';
var_dump(all_external_link($text1, $host)); // true (全內鏈)
$text2 = '查看外部資源 https://google.com 和 https://github.com';
var_dump(all_external_link($text2, $host)); // false (含外鏈)
$text3 = '混合鏈接 https://example.com/page https://evil.com/xss';
var_dump(all_external_link($text3, $host)); // false (含外鏈)
$text4 = '無鏈接純文本內容';
var_dump(all_external_link($text4, $host)); // true (無鏈接視為安全)
?>
<?php
function extract_external_links(string $text, string $host): array {
$reg = '/https?:\/\/((?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,4})/';
preg_match_all($reg, $text, $matches);
$domains = $matches[1] ?? [];
$external = [];
foreach ($domains as $domain) {
if (strtolower($domain) !== strtolower($host)) {
$external[] = $domain;
}
}
return array_unique($external);
}
$text = '訪問 https://a.com, https://b.com, 再看 https://a.com/page';
print_r(extract_external_links($text, 'example.com'));
/* 輸出:
Array
(
[0] => a.com
[1] => b.com
)
*/
?>
<?php
function extract_all_urls(string $text): array {
// 更完整的 URL 正則 (RFC 3986 簡化版)
$reg = '~
\b
(?:https?://)? # 可選協議
(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,} # 域名
(?:/[^\s<>"\']*)? # 可選路徑
(?:\?[^\s<>"\']*)? # 可選查詢
(?:#[^\s<>"\']*)? # 可選錨點
~x';
preg_match_all($reg, $text, $matches);
return $matches[0] ?? [];
}
?>
| 維度 | 表現 | 說明 |
|---|---|---|
| 時間複雜度 | O(N) | N 為文本長度,正則單次掃描 |
| 內存佔用 | 低 | 僅存儲匹配到的域名數組 |
| 準確率 | ~95% | 無法識別 JS 動態生成、編碼混淆、短鏈接的外鏈 |
| 誤報率 | 低 | 正規域名格式匹配,極少誤判 |
| 適用長度 | < 1MB | 超長文本建議分塊處理 |
Content-Security-Policy 頭限制資源加載源| 繞過手法 | 示例 | 防禦方案 |
|---|---|---|
| Unicode 同形字 | ɡoogle.com (ɡ 非 g) | IDN 規範化、Punycode 轉換 |
| 子域名欺騙 | evil.example.com.attacker.com | 提取有效頂級域 (eTLD+1) 比對 |
| 編碼混淆 | https://example.com/%65%76%69%6C | URL 解碼後再檢測 |
| 數據協議 | data:text/html, | 擴展正則匹配 data:、javascript: 協議 |
此工具函數代碼精簡、零依賴,適合集成到表單驗證、內容審核、API 參數檢查等場景,作為第一道外鏈防線。