使用簽名來保證ASP.NET MVC OR WEBAPI的介面安全

来源:http://www.cnblogs.com/kklldog/archive/2016/09/22/webapi-signature.html
-Advertisement-
Play Games

當我們開發一款App的時候,App需要跟後臺服務進行通信獲取或者提交數據。如果我們沒有完善的安全機制則很容易被別用心的人偽造請求而篡改數據。 所以我們需要使用某種安全機制來保證請求的合法。現在最常用的辦法是給每個http請求添加一個簽名,服務端來驗證簽名的合法性,如果簽名合法則執行響應的操作,如果簽 ...


當我們開發一款App的時候,App需要跟後臺服務進行通信獲取或者提交數據。如果我們沒有完善的安全機制則很容易被別用心的人偽造請求而篡改數據。
所以我們需要使用某種安全機制來保證請求的合法。現在最常用的辦法是給每個http請求添加一個簽名,服務端來驗證簽名的合法性,如果簽名合法則執行響應的操作,如果簽名非法則直接拒絕請求。

簽名演算法

簽名演算法一般都使用Hash散列演算法,常用的有MD5,SHA系列演算法。這些演算法可以根據不同的輸入,計算出不同的結果,而且碰撞的概率很低。
簽名演算法跟加密演算法不是一回事。很多同學都會說使用MD5加密一下,其實這是錯誤的。簽名演算法不能恢複原來的數據,因為它本身並不包含原來數據的信息。
而加密方法不同,加密方法是可以根據加密結果重新推算出原來的數據的。
HMAC SHA作為一種更加安全的簽名演算法,使用一個Key來影響簽名的結果。這樣同樣的輸入配合不同的Key可以得出不同的簽名,更加安全。

 public static string HmacSHA256(string secretKey,string plain)
        {
            var keyBytes = Encoding.UTF8.GetBytes(secretKey);
            var plainBytes = Encoding.UTF8.GetBytes(plain);

            using (var hmacsha256 = new HMACSHA256(keyBytes))
            {
                var sb = new StringBuilder();
                var hashValue = hmacsha256.ComputeHash(plainBytes);
                foreach (byte x in hashValue)
                {
                    sb.Append(String.Format("{0:x2}", x));
                }
                return sb.ToString();
            }
        }

簽名的參數

有了簽名演算法,那麼我們簽名的內容哪裡來呢?
一般我們使用http請求的queryString然後加上時間戳還有隨機數來作為簽名的參數。

 public static string MakeSignPlain(SortedDictionary<string,string> queryString,string time,string random )
        {
            var sb = new StringBuilder();
            foreach (var keyValue in queryString)
            {
                sb.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
            }
            if (sb.Length>1)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            sb.Append(time);
            sb.Append(random);

            return sb.ToString().ToUpper();
        }

驗證簽名

驗證簽名就是簡單的比較服務端生產的簽名跟客戶端生產的簽名是否一直。
要註意的一點是最好驗證下時間戳,跟服務端時間比較前後不能相差5分鐘。這也是一個簡單的防Replay Attack的手段。

 public static bool Valid(string requestSign,string signPlain,string time, string secretKey)
        {
            if (string.IsNullOrEmpty(time)||string.IsNullOrEmpty(requestSign)||string.IsNullOrEmpty(signPlain))
            {
                return false;
            }
            //is in range
            var now = DateTime.Now;
            long requestTime =0;
            if (long.TryParse(time,out requestTime))
            {
                var max = now.AddMinutes(5).ToString("yyyyMMddHHmmss");
                var min = now.AddMinutes(-5).ToString("yyyyMMddHHmmss");
                if (!(long.Parse(max) >= requestTime && long.Parse(min) <= requestTime))
                {
                    return false;
                }
              
            }
            else
            {
                return false;
            }

            //hashmac
            var sign = Encryption.HmacSHA256(secretKey, signPlain);

            return requestSign.Equals(sign, StringComparison.CurrentCultureIgnoreCase);
        }

ApiController基類

有了上面這些鋪墊我們就可以在基類完成簽名的驗證了。客戶端需要把上面提到的時間戳,隨機數,簽名和客戶端的ID放入http請求的headers裡面。
我們在基類的OnActionExecuting里取出這些數據組合成簽名的參數,然後根據客戶端ID獲取簽名的Key,然後使用同樣的簽名演算法計算簽名。並且比較客戶端的簽名跟服務端的簽名是否一致。
這裡就不演示了。

預防Replay Attack

預防重放攻擊主要有兩點:

  • 校驗時間戳的範圍
    時間戳跟伺服器時間相差在一個合理的範圍內視為合法。
  • 緩存簽名
    每次請求都去判斷下簽名是否出現過。如果出現過則視為非法請求。
    因為有時間戳跟隨機數的存在,所以理論上每次請求的簽名是不可能重覆的。

客戶端調用

