天天看點

ASP.NET Core 資料保護(Data Protection)【上】

前言

上一篇部落格記錄了如何在 Kestrel 中使用 HTTPS(SSL), 也是我們目前項目中實際使用到的。

資料安全往往是開發人員很容易忽略的一個部分,包括我自己。近兩年業内也出現了很多因為安全問題導緻了很多嚴重事情發生,是以安全對我們開發人員很重要,我們要對我們的代碼的安全負責。

在工作中,我們常常會見到 encode,base64,sha256, rsa, hash,encryption, md5 等,一些人對他們還傻傻分不清楚,也不知道什麼時候使用他們,還有一些人認為MD5就是加密算法。

在 ASP.NET Core 中,為資料保護相關提供了一批新的 API,包括加密解密機制,下面就讓我們來看看吧。

目錄

  • 加密,編碼,哈希之間的差別
  • 資料保護(Data Protection)介紹
  • ASP.NET Core 中的資料保護
  • 總結

編碼,加密,哈希之間的差別

編碼

編碼是資訊從一種形式或格式轉換為另一種形式的過程,他們是可逆的。

如 url、base64、jsunicode、utf-8等等。

加密

加密是可逆的,類似于編碼也是把資料從一種形式轉換為另一種形式,它通過一個特定的加密的密匙,相對應的有解密的過程。加解密的算法有2種:對稱加密算法和非對稱加密算法。

對稱:DES、AES、SM1、RC4 等等。

非對稱:RSA、ECC、SM2 等等。

哈希

又叫"散列",就是把任意長度的資料轉換成固定長度的“指紋”,這個過程是不可逆的。而且隻要輸入發生改變,輸出的 hash值也會有很大不同。

它還有一個特性是相同的輸入總是有相同的結果, 這種特性恰好合适用來用來儲存密碼。

如:MD5、SHA256, SHA512, RipeMD, WHIRLPOOL等等。

在看資料保護官方文檔的時候,微軟的文檔是這樣寫的,大緻意思就是他們基于幾點需求,要開發一套資料保護的庫以便用來給受信任的用戶端和不受信任的用戶端來使用。這幾點要求就是:

1、真實性、完整性

舉了一個身份驗證cookie的例子,就是服務端生成了一個包含xyz權限的token,然後會在将來的某個時間過期,這個時候就需要重新請求生成一個,怎麼樣來保證請求的token不是被篡改過的。

2、機密性

伺服器要保證請求是受信任的,是以就需要一些包含特定操作環境的資訊,比如一個路徑,一個權限或者一個句柄或者其他的一些東西特定于伺服器的東西,這些資訊不應該透漏給不受信任的用戶端,也就是說類似于私鑰。

3、隔離性

然後就是要求做成一個元件,并且這個元件具有獨立性,可以不依賴于系統中的其他元件。如一個bearer token的元件,它要使用這個元件的話,也不需要引用anti-CSRF這種機制了。

再進一步的縮小需求範圍,加密的資料不需要在系統之外的其他系統中使用,另外處理速度要盡可能的快,因為每一次web請求都會使用加密元件一次或者多次。

基于以上要求,微軟提出來可以使用密碼學,因為這是一個典型的密碼學應用的場景。确實這是一個密碼學的應用場景,并且是一個非對稱加密算法的場景。但是大家都知道,非對稱加密是由一個公鑰和私鑰用來保證安全性的,即使公鑰遭洩露,整個通訊仍然是安全的,這就是它比對稱加密的好處。但是非對稱加密也是有缺點的,就是加密和解密花費的時間長,速度慢。

但是上面的要求又是需要速度盡可能快,怎麼辦呢? 于是微軟的工程師們想出了可以通過精簡并且優化非對稱加密機制,來達到這個要求。因為不需要跨系統或者跨語言什麼的,是以也不需要什麼協定之類的,這就給優化帶來了更多的可能性。

到這裡,我就想,如果讓我來基于以上幾點來設計開發這樣一個系統,我應該怎麼樣設計?怎麼樣達到要求?

帶着這個問題,我們來進一步看看微軟是怎麼樣做的吧?

下面是一些總結的設計原則 :

1、配置應該盡量的簡單,預設情況下應該可以零配置,開發人員可以直接運作。

2、提供一個簡單的API,應該容易使用,并且不會輕易用錯。

