[WCF安全係列]消息的保護等級[下篇]
在《上篇》中,我們著重討論了消息的保護等級如果在契約中定義,定義在不同契約(服務契約、錯誤契約和消息契約)中的消息保護等級具有怎樣的層級關係,以及在默認情況下各種綁定采用怎樣的保護等級。在下篇中,我們進一步來探討消息保護等級和綁定的關係。
一、契約的保護等級為綁定進行消息保護設置了“最低標準”
二、顯式地將保護等級設置成ProtectionLevel.None與沒有設置保護等級有區別嗎?
三、消息的保護等級與WS-Addressing
定義在契約上消息保護級別實際上為WCF實施消息保護設置了一個“”。由於整個消息保護機製,不論是簽名還是加密,都是在實現的。而信道層最終是通過綁定來實現的,綁定的屬性決定了信道層處理消息的能力。而綁定安全方麵的屬性自然就決定了最終的信道層是否有能力對消息實施簽名和加密。一方麵,;另一方麵,。
舉個例子,如果我們通過如下的代碼將服務契約ICalculator的Add操作的保護級別設置成EncryptAndSign。
1: [ServiceContract(Namespace = "https://www.artech.com/")]
2: public interface ICalculator
3: {
4: [OperationContract(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
5: double Add(double x, double y);
6: }
但是我們確將終結點使用到的WS2007HttpBinding的安全模式設置成None。那麼在對服務進行寄宿的時候,就會跑出如下圖所示的InvalidOperationException異常,提示“必須保護請求消息”。
1: <system.serviceModel>
2: <bindings>
3: <ws2007HttpBinding>
4: <binding name="bindingWithNoneSecurityMode">
5: <security mode="None"/>
6: </binding>
7: </ws2007HttpBinding>
8: </bindings>
9: <services>
10: <service name="Artech.WcfServices.Services.CalculatorService" >
11: <endpoint address="https://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" bindingConfiguration="bindingWithNoneSecurityMode"
12: contract="Artech.WcfServices.Contracts.ICalculator"/>
13: </service>
14: </services>
15: </system.serviceModel>
在這裏有一個很多人會忽視的要點。表示消息保護級別的ProtectionLevel類型是一個枚舉,所以它肯定有一個默認值。這個默認值就是None,也就是說當你沒有顯式地指定契約具有采用那麼保護級別的時候,默認值就是None。但是這種情況和你顯式保護級別設置為None的效果是完全不一致的。因為前者真正采用的保護級別(當綁定安全被開啟)實際上是EncryptAndSign,後者才是None。那麼WCF如何來區分這兩種情況呢?
如果你足夠細心,你應該會發現:在上麵介紹的定義消息保護級別的特性中,除了具有一個可讀可寫的ProtectionLevel屬性之外,還具有一個隻讀的屬性,該屬性表示你是否對消息保護級別進行了“顯式”的設置。我們可以通過一個簡單的實驗來演示HasProtectionLevel的作用。
下麵我定義了兩個服務契約IServiceContract1和IServiceContract2,其實前者沒有對ProtectionLevel進行相應的設置,後者被顯式地設置為None。
1: [ServiceContract]
2: public interface IServiceContract1
3: {
4: [OperationContract]
5: void DoSomething();
6: }
7: [ServiceContract(ProtectionLevel = ProtectionLevel.None)]
8: public interface IServiceContract2
9: {
10: [OperationContract]
11: void DoSomething();
12: }
然後我編寫了如下的代碼,基於上麵兩個接口類型生成相應的ContractDescription對象,然後將它們的ProtectionLevel和HasProtectionLevel屬性輸出來。從最終的輸出結果我們可以很清楚地看到:兩種情況下下ProtectionLevel屬性值都是None,但是隻有當你顯式地設置了ProtectionLevel的情況下,HasProtectionLevel屬性才會返回True。WCF就是根據ContractDescription的這兩個屬性決定最終采用怎樣的消息保護級別的。
1: ContractDescription contract1 = ContractDescription.GetContract(typeof(IServiceContract1));
2: ContractDescription contract2 = ContractDescription.GetContract(typeof(IServiceContract2));
3:
4: Console.WriteLine("{0,-10}{1,-20}{2,-20}", "Contract","ProtectionLevel", "HasProtectionLevel");
5:
6: Console.WriteLine("{0,-10}{1,-20}{2,-20}", "contract1", contract1.ProtectionLevel, contract1.HasProtectionLevel);
7: Console.WriteLine("{0,-10}{1,-20}{2,-20}", "contract2", contract2.ProtectionLevel, contract2.HasProtectionLevel);
輸出結果:
1: Contract ProtectionLevel HasProtectionLevel
2: contract1 None False
3: contract2 None True
關於消息保護級別與綁定的關係,還有一點需要著重強調。雖然我們可以對於同一個服務契約下操作設置不同的保護級別,但是在WSDL中需要基於WS-Addressing中的尋址(Addressing)機製來識別基於操作的保護級別。。比如說對於如下定義的服務契約ICalculator,在使用BasicHttpBinding的情況下,。
1: [ServiceContract]
2: public interface ICalculator
3: {
4: [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
5: double Add(double x, double y);
6: [OperationContract(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
7: double Substract(double x, double y);
8: }
這實際上會為你的應用帶來一個很隱晦的問題,為了將這個問題闡述得更加清楚,我通過一個例子來說明。還是應用我們的計算服務的例子,下麵是我們再熟悉不過的服務契約的定義,Add操作的保護級別被設置成。
1: [ServiceContract(Namespace = "https://www.artech.com/")]
2: public interface ICalculator
3: {
4: [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
5: double Add(double x, double y);
6: }
但是這個服務契約並被客戶端共享,而客戶端服務契約中定義了一個額外的操作Substract,該操作的保護級別並未作顯式設置。
1: [ServiceContract(Namespace = "https://www.artech.com/")]
2: public interface ICalculator
3: {
4: [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
5: double Add(double x, double y);
6: [OperationContract]
7: double Substract(double x, double y);
8: }
現在選擇BasicHttpBinding作為終結點的綁定,並將安全模式甚至成Message。當你客戶端調用Add操作的時候。會拋出如下圖所示的MessageSecurityException異常,提示“主簽名必須加密”。但是。
出現這樣的異常的原因在於:對於不支持WS-Addressing的BasicHttpBinding來說,會選擇所有操作中等級最高的那個最為所有操作的保護級別。對於客戶端來說,由於Substract沒有對保護級別進行顯式設置,默認采用最高等級的EncryptAndSign。但是服務端的等級確是Sign。
在這種情況下,請求消息會同時被加密和簽名。請求消息被服務端接受之後,雖然它對應的等級是Sign,但是依然能夠處理該請求。這就是所謂的“消息保護級別的最低標準”原則,定義在契約中的保護級別隻是確立了一個消息保護的“底線”。你不能低於這個最低標準,但是可以高於它。但是服務執行正常的運算後,隻會按照定義在本地契約中設置的保護級別對回複消息進行簽名。客戶端接受到這個僅僅被簽名的回複消息,會發現等級不夠,所以才會提示你“主簽名必須加密”。
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 16:34:25