第二十三節:Asp.Net Core中的幾種安全防護


一 . 客戶端IP白名單限制

1.通過中間件檢測

  新建中間件類AdminSafeListMiddleware,獲取白名單ip,通過比較byte值來比較訪問的ip是否在白名單中,如果不在,則返回401無權限。 然后在ConfigureService中進行全局攔截app.UseMiddleware<AdminSafeListMiddleware>(Configuration["IpSafeList"]);

注:該案例完全可以改造成黑名單攔截。

PS:

A. ::1 代表localhost或者127.0.0.1

B. context.Connection.RemoteIpAddress; HttpContext對象獲取遠程訪問地址,類似的還有RemotePort、LocalIpAddress、LocalPort 。

代碼分享:

 1 public class AdminSafeListMiddleware
 2     {
 3         private readonly RequestDelegate _next;
 4         private readonly string _adminSafeList;
 5         public AdminSafeListMiddleware(RequestDelegate next,string adminSafeList)
 6         {
 7             _adminSafeList = adminSafeList;
 8             _next = next;
 9         }
10         public async Task Invoke(HttpContext context)
11         {
12             //if (context.Request.Method != "GET")
13             {
14                 var remoteIp = context.Connection.RemoteIpAddress;    //獲取遠程訪問IP
15                 string[] ip = _adminSafeList.Split(';');
16                 var bytes = remoteIp.GetAddressBytes();
17                 var badIp = true;
18                 foreach (var address in ip)
19                 {
20                     var testIp = IPAddress.Parse(address);
21                     if (testIp.GetAddressBytes().SequenceEqual(bytes))
22                     {
23                         badIp = false;
24                         break;    //直接跳出ForEach循環
25                     }
26                 }
27                 if (badIp)
28                 {
29                     context.Response.StatusCode = 401;
30                     return;
31                 }
32             }
33             await _next.Invoke(context);
34         }
35     }

 

2.通過操作過濾器攔截

  利用操作過濾器,Override方法OnActionExecuting,在方法執行前進行攔截。

 1     /// <summary>
 2     /// IP校驗過濾器
 3     /// </summary>
 4     public class ClientIpCheckFilter: ActionFilterAttribute
 5     {
 6 
 7         private readonly string _safelist;
 8         public ClientIpCheckFilter(IConfiguration configuration)
 9         {
10             _safelist = configuration["IpSafeList"];
11         }
12         /// <summary>
13         /// 方法執行前執行
14         /// </summary>
15         /// <param name="context"></param>
16         public override void OnActionExecuting(ActionExecutingContext context)
17         {
18             var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
19             string[] ip = _safelist.Split(';');
20             var badIp = true;
21             foreach (var address in ip)
22             {
23                 if (remoteIp.IsIPv4MappedToIPv6)
24                 {
25                     remoteIp = remoteIp.MapToIPv4();
26                 }
27                 var testIp = IPAddress.Parse(address);
28                 if (testIp.Equals(remoteIp))
29                 {
30                     badIp = false;
31                     break;
32                 }
33             }
34             if (badIp)
35             {
36                 context.Result = new StatusCodeResult(401);
37                 return;
38             }
39             base.OnActionExecuting(context);
40         }
41     }
 1         public void ConfigureServices(IServiceCollection services)
 2         {
 3             services.AddControllersWithViews(options =>
 4             {
 5                 //全局注冊的時候,過濾器中的構造函數中的注入對象會自動注入(不需要多此一舉進行傳入: options.Filters.Add(new ClientIpCheckFilter(Configuration));)
 6                 //1. 操作過濾器
 7                 options.Filters.Add(typeof(ClientIpCheckFilter));
 8                 //2. 頁面過濾器
 9                 //options.Filters.Add(new ClientIpCheckPageFilter(Configuration));
10             });
11         }

3. Razor Pages 過濾器

   同上操作過濾器類似。

 

二. 重定向攻擊

