2009-05-30 29 views
6

Tôi đã cố gắng để bảo vệ WCF hoạt động cho dự án của mình và đã có rất ít may mắn. Tôi đang cố gắng để tạo ra một dịch vụ sử dụng net.tcp như là ràng buộc, và hiện cả tin nhắn và vận chuyển an ninh. Bảo mật thư được thực hiện bằng cách sử dụng tên người dùng và mật khẩu và bảo mật giao thông được thực hiện (được cho là!) Bằng cách sử dụng chứng chỉ.Bảo mật Giao thông WCF bằng Chứng chỉ là bỏ qua sự tin tưởng của chuỗi

Để thử nghiệm phát triển, tôi đã tạo cơ quan cấp chứng chỉ của riêng mình và đặt chứng chỉ này trong cửa hàng đáng tin cậy của máy tính của tôi (LocalMachine). Sau đó, tôi đã tạo hai chứng chỉ, mỗi chứng chỉ được cấp bởi tổ chức phát hành chứng chỉ của tôi, một cho dịch vụ được sử dụng và một cho ứng dụng khách sử dụng. Tôi đặt cả hai trong số này trong cửa hàng cá nhân (My) trong LocalMachine. Sau đó, để thử nghiệm, tôi đã tạo một chứng chỉ ngẫu nhiên không được ký bởi tổ chức phát hành chứng chỉ của tôi (và do đó không đáng tin cậy) và được đặt trong cửa hàng Cá nhân trong LocalMachine. Tôi đã sử dụng makecert để tạo các chứng chỉ này.

Sau đó, tôi đã định cấu hình ứng dụng khách kết nối với dịch vụ để sử dụng chứng chỉ không tin cậy không hợp lệ làm chứng chỉ ứng dụng khách. Dịch vụ được thiết lập (được cho là) ​​để kiểm tra chứng chỉ ứng dụng khách sử dụng chuỗi tin cậy. Tuy nhiên, khách hàng này có thể kết nối và nói chuyện thành công với dịch vụ! Nó sẽ bị từ chối, bởi vì chứng chỉ của nó không đáng tin cậy!

Tôi không biết điều gì gây ra hành vi này, vì vậy tôi gửi vấn đề cho các bạn để xem những gì bạn tạo ra. Dưới đây là cấu hình WCF của tôi:

Dịch vụ conf:

<system.serviceModel> 
    <services> 
     <service behaviorConfiguration="DHTestBehaviour" name="DigitallyCreated.DHTest.Business.DHTestBusinessService"> 
      <endpoint address="" binding="netTcpBinding" contract="DigitallyCreated.DHTest.Business.IDHTestBusinessService" bindingConfiguration="DHTestNetTcpBinding" bindingNamespace="http://www.digitallycreated.net/DHTest/v1" /> 

      <host> 
       <baseAddresses> 
        <add baseAddress="net.tcp://localhost:8090/"/> 
        <add baseAddress="http://localhost:8091/"/> 
       </baseAddresses> 
      </host> 
     </service> 
    </services> 
    <behaviors> 
     <serviceBehaviors> 
      <behavior name="DHTestBehaviour"> 
       <serviceMetadata httpGetEnabled="true"/> 
       <serviceDebug includeExceptionDetailInFaults="true"/> 
       <serviceCredentials> 
        <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="DHTestMembershipProvider"/> 
        <serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" findValue="CN=business.dhtestDHTest.com" /> 
        <clientCertificate> 
         <authentication certificateValidationMode="ChainTrust" trustedStoreLocation="LocalMachine" revocationMode="NoCheck" /> 
        </clientCertificate> 
       </serviceCredentials> 
       <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="DHTestRoleProvider" /> 
      </behavior> 
     </serviceBehaviors> 
    </behaviors> 
    <bindings> 
     <netTcpBinding> 
      <binding name="DHTestNetTcpBinding"> 
       <security mode="TransportWithMessageCredential"> 
        <message clientCredentialType="UserName"/> 
        <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign"/> 
       </security> 
      </binding> 
     </netTcpBinding> 
    </bindings> 
</system.serviceModel> 

Khách hàng Conf:

