ASP.NET網站專案,使用Query String傳送Base64編碼後的參數值(例如: MyApp.aspx?d=RGFya3RocmVhZCBSb2NrcyEh),卻發現測試有時成功有時失敗,最後查出是程式產生URL時沒有對Base64字串進行UrlEncode編碼所造成的問題。

Base64編碼使用了 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",再加上補齊長度的1個或2個"="符號,共計65個字元。其中只有"="與"+"在Query String有特殊含意,"="用來連接參數名稱與值、"+"則用來表示" "(空白符號)。”?a=ab=&b=123”的寫法仍在程式Request.QueryString的識別容忍範圍,可靠"&"符號或結尾認出a = "ab=",但若參數值出現"+",例如: ?a=X+Y,則Request.QueryString["a"]的結果就會變成"X Y"。換句話說,若是Base64的編碼結果好死不死出現"+"(機率不低),在伺服器端取QueryString變數時就會被解讀成" "(空白),導致Base64解碼錯誤。

要解決這個問題,只需使用Server.UrlEncode()或HttpUtility.UrlEncode()對Base64編碼後的字串再做一次編碼即可,如此"+"會變成%2b、"="會被轉成%3d,在Request.QueryString取值時就可正確無誤地還原。

除此之外,若要交由Javascript端處理,則可使用encodeURIComponent()進行編碼(注意: escape()、encodeURI()、encodeURIComponent()的編碼原則不盡相同,這裡有一篇好文章說明三者的差異);再不然,由於其中關鍵只在於"+",而空白符好並非Base64的合法字元,若傳入參數無法加工,可在Server端將讀取結果中的" "再換回"+",雖然嚴謹度較差,但也是種解法。

以下是重現問題及展示解法的範例:

<%@ Page Language="C#" %>
 
<!DOCTYPE html>
 
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        Encoding enc = Encoding.UTF8;
        base64_sample.Value =
            Convert.ToBase64String(enc.GetBytes("<A>"));
        //故意產生含"+"符號的Base64編碼
        string mode = Request["mode"];
        string b64 = Request["b64"];
        if (!string.IsNullOrEmpty(mode))
        {
            Response.ContentType = "text/plain";
            if (mode == "decode")
            {
                try
                {
                    string res = enc.GetString(Convert.FromBase64String(b64));
                    Response.Write("Result=" + res);
                }
                catch (Exception ex)
                {
                    Response.Write("Error: " + ex.Message);
                }
            }
            else if (mode == "decode-fix")
            {
                string res = enc.GetString(
                    Convert.FromBase64String(FixBase64FromQueryString(b64)));
                Response.Write("Result(Fixed)=" + res);
            }
            Response.End();
        }
            
    }
    //由於QueryString中Base64的"+"會被解讀成空白符號,故用函數還原
    string FixBase64FromQueryString(string b)
    {
        return b.Replace(" ", "+");
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Base64 Test</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.2.js" 
     type="text/javascript"> </script>  
    <script>
        $(function () {
            $("input").click(function () {
                var v = $("#base64_sample").val();
                var m = "decode";
                //b2鈕,使用encodeURIComponent對Base64編碼內容再做編碼
                //注意: escape()或encodeURI()不會轉換"+",不要用錯
                if (this.id == "b2") v = encodeURIComponent(v);
                //b3鈕,參數維持原貌(含"+"),在Server端再做處理
                if (this.id == "b3") m += "-fix";
                var url = location.href + "?mode=" + m + "&b64=" + v;
                $("#dUrl").text(url);
                $("#fDisp").attr("src", url);
            });
        });
    </script>
    <style>
        body { padding: 10px; }
        div { margin: 5px; }
        input { width: 120px; margin-right: 10px; }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <input type="hidden" id="base64_sample" runat="server" />
    <div>
    <input type="button" id="b1" value="直接傳送" />
    <input type="button" id="b2" value="參數編碼後傳送" />
    <input type="button" id="b3" value="Server端修正" />
    </div>
    <div id="dUrl">&nbsp;</div>
    <iframe id="fDisp" style="width: 400px;"></iframe>
    </form>
</body>
</html>

 

Test 1 沒進行UrlEncode或encodeURIComponent()就直接當成參數傳送會導致錯誤

 

Test 2 便用encodeURIComponent()轉換,"+"會被轉成%2B可避開問題

 

Test 3 ASP.NET Server端接入Request[“b64”]後再將空白換回"+"


Comments

# by 讚讚讚

讚讚讚

# by Dean Tsai

先前有看過這篇文章,以為我寫的程式完美可用了,結果不知為何"偶而"會出現錯誤 小弟正愁眉不展,回頭再細看才發現您說jQuery遇到加號會出事!! 黑暗大大認真踩坑的精神令人肅然起敬 感恩大大,讚嘆大大

Post a comment