營銷活動、企業內部應用常需「僅微信可訪問」。最常見做法是檢測 `User-Agent` 中是否包含 `MicroMessenger`。但 UA 極易偽造 (瀏覽器擴展、代理、curl 均可修改),本文提供**基礎版、進階版、配合 JS/Token 的多層防禦方案**,並說明為何無法 100% 防偽造。
<?php
// 入口文件最頂部
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (stripos($userAgent, 'MicroMessenger') === false) {
// 非微信環境
header("Content-Type: text/html; charset=UTF-8");
exit('請在微信客戶端打開此頁面');
}
// 繼續正常業務邏輯
?>
<?php
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$isWechat = stripos($userAgent, 'MicroMessenger') !== false;
$isWPWechat = stripos($userAgent, 'Windows Phone') !== false && stripos($userAgent, 'MicroMessenger') !== false;
if (!$isWechat && !$isWPWechat) {
exit('非微信環境禁止訪問');
}
<?php
// 任意腳本偽造微信 UA 訪問你的頁面
$ch = curl_init('https://your-domain.com/page.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERAGENT => 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.0 NetType/WIFI Language/zh_CN',
CURLOPT_SSL_VERIFYPEER => false,
]);
$response = curl_exec($ch);
// 返回正常業務內容,防禦失效
curl_close($ch);
?>
Chrome 安裝 User-Agent Switcher 擴展,30 秒即可繞過。
<script>
// 頁面加載時檢測
(function() {
var ua = navigator.userAgent;
if (!/MicroMessenger/i.test(ua)) {
document.body.innerHTML = '請在微信中打開';
// 可選:上報偽造嘗試
fetch('/api/log_fake_ua', {method:'POST', body:JSON.stringify({ua:ua})});
}
})();
</script>
<noscript><style>body{display:none!important}</style><div style="padding:20px;text-align:center;">請啟用 JavaScript 並在微信中打開</div></noscript>
作用:阻擋無頭瀏覽器、curl、簡單爬蟲。但 Headless Chrome/Puppeteer 可執行 JS 繞過。
<?php
// 後端生成 JSSDK 簽名
$jsApiTicket = get_jsapi_ticket(); // 從 AccessToken 換取,緩存 7200s
$noncestr = 'Wm3WZYTPz0wzccnW';
$timestamp = time();
$url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$string = "jsapi_ticket=$jsApiTicket&noncestr=$noncestr×tamp=$timestamp&url=$url";
$signature = sha1($string);
// 前端配置
wx.config({
debug: false,
appId: 'YOUR_APPID',
timestamp: $timestamp,
nonceStr: '$noncestr',
signature: '$signature',
jsApiList: ['checkJsApi'] // 僅需驗證配置成功
});
wx.ready(function() {
// 配置成功 => 確認為合法微信環境 (域名已在公眾平台綁定)
window.__WX_VERIFIED__ = true;
});
wx.error(function(res) {
// 配置失敗 => 可能偽造或域名未授權
document.body.innerHTML = '微信驗證失敗,請在授權域名中打開';
});
?>
原理:JSSDK 簽名包含 `url` 參數,攻擊者無法在非授權域名下構造合法簽名 (除非竊取 `jsapi_ticket`)。
<?php
// 入口無 code 參數 => 重定向到微信授權
if (!isset($_GET['code'])) {
$redirect = urlencode('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
$url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=YOUR_APPID&redirect_uri=$redirect&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
header("Location: $url");
exit;
}
// 有 code => 換取 openid
$code = $_GET['code'];
$tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=YOUR_APPID&secret=YOUR_SECRET&code=$code&grant_type=authorization_code";
$json = file_get_contents($tokenUrl);
$data = json_decode($json, true);
if (empty($data['openid'])) {
exit('授權失敗');
}
$openid = $data['openid'];
// 此時確認為真實微信用戶,可綁定業務邏輯
$_SESSION['wechat_openid'] = $openid;
?>
優點:無法偽造 (需微信服務器簽發 code)、自動獲取用戶唯一標識。缺點:首次訪問會彈出「正在獲取用戶信息」提示、需公眾號認證、增加一次跳轉延遲。
| 場景 | 方案組合 | 偽造成本 | 用戶體驗 |
|---|---|---|---|
| 普通營銷頁、文章分享 | UA + JS 檢測 | 低 (繞過簡單) | 無感 |
| 優惠券領取、表單提交 | UA + JSSDK 配置驗證 | 中 (需竊取 ticket) | 無感 |
| 紅包、支付、個人中心、投票 | OAuth2.0 靜默授權 (snsapi_base) | 極高 (需盜用戶手機/微信) | 首次跳轉 1-2s |
| 高價值交易、實名業務 | OAuth2.0 用戶信息授權 (snsapi_userinfo) + 後端風控 | 極高 | 需用戶確認授權 |