[WCF安全系列]消息的保護等級[下篇]
在《上篇》中,我們著重討論了消息的保護等級如果在契約中定義,定義在不同契約(服務契約、錯誤契約和消息契約)中的消息保護等級具有怎樣的層級關系,以及在默認情況下各種綁定采用怎樣的保護等級。在下篇中,我們進一步來探討消息保護等級和綁定的關系。
一、契約的保護等級為綁定進行消息保護設置了“最低標準”
二、顯式地將保護等級設置成ProtectionLevel.None與沒有設置保護等級有區別嗎?
三、消息的保護等級與WS-Addressing
一、契約的保護等級為綁定進行消息保護設置了“最低標準”
定義在契約上消息保護級別實際上為WCF實施消息保護設置了一個“最低標準”。由于整個消息保護機制,不論是簽名還是加密,都是在信道層實現的。而信道層最終是通過綁定來實現的,綁定的屬性決定了信道層處理消息的能力。而綁定安全方面的屬性自然就決定了最終的信道層是否有能力對消息實施簽名和加密。一方面,以契約形式定義的消息保護級別幫助信道層決定應該對傳入的消息采取那個級別的保護機制;另一方面,如果綁定所能提供的消息保護能力不能達到這個最低標準,就會拋出異常。
舉個例子,如果我們通過如下的代碼將服務契約ICalculator的Add操作的保護級別設置成EncryptAndSign。
1: [ServiceContract(Namespace = "http://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="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" bindingConfiguration="bindingWithNoneSecurityMode"
12: contract="Artech.WcfServices.Contracts.ICalculator"/>
13: </service>
14: </services>
15: </system.serviceModel>
二、顯式地將保護等級設置成ProtectionLevel.None與沒有設置保護等級有區別嗎?
在這里有一個很多人會忽視的要點。表示消息保護級別的ProtectionLevel類型是一個枚舉,所以它肯定有一個默認值。這個默認值就是None,也就是說當你沒有顯式地指定契約具有采用那么保護級別的時候,默認值就是None。但是這種情況和你顯式保護級別設置為None的效果是完全不一致的。因為前者真正采用的保護級別(當綁定安全被開啟)實際上是EncryptAndSign,后者才是None。那么WCF如何來區分這兩種情況呢?
如果你足夠細心,你應該會發現:在上面介紹的定義消息保護級別的特性中,除了具有一個可讀可寫的ProtectionLevel屬性之外,還具有一個只讀的HasProtectionLevel屬性,該屬性表示你是否對消息保護級別進行了“顯式”的設置。我們可以通過一個簡單的實驗來演示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
三、消息的保護等級與WS-Addressing
關于消息保護級別與綁定的關系,還有一點需要著重強調。雖然我們可以對于同一個服務契約下操作設置不同的保護級別,但是在WSDL中需要基于WS-Addressing中的尋址(Addressing)機制來識別基于操作的保護級別。在使用的綁定不支持WS-Addressing的情況下(比如BasicHttpBinding),它會選擇所有操作中等級最高的那個作為所有操作的保護級別。比如說對于如下定義的服務契約ICalculator,在使用BasicHttpBinding的情況下,兩個操作采用的保護級別都是EncryptAndSign。
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操作的保護級別被設置成Sign。
1: [ServiceContract(Namespace = "http://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 = "http://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異常,提示“主簽名必須加密”。但是當你將客戶端Substract刪除或者將Substract操作的消息保護級別也設置成Sign是,這個異常將不會出現。
出現這樣的異常的原因在于:對于不支持WS-Addressing的BasicHttpBinding來說,會選擇所有操作中等級最高的那個最為所有操作的保護級別。對于客戶端來說,由于Substract沒有對保護級別進行顯式設置,默認采用最高等級的EncryptAndSign。但是服務端的等級確是Sign。
在這種情況下,請求消息會同時被加密和簽名。請求消息被服務端接受之后,雖然它對應的等級是Sign,但是依然能夠處理該請求。這就是所謂的“消息保護級別的最低標準”原則,定義在契約中的保護級別只是確立了一個消息保護的“底線”。你不能低于這個最低標準,但是可以高于它。但是服務執行正常的運算后,只會按照定義在本地契約中設置的保護級別對回復消息進行簽名。客戶端接受到這個僅僅被簽名的回復消息,會發現等級不夠,所以才會提示你“主簽名必須加密”。


在《上篇》中,我們著重討論了消息的保護等級如果在契約中定義,定義在不同契約(服務契約、錯誤契約和消息契約)中的消息保護等級具有怎樣的層級關系,以及在默認情況下各種綁定采用怎樣的保護等級。在下篇中,我們進一步來探討消息保護等級和綁定的關系。


浙公網安備 33010602011771號