1. 定義

  Web 應用程序頻繁地將用戶重定向到登錄頁時他們訪問要求進行身份驗證的資源。 重定向通常包括returnUrl查詢字符串參數,以便用戶可以返回到最初請求的 URL 后它們已成功登錄。 對用戶進行身份驗證后,它們重定向到他們具有最初請求的 URL。因為請求的查詢字符串中指定的目標 URL, 則惡意用戶可能篡改在查詢字符串。 被篡改的查詢字符串可能允許將用戶重定向到外部、惡意站點的站點。 這一技術稱為開放重定向(或重定向)攻擊。

2. 案例分析

(1).需求

 一個網站,有些頁面是頁面是可以直接查看,有些頁面或操作需要登陸后才能看,在直接可以看的頁面點擊登錄,登錄成功后還是回到該頁面。分兩類:

 A. 某個頁面的按鈕觸發ajax請求需要登錄:如果沒有登錄,點擊后,提示沒有登錄,然后進入登錄頁,登錄成功后,進入發送ajax請求的那個頁面。

 B. 某個頁面的href鏈接需要登錄,如果沒有登錄,點擊后會直接進入登錄頁,登錄成功后,進入到哪個頁面,這里有兩種選擇

  ①. href要鏈接到的頁面

  ②. 觸發href的頁面,然后需要進入的話,再重新點擊一下這個鏈接

注:大部分情況這里傾向情況①,即href要鏈接到的目標頁面

(2).技術分析

 核心實現原理:進入登錄頁面會拼接 xxx?returnUrl=xxxx,在登錄頁面通過js截取returnUrl的值,傳入到校驗登錄的接口,校驗通過后,還需要通過IsLocalUrl校驗returnUrl的合法性,合法直接將該地址返回給前端,進行跳轉;不合法,將首頁地址返回給前端用於跳轉;如果returnUrl為空,表示是直接進入到登錄頁,同樣也是返回首頁地址。

 校驗是否登錄:這里通過Session中是否有值來校驗,還需要區分ajax請求和非ajax請求,如果是ajax請求,返回401給前端,然后前端跳轉到登錄頁,並且將?returnUrl=windows.location.pathName,傳遞給登錄頁;如果是非ajax請求,則在過濾器中通過request.Path.ToString()獲取請求地址(即href要鏈接到的頁面),進行傳遞。

ps:如果是要進入到觸發href的頁面,則前端要進行傳遞returnUrl,然后在過濾器中通過Query獲取,進行傳遞即可。

登錄頁面和校驗登錄的代碼

 1 <html>
 2 <head>
 3     <meta name="viewport" content="width=device-width" />
 4     <title>LoginIndex</title>
 5     <script src="~/lib/jquery/dist/jquery.js"></script>
 6     <script>
 7         $(function () {
 8             //獲取某個Url的參數值
 9             function GetUrlParam(name) {
10                 var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
11                 var r = window.location.search.substr(1).match(reg);
12                 if (r != null) {
13                     return unescape(r[2]);
14                 } else {
15                     return "";
16                 }
17             }
18 
19             $("#j_login").click(function () {
20                 //獲取地址欄returnUrl參數
21                 var returnUrl = GetUrlParam("returnUrl");
22                 $.post("/Home/CheckLogin", { userAccount: "admin", pwd: "12345", returnUrl: returnUrl }, function (data) {
23                     if (data.status == "ok") {
24                         alert("登錄成功");
25                         window.location.href = data.data;
26                     } else {
27                         window.location.href = data.data;
28                     }
29 
30                 });
31             });
32         });
33     </script>
34 </head>
35 <body>
36     我是登錄頁面
37     <button id="j_login">點我登錄</button>
38 </body>
39 </html>
        /// <summary>
        /// 登錄接口
        /// </summary>
        /// <param name="userAccount"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public IActionResult CheckLogin(string userAccount, string pwd, string returnUrl)
        {
            //1. 執行登錄校驗邏輯
            //這里假設校驗通過
            HttpContext.Session.SetString("userName", userAccount);

            //2. 判斷returnUrl中是否有值,如果沒有值表示是直接進入到登錄頁面的,如果有值表示從別的頁面跳轉過來的
            if (string.IsNullOrEmpty(returnUrl))
            {
                //表示是直接進入到的登錄頁,則跳轉到主頁
                return Json(new { status = "ok", data = "/Home/Index" });
            }
            else
            {
                if (Url.IsLocalUrl(returnUrl))
                {
                    //是本地地址,則跳轉過去
                    return Json(new { status = "ok", data = returnUrl });
                }
                else
                {
                    //非本地地址(如:非法地址),則跳轉到主頁
                    return Json(new { status = "ok", data = "/Home/Index" });
                }
            }
        }