<system.serviceModel> 
    <bindings> 
     <netTcpBinding> 
      <binding name="NetTcpBinding_IDHTestBusinessService" closeTimeout="00:01:00" 
      openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 
      transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" 
      hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288" 
      maxBufferSize="65536" maxConnections="10" maxReceivedMessageSize="65536"> 
       <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" 
       maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 
       <reliableSession ordered="true" inactivityTimeout="00:10:00" 
       enabled="false" /> 
       <security mode="TransportWithMessageCredential"> 
        <transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" /> 
        <message clientCredentialType="UserName" /> 
       </security> 
      </binding> 
     </netTcpBinding> 
    </bindings> 
    <behaviors> 
     <endpointBehaviors> 
      <behavior name="DHTestBusinessServiceEndpointConf"> 
       <clientCredentials> 
        <clientCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" findValue="CN=invalid"/> 
        <serviceCertificate> 
         <authentication revocationMode="NoCheck" trustedStoreLocation="LocalMachine"/> 
        </serviceCertificate> 
       </clientCredentials> 
      </behavior> 
     </endpointBehaviors> 
    </behaviors> 
    <client> 
     <endpoint address="net.tcp://phoenix-iv:8090/" binding="netTcpBinding" 
     behaviorConfiguration="DHTestBusinessServiceEndpointConf" 
     bindingConfiguration="NetTcpBinding_IDHTestBusinessService" 
     contract="DHTest.NetTcp.Business.IDHTestBusinessService" 
     name="NetTcpBinding_IDHTestBusinessService"> 
      <identity> 
       <dns value="business.dhtest.com" /> 
      </identity> 
     </endpoint> 
    </client> 
</system.serviceModel> 

Tên người dùng client/mật khẩu mã auth:

DHTestBusinessServiceClient client = new DHTestBusinessServiceClient(); 
client.ClientCredentials.UserName.UserName = "ratfink"; 
client.ClientCredentials.UserName.Password = "testpassword"; 

Thank y ou cho sự giúp đỡ của bạn trước.

EDIT (2009/06/01):

Một người bạn của tôi đã chỉ cho tôi hướng tới a blog mà trả lời câu hỏi là tại sao điều này xảy ra. Rõ ràng, khi bạn chỉ định TransportWithMessageCredential có nghĩa là chính xác rằng: Giao thông vận tải với thông tin đăng nhập tin nhắn chỉ. Đây là lý do tại sao chứng chỉ của tôi bị bỏ qua ở cấp độ vận chuyển.

Tuy nhiên, tôi không xem vấn đề đã hoàn tất và đã đóng, vì tôi vẫn muốn thực hiện việc này. :) Tôi sẽ xem xét các trình xác nhận chứng chỉ tùy chỉnh mà tôi nghĩ rằng tôi có thể cắm vào và xem liệu nó có hoạt động hay không. Tôi sẽ trả lời tất cả các bạn với kết quả.

EDIT (2009/06/08):

Không, xác nhận giấy chứng nhận tùy chỉnh không hoạt động hoặc. WCF chỉ đơn giản là không gọi cho họ.

Trả lời

4

Tôi đã tìm thấy giải pháp cho vấn đề của mình, tuy nhiên, hóa ra lại khó khăn hơn tôi mong đợi.

Về cơ bản, để đạt được cả thông tin xác thực Giao thông và thông báo, bạn cần xác định ràng buộc tùy chỉnh. (Tôi đã tìm thấy thông tin cho hiệu ứng này here).

Tôi thấy cách dễ nhất để làm điều này là tiếp tục thực hiện cấu hình của bạn trong XML, nhưng ở bản sao thời gian chạy và sửa đổi một chút ràng buộc netTcp từ cấu hình XML. Có nghĩa là một chuyển đổi bạn cần phải kích hoạt. Dưới đây là đoạn code ở phía dịch vụ và trên các mặt hàng:

Dịch vụ Side

ServiceHost businessHost = new ServiceHost(typeof(DHTestBusinessService)); 
ServiceEndpoint endpoint = businessHost.Description.Endpoints[0]; 
BindingElementCollection bindingElements = endpoint.Binding.CreateBindingElements(); 
SslStreamSecurityBindingElement sslElement = bindingElements.Find<SslStreamSecurityBindingElement>(); 
sslElement.RequireClientCertificate = true; //Turn on client certificate validation 
CustomBinding newBinding = new CustomBinding(bindingElements); 
NetTcpBinding oldBinding = (NetTcpBinding)endpoint.Binding; 
newBinding.Namespace = oldBinding.Namespace; 
endpoint.Binding = newBinding; 