3、開發人員不需要專門學習怎麼樣管理這些鑰(公鑰,私鑰),系統應該自動的選擇算法和管理鑰的生命周期。理想情況下開發人員都不應該通路這些鑰的原始檔案。

4、鑰應該是受保護的,不會被遠端調用到。系統應該有一個自動保護機制并且可以自動應用。

如果讓我設計這樣一個庫,我可能不會想到這麼多,也許隻會想到前3點。

再看一下針對的閱聽人群體:

1、應用程式開發人員和架構開發人員(不需要學習任何知識)。

2、應用開發人員和系統管理者(不使用預設配置,隻是設定一些路徑等)。

3、針對具有更高安全意識的開發人員提供可擴充api,或特定需求擴充(需要重寫系統的元件,有一些獨特的需求)。

以上,可以看到微軟在開發一個元件的時候對問題的分析,也許我們可以從中學到一些東西。

Web應用程式中經常需要存儲一些敏感資料(如使用者密碼),Windows 系統為桌面程式提供了DPAPI用來使用,但是并不适用于 Web 系統。ASP.NET Core提供了一套簡單易用的API 用來保護資料。

ASP.NET Core 中,資料保護主要是用來給服務端設計的,用來替換ASP.NET 1.x-4.x中的,machineKey主要是用來保證使用Form身份驗證時Cookie資料的加密解密,以確定不會被修改。或者ViewState資料的加密解密不被篡改,以及對session狀态辨別進行驗證。

先看一下最簡單的使用方法:

using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;

public class Program
{
    public static void Main(string[] args)
    {
        // 添加資料保護到服務中
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddDataProtection();
        var services = serviceCollection.BuildServiceProvider();

        // 從DI中建立一個MyClass的執行個體 
        var instance = ActivatorUtilities.CreateInstance<MyClass>(services);
        instance.RunSample();
    }

    public class MyClass
    {
        IDataProtector _protector;

        // 參數 'provider' 來自 DI
        public MyClass(IDataProtectionProvider provider)
        {
            _protector = provider.CreateProtector("Contoso.MyClass.v1");
        }

        public void RunSample()
        {
            Console.Write("Enter input: ");
            string input = Console.ReadLine();

            // 加密
            string protectedPayload = _protector.Protect(input);
            Console.WriteLine($"Protect returned: {protectedPayload}");

            // 解密
            string unprotectedPayload = _protector.Unprotect(protectedPayload);
            Console.WriteLine($"Unprotect returned: {unprotectedPayload}");
        }
    }
}

/*
 * 輸出:
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ
 * Unprotect returned: Hello world!
 */
           

CreateProtector("Contoso.MyClass.v1")

中,參數“Contoso.MyClass.v1”可以了解為一個公鑰,因為 ASP.NET Core Data Protection 是非對稱加密(見前面介紹),是以系統中應該還有一個密鑰,那麼此處的密鑰 ASP.NET Core 在系統内部幫你維護了。

讀到這裡,有同學可能會問了,那系統中是如何幫我維護我的密鑰的呢? 我們不妨先來做一個測試。

首先,我在我的開發環境中,先把上面的程式中的解密部分代碼注釋掉,然後運作上面的程式,輸入一個“Hello World!” ,得到了一個加密的字元串

CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ

(略寫)。

然後我把同樣的程式拷貝到另外一台開發環境的機器上,然後把上面的加密部分代碼注釋掉,使用第一步生成的

CfDJ8ICcgQwZZhlAlTZT...OdfH66i1PnGmpCR5e441xQ

來解密,注意這兩步中我們都使用 "Contoso.MyClass.v1" 來做為公鑰。

運作程式,檢視結果:

ASP.NET Core 資料保護(Data Protection)【上】

程式抛出了一個“System.Security.Cryptography.CryptographicException”異常的結果。

為什麼呢? 這是因為每一台機器都有一個自有的私鑰,由于在解密的過程中,這個私鑰是不同的,是以解密失敗,抛出了一個異常。

私鑰

私鑰存放在哪裡呢?

1、如果程式寄宿在 Microsoft Azure下,存儲在“%HOME%\ASP.NET\DataProtection-Keys” 檔案夾。

2、如果程式寄宿在IIS下,它被儲存在HKLM系統資料庫的ACLed特殊系統資料庫鍵,并且隻有工作程序可以通路,它使用windows的DPAPI加密。

