WCF的安全審核——記錄誰在敲打你的門
WCF所謂的安全審核就是針對(duì)認(rèn)證和授權(quán)所做的針對(duì)EventLog的日志記錄。我們不但可以設(shè)置進(jìn)行審核的事件(認(rèn)證成功/失敗,授權(quán)成功或失敗),還可以選擇記錄信息被寫入的EventLog類型,即應(yīng)用程序日志(Application)還是安全日志(Security)。WCF的安全審核是通過ServiceSecurityAuditBehavior服務(wù)行為實(shí)現(xiàn)的。
一、ServiceSecurityAuditBehavior服務(wù)行為
針對(duì)WCF安全審核的編程只涉及ServiceSecurityAuditBehavior服務(wù)行為。下面給出了定義在ServiceSecurityAuditBehavior中具體審核行為進(jìn)行控制的三個(gè)可讀寫的屬性。
1: public sealed class ServiceSecurityAuditBehavior : IServiceBehavior
2: {
3: //其他成員
4: public AuditLogLocation AuditLogLocation { get; set; }
5: public AuditLevel MessageAuthenticationAuditLevel { get; set; }
6: public AuditLevel ServiceAuthorizationAuditLevel { get; set; }
7: public bool SuppressAuditFailure { get; set; }
8: }
屬性AuditLogLocation代表的是日志信息被寫入的EventLog類型,該屬性的類型是一個(gè)具有如下定義的AuditLogLocation枚舉。其中Application和Security分別代表應(yīng)用程序日志和安全日志。如果選擇Default,則最終日志被寫入的位置決定于當(dāng)前的操作系統(tǒng)。如果支持寫入安全日志,則選擇安全日志類型,否則選擇應(yīng)用程序日志類型。Default是默認(rèn)選項(xiàng)。
1: public enum AuditLogLocation
2: {
3: Default,
4: Application,
5: Security
6: }
MessageAuthenticationAuditLevel和ServiceAuthorizationAuditLevel兩個(gè)屬性分別代表針對(duì)認(rèn)證和授權(quán)審核的級(jí)別。所謂審核的級(jí)別在這里指的應(yīng)該在審核事件(認(rèn)證和授權(quán))在成功或者失敗的情況下進(jìn)行日志記錄。審核級(jí)別通過具有如下定義的AuditLevel枚舉表示。Success和Failure代表分別針對(duì)認(rèn)證/授權(quán)成功和失敗進(jìn)行審核日志。SuccessOrFailure則意味著不管認(rèn)證/授權(quán)是成功還是失敗,都會(huì)進(jìn)行審核日志。None為默認(rèn)值,表示不進(jìn)行審核日記記錄。
1: public enum AuditLevel
2: {
3: None,
4: Success,
5: Failure,
6: SuccessOrFailure
7: }
布爾類型的SuppressAuditFailure屬性表示審核日志失敗是否會(huì)影響應(yīng)用本身。在默認(rèn)的情況下該屬性值為True,意味著為認(rèn)證和授權(quán)進(jìn)行審核日志的時(shí)候出現(xiàn)的異常不會(huì)對(duì)應(yīng)用(服務(wù))本身造成任何影響。
既然是服務(wù)行為,我們就可以通過將創(chuàng)建的ServiceSecurityAuditBehavior添加到服務(wù)的行為列表的方式來進(jìn)行安全審核的控制。當(dāng)然我們還是推薦采用配置的方式來進(jìn)行安全什么的相關(guān)設(shè)置。服務(wù)行為ServiceSecurityAuditBehavior對(duì)應(yīng)的配置節(jié)是<serviceSecurityAudit>。在下面的配置中,我定義了一個(gè)包含了ServiceSecurityAuditBehavior的服務(wù)行為,并對(duì)其四個(gè)屬性進(jìn)行了顯式設(shè)置。
1: <configuration>
2: <system.serviceModel>
3: <behaviors>
4: <serviceBehaviors>
5: <behavior ...>
6: <serviceSecurityAudit auditLogLocation ="Application"
7: messageAuthenticationAuditLevel ="Failure"
8: serviceAuthorizationAuditLevel="SuccessOrFailure"
9: suppressAuditFailure="true"/>
10: </behavior>
11: </serviceBehaviors>
12: </behaviors>
13: </system.serviceModel>
14: </configuration>
二、安全審核的實(shí)現(xiàn)
WCF最終進(jìn)行安全審核的控制信息是從基于某個(gè)終結(jié)點(diǎn)的分發(fā)上下文中獲取的。如下面的代碼片段所示,對(duì)于定義在ServiceSecurityAuditBehavior中的四個(gè)屬性,在DispatchRuntime中具有相應(yīng)的定義。WCF在認(rèn)證和授權(quán)成功或者失敗的時(shí)候,會(huì)根據(jù)該運(yùn)行時(shí)這四個(gè)屬性進(jìn)行相應(yīng)安全審核日志。
1: public sealed class DispatchRuntime
2: {
3: //其他成員
4: public AuditLogLocation SecurityAuditLogLocation { get; set; }
5: public AuditLevel MessageAuthenticationAuditLevel { get; set; }
6: public AuditLevel ServiceAuthorizationAuditLevel { get; set; }
7: public bool SuppressAuditFailure { get; set; }
8: }
而作為服務(wù)行為的ServiceSecurityAuditBehavior,最終的目的就是將定義在自身的這四個(gè)屬性賦值給分發(fā)上下文。而具體操作定義在ApplyDispatchBehavior方法上,整個(gè)邏輯大體上如下面的代碼所示。
1: public sealed class ServiceSecurityAuditBehavior : IServiceBehavior
2: {
3: //其他成員
4: public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
5: ServiceHostBase serviceHostBase)
6: {
7: foreach (ChannelDispatcher channelDispatcher in
8: serviceHostBase.ChannelDispatchers)
9: {
10: foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
11: {
12: DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
13: dispatchRuntime.SecurityAuditLogLocation = this.AuditLogLocation;
14: dispatchRuntime.MessageAuthenticationAuditLevel = this.MessageAuthenticationAuditLevel;
15: dispatchRuntime.ServiceAuthorizationAuditLevel = this.ServiceAuthorizationAuditLevel;
16: dispatchRuntime.SuppressAuditFailure = this.SuppressAuditFailure;
17: }
18: }
19: }
20: }
三、實(shí)例演示:如何實(shí)施安全審核
接下來我們通過一個(gè)簡單的實(shí)例來演示如何進(jìn)行針對(duì)認(rèn)證和授權(quán)的安全審核,并且看看在認(rèn)證或者授權(quán)成功或者失敗的情況下,會(huì)有怎樣的日志信息被記錄下來。
基于認(rèn)證的安全審核
先來演示針對(duì)認(rèn)證的安全審核。我們還是直接使用一直在使用的計(jì)算服務(wù)的例子,服務(wù)契約和服務(wù)的定義我們已經(jīng)很熟悉了,我們現(xiàn)在只來介紹采用的服務(wù)端的配置。從下面的配置可以看出,被寄宿的服務(wù)具有為一個(gè)基于WS2007HttpBinding的終結(jié)點(diǎn),該綁定采用默認(rèn)的Windows認(rèn)證。通過服務(wù)行為,我們將安全審核的AuditLogLocation和MessageAuthenticationAuditLevel分別設(shè)置為Application和SuccessOrFailure。
1: <configuration>
2: <system.serviceModel>
3: <services>
4: <service name="Artech.WcfServices.Service.CalculatorService"
5: behaviorConfiguration="authenticationAudit">
6: <endpoint address="http://127.0.0.1:3721/calculatorservice"
7: binding="ws2007HttpBinding"
8: contract="Artech.WcfServices.Service.Interface.ICalculator"/>
9: </service>
10: </services>
11: <behaviors>
12: <serviceBehaviors>
13: <behavior name="authenticationAudit">
14: <serviceSecurityAudit auditLogLocation ="Application"
15: messageAuthenticationAuditLevel ="SuccessOrFailure"/>
16: </behavior>
17: </serviceBehaviors>
18: </behaviors>
19: </system.serviceModel>
20: </configuration>
客戶端利用如下的代碼進(jìn)行服務(wù)的調(diào)用,兩次服務(wù)調(diào)分別采用Foo和Bar兩個(gè)本地Windows帳號(hào)進(jìn)行,其中基于帳號(hào)Foo給定的密碼是正確的,而基于帳號(hào)Bar給定的密碼是錯(cuò)誤的。而輔助方法Invoke旨在避免避免認(rèn)證失敗導(dǎo)致的異常是程序終止。
1: ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService");
2: NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;
3: credential.UserName = "Foo";
4: credential.Password = "Password";
5: ICalculator calculator = channelFactory.CreateChannel();
6: Invoke(calculator);
7:
8: channelFactory = new ChannelFactory<ICalculator>("calculatorService");
9: credential = channelFactory.Credentials.Windows.ClientCredential;
10: credential.UserName = "Bar";
11: credential.Password = "InvalidPass";
12: calculator = channelFactory.CreateChannel();
13: Invoke(calculator);
由于我們通過配置將應(yīng)用在服務(wù)上的ServiceSecurityAuditBehavior服務(wù)行為的AuditLogLocation和MessageAuthenticationAuditLevel分別設(shè)置為Application和SuccessOrFailure,意味著不論是認(rèn)證成功或者失敗都會(huì)進(jìn)行安全審核日志。而審核日志最終會(huì)被寫入EventLog的應(yīng)用程序日志。當(dāng)程序執(zhí)行后,在事件查看器的應(yīng)用程序節(jié)點(diǎn),你會(huì)發(fā)現(xiàn)具有如下圖所示的4條新的日志(之前的日志在程序運(yùn)行前被清空)。
下面列出了這4條日志的內(nèi)容。其中前3條基于認(rèn)證成功的信息(Information)日志,最后一條是基于認(rèn)證失敗的錯(cuò)誤(Error)日志。[源代碼從這里下載]
1: Security negotiation succeeded.
2: Service: http://127.0.0.1:3721/calculatorservice
3: Action: http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/Issue
4: ClientIdentity: Jinnan-PC\Foo; S-1-5-21-3534336654-2901585401-846244909-1006
5: ActivityId: <null>
6: Negotiation: SpnegoTokenAuthenticator
7:
8: Message authentication succeeded.
9: Service: http://127.0.0.1:3721/calculatorservice
10: Action: http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/SCT
11: ClientIdentity: Jinnan-PC\Foo; S-1-5-21-3534336654-2901585401-846244909-1006
12: ActivityId: <null>
13:
14: Message authentication succeeded.
15: Service: http://127.0.0.1:3721/calculatorservice
16: Action: http://www.artech.com/ICalculator/Add
17: ClientIdentity: Jinnan-PC\Foo; S-1-5-21-3534336654-2901585401-846244909-1006
18: ActivityId: <null>
19:
20: Security negotiation failed.
21: Service: http://127.0.0.1:3721/calculatorservice
22: Action: http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/Issue
23: ClientIdentity:
24: ActivityId: <null>
25: Negotiation: SpnegoTokenAuthenticator
26: Win32Exception: The Security Support Provider Interface (SSPI) negotiation failed.
基于授權(quán)的安全審核
接下來我們演示授權(quán)的安全審核,并查看分別在授權(quán)成功和失敗的情況下分別由怎樣的日志被寫入到EventLog中。我們首先按照如下的方式在服務(wù)類型CalculatorService的Add方法上應(yīng)用PrincipalPermissionAttribute特性使該方法只有在管理員才有權(quán)限調(diào)用。
1: public class CalculatorService : ICalculator
2: {
3: [PrincipalPermission(SecurityAction.Demand,Role="Administrators")]
4: public double Add(double x, double y)
5: {
6: return x + y;
7: }
8: }
然后我們將服務(wù)端配置中關(guān)于安全審核的相關(guān)配置進(jìn)行了如下的修改。我們只關(guān)心授權(quán)相關(guān)的安全審核,所以將messageAuthenticationAuditLevel屬性替換成serviceAuthorizationAuditLevel。
1: <configuration>
2: <system.serviceModel>
3: ...
4: <behaviors>
5: <serviceBehaviors>
6: <behavior name="authenticationAudit">
7: <serviceSecurityAudit auditLogLocation ="Application"
8: serviceAuthorizationAuditLevel ="SuccessOrFailure"/>
9: </behavior>
10: </serviceBehaviors>
11: </behaviors>
12: </system.serviceModel>
13: </configuration>
客戶端在進(jìn)行服務(wù)調(diào)用的時(shí)候需要為帳號(hào)Bar指定正確的密碼。和前面一樣,這里的帳號(hào)Foo被預(yù)先添加到管理員用戶組中,而Bar則沒有,所以只有第一次服務(wù)調(diào)用才是被成功授權(quán)的。
1: ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorService");
2: NetworkCredential credential = channelFactory.Credentials.Windows.ClientCredential;
3: credential.UserName = "Foo";
4: credential.Password = "Password";
5: ICalculator calculator = channelFactory.CreateChannel();
6: Invoke(calculator);
7:
8: channelFactory = new ChannelFactory<ICalculator>("calculatorService");
9: credential = channelFactory.Credentials.Windows.ClientCredential;
10: credential.UserName = "Bar";
11: credential.Password = "Password";
12: calculator = channelFactory.CreateChannel();
13: Invoke(calculator);
當(dāng)客戶端完成兩次服務(wù)調(diào)用后,如下兩條基于授權(quán)的審核日志被寫入應(yīng)用程序日志。雖然只有第一次服務(wù)調(diào)用才是真正被授權(quán)的操作,但是從日志的內(nèi)容我們卻發(fā)現(xiàn)兩條均是“授權(quán)成功”的審核日志。[源代碼從這里下載]
1: Service authorization succeeded.
2: Service: http://127.0.0.1:3721/calculatorservice
3: Action: http://www.artech.com/ICalculator/Add
4: ClientIdentity: Jinnan-PC\Foo; S-1-5-21-3534336654-2901585401-846244909-1006
5: AuthorizationContext: uuid-528e86ce-a4f4-48b3-9a2e-b713e1dea539-1
6: ActivityId: <null>
7: ServiceAuthorizationManager: <default>
8:
9: Service authorization succeeded.
10: Service: http://127.0.0.1:3721/calculatorservice
11: Action: http://www.artech.com/ICalculator/Add
12: ClientIdentity: Jinnan-PC\Bar; S-1-5-21-3534336654-2901585401-846244909-1007
13: AuthorizationContext: uuid-528e86ce-a4f4-48b3-9a2e-b713e1dea539-2
14: ActivityId: <null>
15: ServiceAuthorizationManager: <default>
實(shí)際對(duì)于安全審核中所謂的授權(quán)失敗指的是調(diào)用ServiceAuthorizationManager的CheckAccess方法返回False的情況。在默認(rèn)的情況下,ServiceAuthorizationManager的CheckAccess方法總是返回True,所以授權(quán)總是會(huì)“成功”。
為了迎合安全審核對(duì)“授權(quán)失敗”的判斷,我在Service項(xiàng)目中創(chuàng)建了如下一個(gè)簡單的自定ServiceAuthorizationManager。在重寫的CheckAccessCore方法中,如果當(dāng)前用戶是Foo(Jinnan-PC\Jinnan)就發(fā)返回True,否則返回False。
1: using System.ServiceModel;
2: namespace Artech.WcfServices.Service
3: {
4: public class MyServiceAuthorizationManager : ServiceAuthorizationManager
5: {
6: protected override bool CheckAccessCore(OperationContext operationContext)
7: {
8: string userName = operationContext.ServiceSecurityContext.PrimaryIdentity.Name;
9: return string.Compare(userName, @"Jinnan-PC\Foo",true) == 0;
10: }
11: }
12: }
然后通過如下的配置將這個(gè)自定義的MyServiceAuthorizationManager應(yīng)用到ServiceAuthorizationBehavior服務(wù)行為上。再次運(yùn)行我們的程序,將會(huì)得到分別代表授權(quán)成功和失敗的兩條審核日志,并且在日志中還包含了我們自定的ServiceAuthorizationManager類型(ServiceAuthorizationManager: MyServiceAuthorizationManager)。[源代碼從這里下載]
1: <configuration>
2: <system.serviceModel>
3: ...
4: <behaviors>
5: <serviceBehaviors>
6: <behavior name="authenticationAudit">
7: <serviceAuthorization serviceAuthorizationManagerType="Artech.WcfServices.Service.MyServiceAuthorizationManager, Artech.WcfServices.Service">
8: </serviceAuthorization>
9: <serviceSecurityAudit auditLogLocation ="Application"
10: serviceAuthorizationAuditLevel ="SuccessOrFailure"/>
11: </behavior>
12: </serviceBehaviors>
13: </behaviors>
14: </system.serviceModel>
15: </configuration>
授權(quán)成功/失敗審核日志:
1: Service authorization succeeded.
2: Service: http://127.0.0.1:3721/calculatorservice
3: Action: http://www.artech.com/ICalculator/Add
4: ClientIdentity: Jinnan-PC\Foo; S-1-5-21-3534336654-2901585401-846244909-1006
5: AuthorizationContext: uuid-91ff29c8-84cb-4c1a-837c-0913c12c0be5-1
6: ActivityId: <null>
7: ServiceAuthorizationManager: MyServiceAuthorizationManager
8:
9: Service authorization failed.
10: Service: http://127.0.0.1:3721/calculatorservice
11: Action: http://www.artech.com/ICalculator/Add
12: ClientIdentity: Jinnan-PC\Bar; S-1-5-21-3534336654-2901585401-846244909-1007
13: AuthorizationContext: uuid-91ff29c8-84cb-4c1a-837c-0913c12c0be5-2
14: ActivityId: <null>
15: ServiceAuthorizationManager: MyServiceAuthorizationManager
16: FaultException: Access is denied.


WCF所謂的安全審核就是針對(duì)認(rèn)證和授權(quán)所做的針對(duì)EventLog的日志記錄。我們不但可以設(shè)置進(jìn)行審核的事件(認(rèn)證成功/失敗,授權(quán)成功或失敗),還可以選擇記錄信息被寫入的EventLog類型,即應(yīng)用程序日志還是安全日志。WCF的安全審核是通過ServiceSecurityAuditBehavior服務(wù)行為實(shí)現(xiàn)的。

浙公網(wǎng)安備 33010602011771號(hào)