Client Side

DHTestBusinessServiceClient client = new DHTestBusinessServiceClient(); 
ServiceEndpoint endpoint = client.Endpoint; 
BindingElementCollection bindingElements = endpoint.Binding.CreateBindingElements(); 
SslStreamSecurityBindingElement sslElement = bindingElements.Find<SslStreamSecurityBindingElement>(); 
sslElement.RequireClientCertificate = true; //Turn on client certificate validation 
CustomBinding newBinding = new CustomBinding(bindingElements); 
NetTcpBinding oldBinding = (NetTcpBinding)endpoint.Binding; 
newBinding.Namespace = oldBinding.Namespace; 
endpoint.Binding = newBinding; 

Bạn sẽ nghĩ rằng muốn có nó, nhưng bạn đã sai rồi! :) Đây là nơi nó được thêm què. Tôi đã phân bổ các phương thức dịch vụ cụ thể của tôi với PrincipalPermission để hạn chế quyền truy cập dựa trên vai trò của người dùng dịch vụ như sau:

[PrincipalPermission(SecurityAction.Demand, Role = "StandardUser")] 

Điều này bắt đầu thất bại ngay khi tôi áp dụng các thay đổi trên. Lý do là vì số

OperationContext.Current.ServiceSecurityContext.PrimaryIdentity 

kết thúc là không xác định, tên người dùng ít hơn, chưa được xác thực IIdentity. Điều này là do thực tế có hai số danh tính đại diện cho người dùng: một cho chứng chỉ X509 được sử dụng để xác thực qua Giao thông và một cho thông tin đăng nhập tên người dùng và mật khẩu được sử dụng để xác thực ở cấp Thư. Khi tôi đảo ngược thiết kế các mã nhị phân WCF để xem tại sao nó không cho tôi PrimaryIdentity của tôi, tôi thấy rằng nó có một dòng mã rõ ràng làm cho nó trở lại mà trống rỗng IIdentity nếu nó tìm thấy nhiều hơn một IIdentity. Tôi đoán đó là bởi vì nó không có cách nào để tìm ra cái nào là chính.

Điều này có nghĩa là sử dụng thuộc tính PrincipalPermission nằm ngoài cửa sổ. Thay vào đó, tôi đã viết một phương pháp để bắt chước chức năng của nó mà thể thỏa thuận với nhiều IIdentities:

private void AssertPermissions(IEnumerable<string> rolesDemanded) 
{ 
    IList<IIdentity> identities = OperationContext.Current.ServiceSecurityContext.AuthorizationContext.Properties["Identities"] as IList<IIdentity>; 
    if (identities == null) 
     throw new SecurityException("Unauthenticated access. No identities provided."); 

    foreach (IIdentity identity in identities) 
    { 
     if (identity.IsAuthenticated == false) 
      throw new SecurityException("Unauthenticated identity: " + identity.Name); 
    } 

    IIdentity usernameIdentity = identities.Where(id => id.GetType().Equals(typeof(GenericIdentity))).SingleOrDefault(); 
    string[] userRoles = Roles.GetRolesForUser(usernameIdentity.Name); 

    foreach (string demandedRole in rolesDemanded) 
    { 
     if (userRoles.Contains(demandedRole) == false) 
      throw new SecurityException("Access denied: authorisation failure."); 
    } 
} 

Nó không đẹp (đặc biệt là cách tôi phát hiện tên người dùng/mật khẩu chứng IIdentity), nhưng nó hoạt động! Bây giờ, ở phía trên cùng của phương pháp dịch vụ của tôi, tôi cần phải gọi nó như thế này:

AssertPermissions(new [] {"StandardUser"}); 
+1

+1 Tôi biết đó là cũ nhưng cũng được thực hiện, có vẻ tốt hơn nhiều so với giải pháp của tôi (hay tôi nên gọi nó là hack) ở đây: http://stackoverflow.com/questions/9111361/wcf-service-with-wshttpbinding-manipulating-http-request-headers –

0

xem Codeplex - Intranet Section cung cấp danh sách kiểm tra cho các trường hợp khác nhau. Một thứ khác cần lưu ý là netTcpBindings không được hỗ trợ để sử dụng trong IIS5.0 và IIS6.0 - xem 3rd paragraph here, chỉ IIS7.0 +.

Các vấn đề liên quan