3、如果目前使用者可用,即win10或者win7中,它存儲在“%LOCALAPPDATA%\ASP.NET\DataProtection-Keys”檔案夾,同樣使用的windows的DPAPI加密。

4、如果這些都不符合,那麼也就是私鑰是沒有被持久化的,也就是說當程序關閉的時候,生成的私鑰就丢失了。

下面是部落客機器上的私鑰檔案:

一個xml配置檔案,位于

C:\Users\使用者名\AppData\Local\ASP.NET\DataProtection-Keys

檔案夾,名為:

key-c37e3ed9-fbb5-47bc-9e8c-128afaf1c6d9.xml

,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<key id="c37e3ed9-fbb5-47bc-9e8c-128afaf1c6d9" version="1">
  <creationDate>2016-08-15T05:21:16.7925949Z</creationDate>
  <activationDate>2016-08-15T05:21:16.7165905Z</activationDate>
  <expirationDate>2016-11-13T05:21:16.7165905Z</expirationDate>
  <descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
    <descriptor>
      <encryption algorithm="AES_256_CBC" />
      <validation algorithm="HMACSHA256" />
      <encryptedSecret decryptorType="Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiXmlDecryptor, Microsoft.AspNetCore.DataProtection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" xmlns="http://schemas.asp.net/2015/03/dataProtection">
        <encryptedKey xmlns="">
          <!-- This key is encrypted with Windows DPAPI. -->
          <value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAArS6GBZu5C024S8VcNDckGgAAAAACAAAAAAAQZgAAAAEAACAAAABBUO4j0CscEZsdcHDAStXnDvtx+zFucmsG90sdhyjfgQAAAAAOgAAAAAIAACAAAABGr9fgvZkLAlgIZkGym5uLiufpaEcuVsp35+J96ItTYlABAADEZxVArK0QtxufuaRt/kVR2ZBZEoLhlYJ44BhvQDd6b9tN0L9Y7W2eeBPBefcZaGZk5xILwZYI5box9omwC/mp8t9wopVaratjZuNs21Al+JzxS+PeV9X0iPtRyfx2K7DJYOUT6IqoFR2ykL5MI9jvkIbUxcQOs0BKOwAHl4yAlYF2tR8pz1FkXKqZafovc11aOZeZhkfd2hiA53tan94bQOP43Z4HF+QWSazrq5IIqdFSOyZQemWL9Z7eYyoNpEktf3eGZQu/KBOg/BH5yizWa+6b7RLcEX6JdQ2/jpmnHNl+HPMIah3UZV0mRfAE2j58cUjosnV+LDQZoLn4OP70YWtO/tTBc4tsEY3n/WboL4PgPPmQ+2jfd/zmEQIon+4d7TY+mGh4c6wXAmAZF517UAHQMC1icx4HSJC8DTuWPlINihPyufejuPmLqW6CW8NAAAAA7ziObXv+Ax4Mm0AtZiGw0/IepDv/gJSxhEwLIDhfvQIQJv//G500EYtIbZJW6sWit//ypfjrUZYglHgKV+GpbA==</value>
        </encryptedKey>
      </encryptedSecret>
    </descriptor>
  </descriptor>
</key>
           

檔案包含一個建立日期,一個過期日期。間隔為90天,當90天之後密鑰就會失效,系統将自動生成一個新的密鑰并設定新的密鑰作為活動的密鑰。隻要已過期的密鑰還存在于系統上,你仍然可以解密任何受保護的資料。

文章不宜太長,下篇再接着寫。

如果您覺得本篇文章對你有用的話,不妨點個【推薦】。

這篇文章算是對ASP.NET Core Data Protection做了一個大緻的介紹,并且包含了一個簡單的使用方法。 在實際使用過程中,其實很多元件内部都會使用到它,比如

Session

中間件,

Identity

Authercation

中間件等等,對于普通開發人員在編碼的時候可能不會用到,但是在做系統分布式部署的時候如果你不了解這個機制可能就會遇到麻煩了(詳見蟋蟀部落格的這篇文章),是以還是可以期待一下下文,更加深入的了解它,掌握它。

本文位址:http://www.cnblogs.com/savorboard/p/dotnetcore-data-protection.html

作者部落格:Savorboard

歡迎轉載,請在明顯位置給出出處及連結

繼續閱讀