校驗是否登錄過濾器代碼:

 1      /// <summary>
 2     /// 校驗是否登錄的過濾器
 3     /// </summary>
 4     public class CheckLogin : ActionFilterAttribute
 5     {
 6         /// <summary>
 7         /// 方法執行前執行
 8         /// </summary>
 9         /// <param name="context"></param>
10         public override void OnActionExecuting(ActionExecutingContext context)
11         {
12 
13             var _session = context.HttpContext.Session;
14             //判斷是否登錄
15             var isLogin = _session.GetString("userName");
16             if (string.IsNullOrEmpty(isLogin))
17             {
18                 var request = context.HttpContext.Request;
19                 var returnUrl = request.Path.ToString();  //獲取當前請求地址 
20                 //判斷請求類型
21                 if (IsAjaxRequest(request))
22                 {
23                     //表示是ajax請求
24                     context.Result = new ContentResult { StatusCode = 401, Content = "您沒有登錄" };
25                     return;
26                 }
27                 else
28                 {
29                     //截斷請求
30                     context.Result = new RedirectResult($"/Home/LoginIndex?returnUrl={returnUrl}");
31                     return;
32                 }
33             }
34         }
35 
36         /// <summary>
37         /// 判斷該請求是否是ajax請求
38         /// </summary>
39         /// <param name="request"></param>
40         /// <returns></returns>
41         private bool IsAjaxRequest(HttpRequest request)
42         {
43             string header = request.Headers["X-Requested-With"];
44             return "XMLHttpRequest".Equals(header);
45         }
46     }

其它子頁面代碼

 1 <html>
 2 <head>
 3     <meta name="viewport" content="width=device-width" />
 4     <title>Index1</title>
 5     <style>
 6         .now {
 7             background-color: red;
 8         }
 9     </style>
10     <script src="~/lib/jquery/dist/jquery.js"></script>
11 
12     <script>
13         $(function () {
14             console.log(window.location.pathname);
15 
16             $("#j_btn1").click(function () {
17                 window.location.href = encodeURI("/Home/Index1");
18             })
19             $("#j_btn2").click(function () {
20                 window.location.href = encodeURI("/Home/Index2");
21             })
22 
23             $("#j_login").click(function () {
24                 window.location.href = encodeURI("/Home/LoginIndex?returnUrl=" + window.location.pathname);
25             });
26 
27             $("#j_btn3").click(function () {
28                 $.ajax({
29                     url: "/Home/GetMsg",
30                     type: "post",
31                     data: {},
32                     datatype: "json",
33                     success: function (data) {
34                         console.log(data);
35                         if (data.status == "ok") {
36                             alert(data.msg);
37                         } else {
38                             alert(data.msg);
39                         }
40                     },
41                     //當安全校驗未通過的時候進入這里
42                     error: function (xhr) {
43                         alert("您沒有登錄");
44                         window.location.href = encodeURI("/Home/LoginIndex?returnUrl=" + window.location.pathname);
45                     }
46                 });
47             })
48             $("#j_btn4").click(function () {
49                 window.location.href = "/Home/DetailViews";
50             })
51 
52 
53         });
54 
55     </script>
56 
57 
58 </head>
59 <body>
60     <button id="j_btn1" class="now">頁面1</button>
61     <button id="j_btn2">頁面2</button>
62     <button id="j_login">登錄</button>
63 
64     <br />
65     <button id="j_btn3">獲取信息</button>
66     <button id="j_btn4">進入詳情頁</button>
67 
68     <br />
69     我是頁面一
70 </body>
71 </html>
Index1
 1 @{
 2     Layout = null;
 3 }
 4 
 5 <!DOCTYPE html>
 6 
 7 <html>
 8 <head>
 9     <meta name="viewport" content="width=device-width" />
