1.12.1【必須】限定跳轉(zhuǎn)目標(biāo)地址
<script>location.href=__Redirection_URL__</script>
// 使用express實(shí)現(xiàn)的登錄成功后的回調(diào)跳轉(zhuǎn)頁(yè)面
// bad: 未校驗(yàn)頁(yè)面重定向地址
app.get("/login", (req, res) => {
// 若未登錄用戶訪問(wèn)其他頁(yè)面,則讓用戶導(dǎo)向到該處理函數(shù)進(jìn)行登錄
// 使用參數(shù)loginCallbackUrl記錄先前嘗試訪問(wèn)的url,在登錄成功后跳轉(zhuǎn)回loginCallbackUrl:
const { loginCallbackUrl } = req.query;
if (loginCallbackUrl) {
res.redirect(loginCallbackUrl);
}
});
// good: 白名單限定重定向地址
function isValidURL(sUrl) {
return !!((/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i).test(sUrl) || (/^[\w][\w/.\-_%]+$/i).test(sUrl) || (/^[/\\][^/\\]/i).test(sUrl));
}
app.get("/login", (req, res) => {
// 若未登錄用戶訪問(wèn)其他頁(yè)面,則讓用戶導(dǎo)向到該處理函數(shù)進(jìn)行登錄
// 使用參數(shù)loginCallbackUrl記錄先前嘗試訪問(wèn)的url,在登錄成功后跳轉(zhuǎn)回loginCallbackUrl:
const { loginCallbackUrl } = req.query;
if (loginCallbackUrl && isValidUrl(loginCallbackUrl)) {
res.redirect(loginCallbackUrl);
}
});
// good: 白名單限定重定向地址,通過(guò)返回html實(shí)現(xiàn)
function isValidURL(sUrl) {
return !!((/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i).test(sUrl) || (/^[\w][\w/.\-_%]+$/i).test(sUrl) || (/^[/\\][^/\\]/i).test(sUrl));
}
app.get("/login", (req, res) => {
// 若未登錄用戶訪問(wèn)其他頁(yè)面,則讓用戶導(dǎo)向到該處理函數(shù)進(jìn)行登錄
// 使用參數(shù)loginCallbackUrl記錄先前嘗試訪問(wèn)的url,在登錄成功后跳轉(zhuǎn)回loginCallbackUrl:
const { loginCallbackUrl } = req.query;
if (loginCallbackUrl && isValidUrl(loginCallbackUrl)) {
// 使用encodeURI,過(guò)濾左右尖括號(hào)與雙引號(hào),防止逃逸出包裹的雙引號(hào)
const redirectHtml = `<script>location.href = "${encodeURI(loginCallbackUrl)}";</script>`;
res.end(redirectHtml);
}
});
關(guān)聯(lián)漏洞:中風(fēng)險(xiǎn) - 任意URL跳轉(zhuǎn)漏洞
更多建議: