十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
作為一個(gè)在X94的航空工程師,你的老板要求你從2號(hào)樓的工程圖中檢索出一個(gè)特定的專利。不幸的是,進(jìn)入大樓需要你出示你具有進(jìn)入大樓的資格的證明,然后你迅速地以徽章的形式出示給了保安。到了十三樓,進(jìn)入建筑師工程師圖紙庫要求通過他們的生物鑒定系統(tǒng)來驗(yàn)證你是你聲稱的那個(gè)人。最后在你的目的地,你提供給庫管理員一串對(duì)你毫無意義的字母數(shù)字代碼,但是在合適的人手上,它可以轉(zhuǎn)換成哪里可以找的你需要的工程圖的真實(shí)索引。
創(chuàng)新互聯(lián)建站專注于企業(yè)全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、商城網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、html5、成都商城網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為商城等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
在上面的比喻中,我們可以很容易地確定適當(dāng)?shù)陌踩胧﹣肀Wo(hù)敏感數(shù)據(jù)的訪問。除了個(gè)人訪問所需驗(yàn)證,一個(gè)附加的可能不是很明顯的安全措施就是以字母數(shù)字碼的形式混淆技術(shù)文檔身份,并間接映射到真實(shí)的文檔身份和庫中的位置。
形象地說,這個(gè)比喻是一流行的被稱為“非安全的直接對(duì)象引用”的Web應(yīng)用安全漏洞的解答,該漏洞在OWASP最關(guān)鍵漏洞Top10中排第四。但如果這就是答案的話, 你接下來自然會(huì)問“關(guān)于我Web應(yīng)用的具體問題是什么且該如何去解決?”
我們對(duì)在我們網(wǎng)站上展示商品的想法都很熟悉。用戶通過發(fā)起請(qǐng)求來查看商品詳情,向他們的購(gòu)物車?yán)锾砑由唐?,或進(jìn)行類似的活動(dòng)。你很有可能會(huì)利用商品的ID去標(biāo)識(shí)用戶正在請(qǐng)求哪件商品的詳細(xì)信息,標(biāo)識(shí)添加進(jìn)他們購(gòu)物車的商品等等。最重要的是,這個(gè)ID很有可能是存儲(chǔ)商品信息的數(shù)據(jù)庫表的主鍵。如果真是這樣,那么我們就擁有了一個(gè)直接對(duì)象引用。在網(wǎng)頁上展示的某個(gè)商品(對(duì)象)被特定的ID標(biāo)識(shí),而這個(gè)ID是對(duì)數(shù)據(jù)庫中相同標(biāo)識(shí)的直接引用。
“說的不錯(cuò),但那又如何?”是這樣,在簡(jiǎn)單的商家對(duì)顧客場(chǎng)景下,上文所講的情況不是什么問題。但假定這是一個(gè)金融類服務(wù)應(yīng)用,比方說是你最常用的網(wǎng)上銀行,上面有你的各個(gè)活期、定期儲(chǔ)蓄賬戶和其他敏感數(shù)據(jù),那將會(huì)怎樣呢?想象一下,你在你的賬戶頁面選擇查看 ID 為 1344573490 的存款賬戶的詳細(xì)信息:
作為一個(gè)經(jīng)過身份核實(shí)的名為Mary Wiggins的用戶,網(wǎng)站顯示了針對(duì)你存款賬戶的信息:
我們可以直接看出這個(gè)支票戶頭就是我們擁有的賬戶,同時(shí)也能確認(rèn)這是一個(gè)直接引用。但要是你決定把 accountNumber 參數(shù)從 1344573490 改為 1344573491,那將會(huì)發(fā)生什么呢?
Erin Maley,誰是Erin Maley?那不是我們。我們作為Mary Wiggins是已經(jīng)明確被認(rèn)證過的。我們所有所做的事情就是順序地增加賬戶號(hào)直到下一個(gè)可能的值,并且我們可以看到一個(gè)不是我們所持有的賬戶信息。在這個(gè)例子中,我們有一個(gè)直接關(guān)聯(lián)的賬戶,它可以被定義為系統(tǒng)內(nèi)任何地方被標(biāo)識(shí)的賬戶號(hào)。更進(jìn)一步說,我們演習(xí)了一個(gè)潛在的問題,曝光一個(gè)直接相關(guān)的賬戶是簡(jiǎn)單的數(shù)據(jù)工程。
如果你自己覺得這不是直接引用惹的禍,而是身份驗(yàn)證上出了差錯(cuò),那么你只對(duì)了一半。我們討論不安全直接對(duì)象引用所造成的缺陷時(shí),實(shí)際上看到了兩個(gè)問題。我發(fā)現(xiàn)下圖能夠更清楚的描述這個(gè)缺陷究竟是什么:
如果不安全的直接對(duì)象引用涉及以下兩方面……
泄露敏感數(shù)據(jù)
缺乏合理的訪問控制
……那么我們對(duì)于彌補(bǔ)這個(gè)缺陷的看法是什么,以及我們應(yīng)該何時(shí)采取行動(dòng)?接下來,我們首先解決影響最大范圍最廣的問題——合理的訪問控制。
就像文章開頭舉的例子,多層級(jí)的訪問控制是必須的。雖然我們有權(quán)進(jìn)入大樓,但進(jìn)入樓內(nèi)某些區(qū)域需要特定的權(quán)限。當(dāng)我們考慮在Web應(yīng)用中保護(hù)資源時(shí),可以使用這樣的準(zhǔn)則來達(dá)到目的。
首先,當(dāng)前合法用戶是否有權(quán)請(qǐng)求資源?在我們對(duì)該用戶一無所知的情況下,該如何確定當(dāng)前用戶可以被允許發(fā)起這個(gè)請(qǐng)求?因此第一步我們要做的是,在和用戶交互時(shí),通過添加訪問控制來保護(hù)資源。
在ASP.NET中,用戶交互通過控制器動(dòng)作(controller action)完成。我們可以在ASP.NET MVC控制器上使用[Authorize]特性(attribute)來確保用戶只有先經(jīng)過系統(tǒng)核實(shí)身份才能執(zhí)行控制器上的動(dòng)作,而匿名用戶將被拒絕。
[Authorize] public class AccountsController : Controller { [HttpGet] public ActionResult Details(long accountNumber) { //...
這樣就確保了API無法被公開使用,根據(jù)你的ASP.NET配置,用戶會(huì)被重定向到登錄頁面(默認(rèn)行為)。[Authorize]特性通過額外的約束來匹配特定的用戶和角色:
[Authorize(Roles = "Admin, Manager")] public class AccountsController : Controller { //..
[Authorize]特性除了可以被應(yīng)用到控制器動(dòng)作上外,還能進(jìn)行更多粒度的控制。例如在控制器上放置身份驗(yàn)證約束,同時(shí)在控制器的不同動(dòng)作上使用基于角色的訪問控制。
在我們的銀行賬戶例子中,只對(duì)用戶進(jìn)行身份驗(yàn)證是不夠的,因?yàn)槲覀儯ㄖ唤?jīng)過身份驗(yàn)證的用戶)竟然能訪問另一個(gè)用戶的支票賬戶信息。對(duì)于像銀行賬戶例子中看到的這種濫用行為,通常被稱作為橫向權(quán)限提升,用戶可以訪問其他相同等級(jí)的用戶信息。然而,有權(quán)發(fā)起對(duì)某個(gè)資源的請(qǐng)求與擁有對(duì)實(shí)際資源的權(quán)限是完全不同的概念。
因此,我們必須采取的第二層也是最重要訪問控制就是,保證用戶被授權(quán)訪問資源。在基于角色的訪問控制的情況下,這就跟確保用戶屬于合理的角色一樣容易。如果被請(qǐng)求的資源只需要某個(gè)提升的權(quán)限,你可以利用之前演示的[Authorize]的Role屬性來搞定。
[Authorize(Roles = "Admin")] public class AccountsController : Controller { //..
但是更多的時(shí)候,你被要求在數(shù)據(jù)層面對(duì)用戶進(jìn)行權(quán)限驗(yàn)證,以保證其有權(quán)訪問所請(qǐng)求的資源。考慮到受許多不同因素的影響,解決方案多種多樣,就上文提到的查看銀行賬戶詳情的案例,我們可以驗(yàn)證用戶是否為其所請(qǐng)求賬戶的擁有者:
[Authorize] public class AccountsController : Controller { [HttpGet] public ActionResult Details(long accountNumber) { Account account = _accountRepository.Find(accountNumber); if (account.UserId != User.Identity.GetUserId()) { return new HttpUnauthorizedResult("User is not Authorized."); } //...
記得我們已經(jīng)在控制器級(jí)別使用了[Authorize]特性,所以沒必要在動(dòng)作級(jí)別畫蛇添足。
需要重點(diǎn)注意的是,在上面的關(guān)于在ASP.NET中使用Forms Authentication引發(fā)的非授權(quán)結(jié)果的例子中將會(huì)強(qiáng)制一個(gè)302跳轉(zhuǎn)到登陸頁面,無論用戶是否已經(jīng)的到授權(quán)。因此,你或許需要對(duì)處理這種行為作出必要的改變,這取決于你的應(yīng)用,你的需求和你用戶的期望。你的選擇或者你是否需要處理這種行為很大程度上依賴于框架的風(fēng)格,使用OWIN模塊,和你的應(yīng)用的需要。
好處是減少了去確定沒有用戶提權(quán)的次數(shù),保證了合適的訪問權(quán)限控制。至少,我們可以加強(qiáng)對(duì)請(qǐng)求本身和請(qǐng)求對(duì)被請(qǐng)求資源的訪問的訪問控制。但是,如同我前面提到的若干種場(chǎng)合, 在我們的應(yīng)用加強(qiáng)防止數(shù)據(jù)泄露總是應(yīng)該評(píng)估的一個(gè)安全步驟。什么是我所說的“數(shù)據(jù)泄露”?我們可以通過研究其他包含不安全的直接對(duì)象引用(如混淆)來回答這個(gè)問題。
混淆 就是故意隱藏意圖的行為。在我們這兒, 我們可以使用混淆手段來推斷安全性。 一個(gè)人們認(rèn)同的簡(jiǎn)單例子就是URL短鏈。雖然初衷并不是為了安全性, 像這樣的URL http://bit.ly/1Gg2Pnn 是從真實(shí)的URL從混淆過來的。 根據(jù)這個(gè)短鏈, Bit.ly能夠?qū)⒒煜腢RL http://bit.ly/1Gg2Pnn 映射到真正的http://lockmedown.com/preventing-xss-in-asp-net-made-easy.
我使用了關(guān)于銀行賬戶交互的金融例子,因?yàn)檫@是一個(gè)完美的例子,在其中的元數(shù)據(jù)是很敏感的。在這種情況下,一個(gè)支票帳戶就是我們要保護(hù)的數(shù)據(jù)。而賬戶號(hào)碼就是關(guān)于支票賬號(hào)的元數(shù)據(jù),我們認(rèn)為這是敏感數(shù)據(jù)。
我們看到在前面我們只是增加了帳號(hào)的數(shù)值就能夠嚴(yán)格訪問另一個(gè)用戶的支票帳戶,因?yàn)闆]有數(shù)據(jù)級(jí)訪問控制。但我們可以通過混淆賬號(hào)建立另一防御屏障使惡意用戶失去直接駕馭系統(tǒng)的能力,這通過改變數(shù)值就行。
可以實(shí)現(xiàn)不同級(jí)別的混淆,每一級(jí)別都能提供不同級(jí)別的安全性和平衡性.我們將看到第一個(gè)選項(xiàng)是一種比較常見的,安全的但有些限制的選項(xiàng),我喜歡稱之為“視野”,該詞間接參考地圖。
引用映射與 Bit.ly 短網(wǎng)址并沒有什么不同,你的服務(wù)器知道怎樣將一個(gè)公開的表面值映射到一個(gè)內(nèi)部值來代表敏感數(shù)據(jù)。作用域代表我們用于限制映射使用而放入的限制條件。這對(duì)理論研究已經(jīng)足夠了,我們來看一個(gè)例子:
我們認(rèn)為一個(gè)賬號(hào)編號(hào)例如1344573490是一個(gè)敏感數(shù)據(jù),我們希望隱藏它并只提供可被確認(rèn)的賬號(hào)持有者。為了避免暴露賬號(hào)編號(hào),我們可以提供一個(gè)間接引用到賬號(hào)編號(hào)的公開表面值。服務(wù)器將會(huì)知道怎樣把這個(gè)間接引用映射回直接引用,這個(gè)直接引用指向我們的賬號(hào)編號(hào)。服務(wù)器使用的映射存儲(chǔ)在一個(gè) ASP.NET 用戶回話中,這就是作用域,關(guān)于作用域的更多內(nèi)容,來看看這個(gè)實(shí)現(xiàn):
public static class ScopedReferenceMap
{
private const int Buffer = 32;
///
/// Extension method to retrieve a public facing indirect value
///
///
///
///
public static string GetIndirectReference(this T value)
{
//Get a converter to convert value to string
var converter = TypeDescriptor.GetConverter(typeof (T));
if (!converter.CanConvertTo(typeof (string)))
{
throw new ApplicationException("Can't convert value to string");
}
var directReference = converter.ConvertToString(value);
return CreateOrAddMapping(directReference);
}
///
/// Extension method to retrieve the direct value from the user session
/// if it doesn't exists, the session has ended or this is possibly an attack
///
///
///
public static string GetDirectReference(this string indirectReference)
{
var map = HttpContext.Current.Session["RefMap"];
if (map == null ) throw new ApplicationException("Can't retrieve direct reference map");
return ((Dictionary) map)[indirectReference];
}
private static string CreateOrAddMapping(string directReference)
{
var indirectReference = GetUrlSaveValue();
var map =
(Dictionary) HttpContext.Current.Session["RefMap"] ??
new Dictionary();
//If we have it, return it.
if (map.ContainsKey(directReference)) return map[directReference];
map.Add(directReference, indirectReference);
map.Add(indirectReference, directReference);
HttpContext.Current.Session["RefMap"] = map;
return indirectReference;
}
private static string GetUrlSaveValue()
{
var csprng = new RNGCryptoServiceProvider();
var buffer = new Byte[Buffer];
//generate the random indirect value
csprng.GetBytes(buffer);
//base64 encode the random indirect value to a URL safe transmittable value
return HttpServerUtility.UrlTokenEncode(buffer);
}
}
這里,我們創(chuàng)建了一個(gè)簡(jiǎn)單的工具類 ScopedReferenceMap,可以提供擴(kuò)展的方法處理一個(gè)值例如我們的銀行卡號(hào)1344573490處理成 Xvqw2JEm84w1qqLN1vE5XZUdc7BFqarB0,這就是所謂的間接引用。
最終,當(dāng)一個(gè)間接引用值被請(qǐng)求時(shí),我們使用一個(gè)用戶會(huì)話來作為保持請(qǐng)求中的間接引用和直接引用之間的映射的一種方法。用戶會(huì)話成為間接引用的作用域,而且強(qiáng)制在每個(gè)用戶映射上加上時(shí)間限制。只有經(jīng)過驗(yàn)證和指定的用戶會(huì)話才具有檢索的能力。
你可以利用它在任何你需要的地方創(chuàng)建間接引用,例如:
AccountNumber = accountNumber.GetIndirectReference(); //create an indirect reference
現(xiàn)在,在一個(gè)使用如下URL的傳入請(qǐng)求(請(qǐng)求一個(gè)賬號(hào)的詳細(xì)信息):
我們可以看出,對(duì)accountNumber的間接引用映射通過與我們的訪問控制合作重新得到真實(shí)值:
[HttpGet] public ActionResult Details(string accountNumber) { //get direct reference var directRefstr = accountNumber.GetDirectReference(); var accountNum = Convert.ToInt64(directRefstr); Account account = _accountRepository.Find(accountNum); //Verify authorization if (account.UserId != User.Identity.GetUserId()) { return new HttpUnauthorizedResult("User is not Authorized."); } //…
在我們對(duì)獲得直接引用的嘗試中,如果ASP.NET用戶會(huì)話沒有獲得一個(gè)映射,那就可能是受到了***。但是,如果映射存在,仍然得到了直接引用,則可能是值被篡改了。
正如我前面提到的,用戶會(huì)話創(chuàng)建了一個(gè)作用域,用戶和時(shí)間約束限制了映射回直接引用的能力。這些限制條件以其自身的形式提供額外的安全措施。但是,你或許在使用 ASP.NET 會(huì)話狀態(tài)時(shí)遇到問題,這可能是由于已知的安全弱點(diǎn),你也可能會(huì)問怎樣才能讓這些限制條件與提供含狀態(tài)傳輸(Representational State Transfer)風(fēng)格的引擎例如超媒體狀態(tài)應(yīng)用引擎良好的合作共處?真是個(gè)好問題,讓我們來檢查一些替代選項(xiàng)吧。
HATEOAS Gonna Hate
如果你思考過通過網(wǎng)絡(luò)服務(wù)進(jìn)行的典型交互方式,這種在你的應(yīng)用中通過發(fā)送一個(gè) request 和接受一個(gè)包含額外超媒體鏈接(例如 URLs)的 response 來獲得額外的資源的方式對(duì) web 開發(fā)者來說是一個(gè)可以理解的概念。
這個(gè)概念經(jīng)過高度的精煉已經(jīng)成為構(gòu)建 REST 風(fēng)格的網(wǎng)絡(luò)服務(wù)的支柱之一:超媒體作為應(yīng)用程序狀態(tài)或 HATEOAS 的引擎。用一句話來解釋 HATEOAS 是網(wǎng)絡(luò)服務(wù)提供對(duì)于資源發(fā)現(xiàn)操作的能力:它通過在 HTTP 響應(yīng)中提供超媒體鏈接。這不是一篇關(guān)于定義 REST 風(fēng)格網(wǎng)絡(luò)服務(wù)的論文,所以,如果 REST 和 HATEOAS 對(duì)你來說是陌生概念,你需要查看關(guān)于 REST 和關(guān)于 HATEOAS 的資料來對(duì)他們有一個(gè)了解。
因此,提供包含有作用域的間接引用參數(shù)的 URL 的想法與像 HATEOAS 這樣的概念或需要一直提供持久性 URL (具有較長(zhǎng)生存時(shí)間的 URL )之間是有很大困難的。如果我們希望提供持久性 URL 的同時(shí),包含間接引用值,那么我們就需要采用一種不同的安全方法,我們應(yīng)該怎么做呢?
靜態(tài)間接引用映射
為了提供包含間接引用的持久性 URL,我們接下來就需要一些方法來在任意給定的時(shí)間或者至少是在未來相當(dāng)長(zhǎng)的一段時(shí)間內(nèi)將間接值映射回原始的直接值。如果我們想要持久性,那么像使用一個(gè)用戶會(huì)話來維持一個(gè)引用映射這樣的限制條件將不再是個(gè)可用的選項(xiàng)。讓我們來看看可以使用靜態(tài)間接引用映射方案的場(chǎng)景。
假設(shè)你有一個(gè) B2B 網(wǎng)絡(luò)應(yīng)用,它允許商家獲得指定給他們的 VIP 商品的定價(jià)。給客戶系統(tǒng)發(fā)送一個(gè)請(qǐng)求,返回一個(gè)包含鏈接到此客戶的 VIP 商品的附加超媒體鏈接的響應(yīng)。當(dāng)點(diǎn)擊 VIP 商品鏈接時(shí),接收到的響應(yīng)就包含他們指定商家的所有可用 VIP 商品的超媒體鏈接。
在我們的例子中,我們決定通過創(chuàng)建一個(gè)間接引用,對(duì)VIP商品URL中的VIP商品ID加以混淆,到時(shí)候我們能很快地重新映射回商品的實(shí)際ID。
例子: https://AppCore.com/business/Acme/VIP/Products/99933
針對(duì)我們的處境,加密是一個(gè)不錯(cuò)的選擇,這使得我們能更好的掌控將間接引用映射回實(shí)際商品ID的生命周期。
如同我們?cè)谟蛞美又凶龅哪菢?,利用相同的API,來看看它將會(huì)成為什么樣子,然后我們帶著關(guān)注和額外的選擇,再討論一下我們做了什么和為什么用這種方法:
public static class StaticReferenceMap
{
public const int KeySize = 128; //bits
public const int IvSize = 16; //bytes
public const int OutputByteSize = KeySize / 8;
private static readonly byte[] Key;
static StaticReferenceMap()
{
Key = //pull 128 bit key in
}
///
/// Generates an encrypted value using symmetric encryption.
/// This is utilizing speed over strength due to the limit of security through obscurity
///
///Primitive types only
/// direct value to be encrypted
///Encrypted value
public static string GetIndirectReferenceMap(this T value)
{
//Get a converter to convert value to string
var converter = TypeDescriptor.GetConverter(typeof (T));
if (!converter.CanConvertTo(typeof (string)))
{
throw new ApplicationException("Can't convert value to string");
}
//Convert value direct value to string
var directReferenceStr = converter.ConvertToString(value);
//encode using UT8
var directReferenceByteArray = Encoding.UTF8.GetBytes(directReferenceStr);
//Encrypt and return URL safe Token string which is the indirect reference value
var urlSafeToken = EncryptDirectReferenceValue(directReferenceByteArray);
return urlSafeToken;
}
///
/// Give a encrypted indirect value, will decrypt the value and
/// return the direct reference value
///
/// encrypted string
///direct value
public static string GetDirectReferenceMap(this string indirectReference)
{
var indirectReferenceByteArray =
HttpServerUtility.UrlTokenDecode(indirectReference);
return DecryptIndirectReferenceValue(indirectReferenceByteArray);
}
private static string EncryptDirectReferenceValue(byte[] directReferenceByteArray)
{
//IV needs to be a 16 byte cryptographic stength random value
var iv = GetRandomValue();
//We will store both the encrypted value and the IV used - IV is not a secret
var indirectReferenceByteArray = new byte[OutputByteSize + IvSize];
using (SymmetricAlgorithm algorithm = GetAlgorithm())
{
var encryptedByteArray =
GetEncrptedByteArray(algorithm, iv, directReferenceByteArray);
Buffer.BlockCopy(
encryptedByteArray, 0, indirectReferenceByteArray, 0, OutputByteSize);
Buffer.BlockCopy(iv, 0, indirectReferenceByteArray, OutputByteSize, IvSize);
}
return HttpServerUtility.UrlTokenEncode(indirectReferenceByteArray);
}
private static string DecryptIndirectReferenceValue(
byte[] indirectReferenceByteArray)
{
byte[] decryptedByteArray;
using (SymmetricAlgorithm algorithm = GetAlgorithm())
{
var encryptedByteArray = new byte[OutputByteSize];
var iv = new byte[IvSize];
//separate off the actual encrypted value and the IV from the byte array
Buffer.BlockCopy(
indirectReferenceByteArray,
0,
encryptedByteArray,
0,
OutputByteSize);
Buffer.BlockCopy(
indirectReferenceByteArray,
encryptedByteArray.Length,
iv,
0,
IvSize);
//decrypt the byte array using the IV that was stored with the value
decryptedByteArray = GetDecryptedByteArray(algorithm, iv, encryptedByteArray);
}
//decode the UTF8 encoded byte array
return Encoding.UTF8.GetString(decryptedByteArray);
}
private static byte[] GetDecryptedByteArray(
SymmetricAlgorithm algorithm, byte[] iv, byte[] valueToBeDecrypted)
{
var decryptor = algorithm.CreateDecryptor(Key, iv);
return decryptor.TransformFinalBlock(
valueToBeDecrypted, 0, valueToBeDecrypted.Length);
}
private static byte[] GetEncrptedByteArray(
SymmetricAlgorithm algorithm, byte[] iv, byte[] valueToBeEncrypted)
{
var encryptor = algorithm.CreateEncryptor(Key, iv);
return encryptor.TransformFinalBlock(
valueToBeEncrypted, 0, valueToBeEncrypted.Length);
}
private static AesManaged GetAlgorithm()
{
var aesManaged = new AesManaged
{
KeySize = KeySize,
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7
};
return aesManaged;
}
private static byte[] GetRandomValue()
{
var csprng = new RNGCryptoServiceProvider();
var buffer = new Byte[16];
//generate the random indirect value
csprng.GetBytes(buffer);
return buffer;
}
}
在這里,我們的API應(yīng)該看起來像ScopedReferenceMap,只有在發(fā)生變化時(shí)才會(huì)在內(nèi)部運(yùn)行,我們借助了.NET 中具有128位秘鑰的AesManaged對(duì)稱加密庫和一個(gè)對(duì)初始向量(IV)高度加密的隨機(jī)值。你們中的一些人可能會(huì)意識(shí)到,怎樣才能做到在速度與強(qiáng)度之間的最優(yōu)化呢?
AesManaged在實(shí)例中要比FIPS快約170倍,相當(dāng)于AesCryptoServiceProvider
128位長(zhǎng)度需要執(zhí)行算法的次數(shù)少于4次,這要比更大的256位長(zhǎng)度要小
關(guān)鍵點(diǎn)之一是我們?yōu)槌跏枷蛄浚↖V)生成一個(gè)強(qiáng)加密的隨機(jī)值,這個(gè)隨機(jī)值應(yīng)用到了所有的加密過程中。秘鑰同樣是個(gè)機(jī)密,為了保密,我選擇將它留給你,讓你來找出你想怎樣使用秘鑰,好的一方面是我們不必與任何人分享秘鑰。最終,我們存儲(chǔ)帶有密碼的非機(jī)密的初始向量(間接引用),這樣我們就可以在一個(gè)請(qǐng)求中解密間接引用。
要絕對(duì)地清楚,這不是一個(gè)可替代的訪問控制。這只能用或應(yīng)該用在正確的訪問控制連接上。
現(xiàn)在,也還有一個(gè)沒有那么復(fù)雜的方法。一種改進(jìn)過的方法是包含了上述過程的加密認(rèn)證(AE),但是這是一個(gè)基于哈希消息驗(yàn)證碼的過程。認(rèn)證加密也支持像填充、消息篡改等暴漏的安全***。此外,像 Stan Drapkin那樣的學(xué)著會(huì)告訴你對(duì)稱加密必須被認(rèn)證加密。
然而,這并不是一篇關(guān)于加密的文章。所有的出發(fā)點(diǎn)就是以最后一個(gè)選項(xiàng)來“照亮”其他的選項(xiàng),目的是給那些不間接使用作用域的用戶會(huì)話,如.NET,提供一個(gè)敏感數(shù)據(jù)的模糊環(huán)境。
緩解和減少不安全的直接對(duì)象引用的唯一可靠的方法是具有適當(dāng)?shù)脑L問控制,再多的混淆都不能阻止對(duì)數(shù)據(jù)的未授權(quán)訪問。
資料是非常重要的,惡意用戶會(huì)以對(duì)他們有利的方式來使用它,當(dāng)你意識(shí)到的時(shí)候就太晚了。因此,當(dāng)你認(rèn)為一項(xiàng)數(shù)據(jù)是個(gè)敏感數(shù)據(jù)時(shí),你需要應(yīng)用一定等級(jí)的混淆來進(jìn)行技術(shù)上的限制,例如使用用戶會(huì)話。但是會(huì)有一個(gè).NET會(huì)話開銷,所以要知道你應(yīng)該怎樣利用它。
絕大多數(shù)應(yīng)用并不需要混淆和創(chuàng)建間接引用,但是對(duì)于像金融等高度敏感的網(wǎng)站最好加上這層額外的安全層。
最后一點(diǎn)是:對(duì)特定數(shù)據(jù)值的混淆只是一個(gè)模糊的安全。它需要與其它安全措施同時(shí)使用,例如正確的訪問控制。從這方面來說,不應(yīng)該單獨(dú)依賴它。
不安全的直接對(duì)象引用主要涉及的內(nèi)容是,通過合理的訪問控制來保護(hù)數(shù)據(jù)不被未經(jīng)授權(quán)的訪問。其次,為了防止像直接引用鍵值那樣的敏感數(shù)據(jù)遭到泄露,要了解如何以及何時(shí)該通過間接引用那些鍵值來添加一層混淆。最后,在決定要使用混淆技術(shù)時(shí),要意識(shí)到利用間接引用映射來彌補(bǔ)漏洞的局限性。