這裡演示一下C#簽名並且調用http介面的代碼

 [TestMethod()]
        public void GetUserTest()
        {
            string url = "http://localhost:8090/api/test/GetUser";
            string userId = "A39891D4-6CEF-4538-A562-3A422CA9C17A";
            string appId = "100001";
            string secretKey = "M/vkPOWXgBa7GnRd73t7j+jsKfbZtb+f";
            string rumdon = Guid.NewGuid().ToString();
            string time = DateTime.Now.ToString("yyyyMMddHHmmss");

            //make signture plain text
            var sortDict = new SortedDictionary<string, string>()
            {
                {"userId",userId }
            };
            var signPlain = new StringBuilder();
            foreach (var keyValue in sortDict)
            {
                signPlain.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
            }
            if (signPlain.Length > 1)
            {
                //remove last &
                signPlain.Remove(signPlain.Length - 1, 1);
            }
            signPlain.Append(time);
            signPlain.Append(random);

            Console.WriteLine("sign plain:{0}", signPlain.ToString().ToUpper());
            //make sign
            var sign = Encryption.HmacSHA256(secretKey, signPlain.ToString().ToUpper());
            Console.WriteLine("sign:{0}", sign);

            string requestUrl = string.Format("{0}?{1}={2}", url, "userId", userId);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
            request.Method = "GET";
            //add headers
            request.Headers.Add("time", time);
            request.Headers.Add("appId", appId);
            request.Headers.Add("random", random);
            request.Headers.Add("sign", sign);
            //
            //start request
            try
            {
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    var responseStream = response.GetResponseStream();
                    if (responseStream != null)
                    {
                        using (StreamReader reader = new StreamReader(responseStream))
                        {
                            var content = reader.ReadToEnd();

                            Console.WriteLine(content);
                        }
                    }
                }
            }
            catch (WebException ex)
            {
                using (HttpWebResponse response = (HttpWebResponse)ex.Response)
                {
                    var responseStream = response.GetResponseStream();
                    if (responseStream != null)
                    {
                        using (StreamReader reader = new StreamReader(responseStream))
                        {
                            var content = reader.ReadToEnd();
                            Console.WriteLine(content);
                        }
                    }
                }
            }
        }

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 創建基於對話框的Win32應用程式(二) —— Button的應用、新建子窗體 可以發現上一節創建的窗體中,點擊OK和Cancel兩個按鈕是沒有任何反應的。現在我們來為他們添加退出對話框的功能。 6、首先定義命令消息響應函數(Dlg_OnCommand)如下: 其中IDOK和IDCANCEL分別是按 ...
  • 一.創建一個空項目 請查看 新建 .NET Core 項目 -- Hello World! 一節,新建一個項目: 二.添加引用並修改配置為 Web API (.NET Core 已將 MVC/Web API 底層代碼及表層編程介面合二為一) 修改 .vscode\launch.json 文件 代碼如 ...
  • 下麵介紹各種List的sort的用法與比較 首先,我們建一個People的實體,有name、age、sex的屬性,我們要排序的欄位是年齡age 新建一個實體類 新建list的數據 1. 第1種排序方法,使用 IComparer 可以看到第一種方法比價麻煩,要新建一個新的類來做 2. 第2種排序方法, ...
  • 一、併發的基本含義 在操作系統中,併發是指一個時間段中有幾個程式都處於已啟動運行到運行完畢之間,且這幾個程式都是在同一個處理機上運行,但任一個時刻點上只有一個程式在處理機上運行。 在關係資料庫中,允許多個用戶同時訪問和更改共用數據的進程。SQL Server 使用鎖定以允許多個用戶同時訪問和更改共用 ...
  • Web 應用程式使用的 Cookie 個人認為這裡設置的cookie與訪問cookie的安全性關聯大一點,配置節如下 httpOnlyCookies:預設是false,作用是是否禁用瀏覽器腳本訪問cookie。在Form認證時會頒發一個認證票寫在cookie,最開始我以為這裡設置了則可以訪問,結果並 ...
  • 背水一戰 Windows 10 之 控制項(彈出類): FlyoutBase, Flyout, MenuFlyout ...
  • ASP.NET Core中間件(Middleware)進階學習實現SOAP 解析。 本篇將介紹實現ASP.NET Core SOAP服務端解析,而不是ASP.NET Core整個WCF host。 因為WCF中不僅僅只是有SOAP, 它還包含很多如消息安全性,生成WSDL,雙工通道,非HTTP傳輸等 ...
  • 前言 大家好,今天給大家介紹一個 ASP.NET Core MVC 的一個新特性,給全局路由添加統一首碼。嚴格說其實不算是新特性,不過是Core MVC特有的。 應用背景 不知道大家在做 Web Api 應用程式的時候,有沒有遇到過這種場景,就是所有的介面都是以 /api 開頭的,也就是我們的api ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...