開放重新導向漏洞
| , 4 minutes reading.
1. 定義
開放重新導向 (Open Redirect) 漏洞發生在 Web 應用程式接受用戶控制的參數,並在未經適當驗證的情況下使用它將用戶重新導向到外部 URL。
雖然開放重新導向本身看起來危害較低,但它們對攻擊者非常有價值,因為它們濫用了用戶對合法網域的信任。使用 trusted-bank.com/redirect?url=evil.com 的釣魚連結比直接指向 evil.com 的連結看起來更合法。
2. 技術原理
許多合法應用程式需要重新導向功能:
- 登入後重新導向:在認證後將用戶返回到其原始頁面
- 行銷追蹤:在到達目的地之前透過追蹤系統重新導向
- 單一登入:在身分提供者和應用程式之間重新導向
易受攻擊的模式:
https://example.com/login?redirect=https://evil.com成功登入後,應用程式在未經驗證的情況下重新導向到 evil.com。
常見的易受攻擊參數:
redirect、redirect_uri、return、returnUrlnext、url、target、destinationcontinue、goto、out、redir
繞過技術: 攻擊者使用各種技巧繞過弱驗證:
# 基本
?redirect=https://evil.com
# 協定相對
?redirect=//evil.com
# 網域混淆
?redirect=https://example.com.evil.com
?redirect=https://[email protected]
?redirect=https://evil.com/example.com
# URL 編碼
?redirect=https%3A%2F%2Fevil.com
# 參數污染
?redirect=https://example.com&redirect=https://evil.com3. 攻擊流程
sequenceDiagram
participant Victim as 受害者
participant Attacker as 攻擊者
participant TrustedSite as 受信任網站
participant PhishingSite as 釣魚網站
Attacker->>Victim: 發送帶有連結的釣魚郵件<br/>trusted-bank.com/redirect?url=evil.com
Note over Victim: 連結看起來合法<br/>網域是 trusted-bank.com
Victim->>TrustedSite: 點擊連結到 trusted-bank.com
TrustedSite->>TrustedSite: 處理重新導向參數
TrustedSite-->>Victim: 302 重新導向到 evil.com
Victim->>PhishingSite: 瀏覽器跟隨重新導向
Note over PhishingSite: 偽造的登入頁面<br/>與 trusted-bank.com 完全相同
Victim->>PhishingSite: 輸入憑證
PhishingSite->>Attacker: 憑證被擷取4. 真實案例:Google OAuth 開放重新導向 (2016)
目標: Google OAuth 2.0 認證流程。 漏洞類別: OAuth 回呼中的開放重新導向。
漏洞背景: 2016 年,研究員 Egor Homakov 發現 Google 的 OAuth 實作在 redirect_uri 參數驗證中存在開放重新導向漏洞。
攻擊過程:
- OAuth 要求應用程式註冊允許的
redirect_uri值。 - Google 對某些註冊網域的驗證過於寬鬆。
- 攻擊者可以找到一個在其自身網域上存在開放重新導向的合法應用程式。
- 鏈條:Google OAuth -> 合法應用(存在開放重新導向)-> 攻擊者網站。
攻擊鏈範例:
1. 用戶點擊: accounts.google.com/oauth?redirect_uri=legitimate-app.com
2. Google 驗證: legitimate-app.com 已註冊 - 通過
3. Google 重新導向到: legitimate-app.com/callback?code=AUTH_CODE
4. legitimate-app.com 存在開放重新導向: /goto?url=evil.com
5. 攻擊者收到: evil.com?code=AUTH_CODE
6. 攻擊者用 code 交換存取權杖影響: 這允許攻擊者透過鏈接漏洞竊取 OAuth 權杖。Google 加強了其 redirect_uri 驗證,並鼓勵使用更嚴格的匹配模式。
5. 深度防禦策略
A. 白名單驗證
僅允許重新導向到預先批准的目的地。
const ALLOWED_DOMAINS = [
'example.com',
'subdomain.example.com',
'partner-site.com'
];
function validateRedirect(url) {
try {
const parsed = new URL(url);
return ALLOWED_DOMAINS.includes(parsed.hostname);
} catch {
return false;
}
}B. 僅允許相對 URL
將重新導向限制為相對路徑(僅限同源)。
function safeRedirect(url) {
// 僅允許以 / 開頭的路徑
if (url.startsWith('/') && !url.startsWith('//')) {
return url;
}
return '/'; // 預設安全重新導向
}警告: 注意協定相對 URL(//evil.com)。
C. 間接參照映射
使用權杖代替直接 URL。
const REDIRECT_MAP = {
'dashboard': '/user/dashboard',
'settings': '/user/settings',
'logout': '/auth/logout'
};
// 用法: /redirect?target=dashboard
function handleRedirect(target) {
const destination = REDIRECT_MAP[target];
if (destination) {
return redirect(destination);
}
return redirect('/');
}D. 用戶確認
對於外部重新導向,顯示警告頁面。
<div class="redirect-warning">
<h2>您即將離開 example.com</h2>
<p>您正在被重新導向到外部網站:</p>
<p><strong>https://external-site.com</strong></p>
<p>我們不對外部網站的內容負責。</p>
<a href="https://external-site.com">繼續</a>
<a href="/">返回 example.com</a>
</div>E. 嚴格的 OAuth 設定
對於 OAuth 實作:
- 精確匹配: 要求
redirect_uri精確匹配,而非前綴匹配。 - 禁止萬用字元: 避免在註冊的重新導向 URI 中使用萬用字元模式。
- 僅限 HTTPS: 在正式環境中僅允許 HTTPS 重新導向 URI。
# 好:僅精確匹配
已註冊: https://app.example.com/oauth/callback
有效: https://app.example.com/oauth/callback
無效: https://app.example.com/oauth/callback/evil
# 壞:前綴匹配
已註冊: https://app.example.com/
有效: https://app.example.com/anything <- 危險