前言
最近同事詢問,為什麼 http://rm-nb/ap1/(A(abc))/Login 跟 http://rm-nb/ap1/Login 一樣,都會被 Login Controller 處理到呢?
在 RouteConfig 明明沒有設定那樣子的 Route Path Pattern 呀!
研究
看到那個 Url 就像是 Cookieless 設定成 true ,所以把 SessionId 的值,放在 Url 上面。
但 web.config 的 cookieless 是設定 “UseCookies” (預設也是),又不是設定 “UseUri” 這是怎麼一回事呢?
1 | <sessionState cookieless="UseCookies" mode="InProc" timeout="30" /> |
參考 Understand How the ASP.NET Cookieless Feature Works(.NET 2.X) 可以發現, URL 上面的值可以是 A(XXXX), S(XXXX) 或是 F(XXXX) ,所以上面的 (A(XXXX)) 是符合的。如果你改成 a(XXXX) or b(XXXX) 就會去 Match 設定的 Route Path Pattern 哦!
但目前有個問題是,如果有黑箱去掃你的 Url 時,這時它在 URL 上去加入 A(XXXX) 時,而你的 Response 如果跟沒有加 A(XXXX) 時一樣,他就會認為有問題。
這…怎麼辦呢?
為什麼有加 A(XXXX) 跟沒加的狀況是一樣的呢?
在 Global.asax.cs 的 Application_BeginRequest event 中,
可以發現,加 A(XXXX) 跟沒加 的 Context.Request.Url 是一樣的,
那結果當然就會是一樣的。
為什麼加 A(XXXX) 跟沒加的 URL 在 Request.Url 會變成一樣呢?
從 Possible Bug With ASP.NET MVC 3 Routing? 可以知道,在 HttpContext Init 時,就會檢查 Cookieless 的部份,如果有 SessionId 在 Url 上,就會將它移除(CookielessHelper.RemoveCookielessValuesFromPath()),所以我們在 Application_BeginRequest event 中看到有加 A(XXXX) 跟沒加 的 Context.Request.Url 是一樣的。
1 | private void Init(HttpRequest request, HttpResponse response) |
解法
從 System.Web.Security 的 CookielessHelperClass RemoveCookielessValuesFromPath Method 可以發現,它將 SessionId 從 Url 中移除,並將它的值寫到了 Response._appPathModifier 這個私有變數之中,
從下面2張圖可以發現,取到的 Url 值都一樣,而有加 A(XXXX) 的值被移到了 Response._appPathModifier 之中。
即然知道它會把值寫到 Response._appPathModifier 之中,如果程式確定不會 UseUri ,
就可以檢查 Response._appPathModifier 如果值不為 null ,就噴 404 的錯誤,如下,
1 | protected void Application_BeginRequest(object sender, EventArgs e) |
- 補充: 加入 Deny HTTP GET with request body
有時,黑箱也會測試在 Get 時,給你 Body 的資料,看你的程式會不會擋,也可以在 BeginRequest 多加入檢查如果 Get 又有 Body 的話,就回 403,如下,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected void Application_BeginRequest(object sender, EventArgs e)
{
//到這時,Url已被改成沒包含SessionId
//但它的值會放在 Response._appPathModifier 變數之中
var appPathModifierFieldInfo = Context.Response.GetType().GetField("_appPathModifier",
BindingFlags.NonPublic | BindingFlags.Instance);
var appPathModifier = appPathModifierFieldInfo.GetValue(Context.Response);
if (appPathModifier != null)
{
//url 中有 SessionId
throw new HttpException(404, "Not found");
}
//deny HTTP GET with request body
if(Request.HttpMethod == "GET" && Request.ContentLength > 0)
{
throw new HttpException(403, "Forbidden");
}
}
參考資料
Understand How the ASP.NET Cookieless Feature Works(.NET 2.X)
ASP.NET Session State .NET 1.X
Why do all ASP.NET MVC websites allow and ignore the string “/(F())/” in front of ANY URL?
Possible Bug With ASP.NET MVC 3 Routing?
Cannot disable ASP.NET cookieless mode