如何使用簽名保證asp.net mvc or webapi的接口安全
當(dāng)我們開發(fā)一款app的時候,app需要跟后臺服務(wù)進行通信獲取或者提交數(shù)據(jù)。如果我們沒有完善的安全機制則很容易被別用心的人偽造請求而篡改數(shù)據(jù)。
所以我們需要使用某種安全機制來保證請求的合法?,F(xiàn)在最常用的辦法是給每個http請求添加一個簽名,服務(wù)端來驗證簽名的合法性,如果簽名合法則執(zhí)行響應(yīng)的操作,如果簽名非法則直接拒絕請求。
簽名算法
簽名算法一般都使用hash散列算法,常用的有md5,sha系列算法。這些算法可以根據(jù)不同的輸入,計算出不同的結(jié)果,而且碰撞的概率很低。
簽名算法跟加密算法不是一回事。很多同學(xué)都會說使用md5加密一下,其實這是錯誤的。簽名算法不能恢復(fù)原來的數(shù)據(jù),因為它本身并不包含原來數(shù)據(jù)的信息。
而加密方法不同,加密方法是可以根據(jù)加密結(jié)果重新推算出原來的數(shù)據(jù)的。
hmac sha作為一種更加安全的簽名算法,使用一個key來影響簽名的結(jié)果。這樣同樣的輸入配合不同的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(); } }
簽名的參數(shù)
有了簽名算法,那么我們簽名的內(nèi)容哪里來呢?
一般我們使用http請求的querystring然后加上時間戳還有隨機數(shù)來作為簽名的參數(shù)。
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(); }
驗證簽名
驗證簽名就是簡單的比較服務(wù)端生產(chǎn)的簽名跟客戶端生產(chǎn)的簽名是否一直。
要注意的一點是最好驗證下時間戳,跟服務(wù)端時間比較前后不能相差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基類
有了上面這些鋪墊我們就可以在基類完成簽名的驗證了??蛻舳诵枰焉厦嫣岬降臅r間戳,隨機數(shù),簽名和客戶端的id放入http請求的headers里面。
我們在基類的onactionexecuting里取出這些數(shù)據(jù)組合成簽名的參數(shù),然后根據(jù)客戶端id獲取簽名的key,然后使用同樣的簽名算法計算簽名。并且比較客戶端的簽名跟服務(wù)端的簽名是否一致。
這里就不演示了。
預(yù)防replay attack
預(yù)防重放攻擊主要有兩點:
- 校驗時間戳的范圍
時間戳跟服務(wù)器時間相差在一個合理的范圍內(nèi)視為合法。
- 緩存簽名
每次請求都去判斷下簽名是否出現(xiàn)過。如果出現(xiàn)過則視為非法請求。
因為有時間戳跟隨機數(shù)的存在,所以理論上每次請求的簽名是不可能重復(fù)的。
客戶端調(diào)用
這里演示一下c#簽名并且調(diào)用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); } } } } }
以上就是如何使用簽名保證asp.net mvc or webapi的接口安全的詳細內(nèi)容,更多關(guān)于用簽名保證asp.net mvc or webapi的接口安全的資料請關(guān)注碩編程其它相關(guān)文章!