10     <title>Index2</title>
11     <style>
12         .now {
13             background-color: red;
14         }
15     </style>
16     <script src="~/lib/jquery/dist/jquery.js"></script>
17 
18     <script>
19         $(function () {
20             console.log(window.location.pathname);
21 
22             $("#j_btn1").click(function () {
23                 window.location.href = encodeURI("/Home/Index1");
24             })
25             $("#j_btn2").click(function () {
26                 window.location.href = encodeURI("/Home/Index2");
27             })
28 
29             $("#j_login").click(function () {
30                 window.location.href = encodeURI("/Home/LoginIndex?returnUrl=" + window.location.pathname);
31             });
32 
33             $("#j_btn3").click(function () {
34                 $.ajax({
35                     url: "/Home/GetMsg",
36                     type: "post",
37                     data: {},
38                     datatype: "json",
39                     success: function (data) {
40                         console.log(data);
41                         if (data.status == "ok") {
42                             alert(data.msg);
43                         } else {
44                             alert(data.msg);
45                         }
46                     },
47                     //當安全校驗未通過的時候進入這里
48                     error: function (xhr) {
49                         alert("您沒有登錄");
50                         window.location.href = encodeURI("/Home/LoginIndex?returnUrl=" + window.location.pathname);
51                     }
52                 });
53             })
54             $("#j_btn4").click(function () {
55                 window.location.href = "/Home/DetailViews";
56             })
57 
58 
59         });
60 
61     </script>
62 
63 
64 </head>
65 <body>
66     <button id="j_btn1" >頁面1</button>
67     <button id="j_btn2" class="now">頁面2</button>
68     <button id="j_login">登錄</button>
69 
70     <br />
71     <button id="j_btn3">獲取信息</button>
72     <button id="j_btn4">進入詳情頁</button>
73 
74     <br />
75     我是頁面2
76 </body>
77 </html>
Index2

 

三. 跨站腳本攻擊(XSS)

1. 定義

  跨站點腳本 (XSS) 是一個安全漏洞,這會使攻擊者將客戶端腳本 (通常是 JavaScript) 放入網頁。 當其他用戶加載攻擊者的腳本將運行受影響的頁面時,攻擊者可以竊取 cookie 和會話令牌,更改通過 DOM 操作網頁的內容或將瀏覽器重定向到另一頁。 當應用程序接受用戶輸入, 並將其輸出到頁面而無需驗證、 編碼或進行轉義,通常會發生 XSS 漏洞。

 2. 三種編碼

  Url、Html、JS三種編碼,使用HttpUtility類,需要依賴程序集【System.Web】

 1   public void TestCode()
 2         {
 3             string msg = "http://www.baidu.com";         
 4             //Url編碼和解碼
 5             var urlCode=  HttpUtility.UrlEncode(msg);
 6             var urlMsg = HttpUtility.UrlDecode(urlCode);
 7             //Html編碼和解碼
 8             var htmlCode = HttpUtility.HtmlEncode(msg);
 9             var htmlMsg = HttpUtility.HtmlDecode(htmlCode);
10             //JS編碼和解碼
11             var jsCode = HttpUtility.JavaScriptStringEncode(msg);
12         }

 

四. 跨站點請求偽造攻擊(XSRF/CSRF)

1. 定義

  跨站點請求偽造(也稱為 XSRF 或 CSRF)是一種針對 web 托管應用的攻擊,惡意 web 應用可能會影響客戶端瀏覽器與信任該瀏覽器的web 應用之間的交互。 這些攻擊是可能的,因為 web 瀏覽器會自動向網站發送每個請求的身份驗證令牌。 這種形式的攻擊也稱為一鍵式攻擊或會話riding ,因為攻擊利用用戶以前的經過身份驗證的會話。

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM