WCF安全实现设计问题

Posted

技术标签:

【中文标题】WCF安全实现设计问题【英文标题】:WCF security implementation design issue 【发布时间】:2012-05-29 08:29:40 【问题描述】:

我有创建 ASP.net/WinForms 应用程序的经验,现在我想通过创建一个简单的任务管理器项目来学习 WCF。

提前感谢您阅读传入的文本块。我的问题更多是设计问题,而不是实际的编码问题。

我的目标如下:

创建一个用于管理任务/待办事项列表的 Web 服务(使用 WCF) Web 服务将允许用户注册帐户、创建新的待办事项列表、与其他用户共享待办事项列表等 在 Web 服务正常运行并实现所有内容后,我希望能够在其上叠加一个 ASP.NET 网站并将 Web 服务用于后端

目前我有以下内容:

1 个托管 Web 服务的控制台应用程序 1 个控制台应用程序(客户端)用于调用 Web 服务(我以这种方式测试我的 Web 服务)

Web Service 应用程序具有以下配置文件(希望我粘贴成功):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Tasker_Server.Properties.Settings.TaskerConnectionString"
      connectionString="Data Source=PROPHET\SQLEXPRESS;Initial Catalog=Tasker;Persist     Security Info=True;User ID=sa;Password=stf"
  providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.serviceModel>
    <services>
      <service name="Tasker_Server.TaskerService" behaviorConfiguration="TaskerServiceBehavior">
        <host>
          <baseAddresses>
             <add baseAddress="http://localhost:8000/TaskerTest/Service" />
          </baseAddresses>
        </host>
        <endpoint name="login" address="username" binding="wsHttpBinding"
              bindingConfiguration="Binding1"
              contract="Tasker_Server.ITasker" />
        <endpoint name="reg" address="reg" binding="wsHttpBinding"
              bindingConfiguration="Binding2"
              contract="Tasker_Server.Contracts.IRegister" />
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="Binding1" receiveTimeout="00:20:00">
          <security mode="Message">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
        <binding name="Binding2">
          <security mode="None">
            <transport clientCredentialType="None" />
            <message establishSecurityContext="false" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="TaskerServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom"
                                customUserNamePasswordValidatorType="Tasker_Server.CustomValidator, Tasker_Server" />
            <serviceCertificate findValue="localhost"
                            storeLocation="LocalMachine"
                            storeName="My"
                            x509FindType="FindBySubjectName" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

然后我像这样启动 Web 服务:

ServiceHost selfHost = new ServiceHost(typeof(TaskerService));

try 
    selfHost.Open();
    Console.WriteLine("Service is up... (press <ENTER> to terminate)");
    Console.ReadLine();

    selfHost.Close();

catch (CommunicationException ce) 
    Console.WriteLine("Exception: 0", ce.Message);
    Console.ReadLine();
    selfHost.Abort();

目前我只有两个合同:

[ServiceContract(Namespace="http://Tasker_Server")]
public interface ITasker 
    [OperationContract]
    string CheckCredentials(string username, string password);

[ServiceContract(Namespace="http://Tasker_Register")]
public interface IRegister 
    [OperationContract]
    string RegisterUser(string username, string password, string email);

我试图完成的是:

    提供不安全的端点;客户可以调用它并注册一个帐户。 提供一个安全端点(我使用带有自定义 UserNamePasswordValidator 的 UserName 身份验证),用户可以通过该端点“登录”并调用所有操作。

我的软件中的这两个东西现在都可以使用。我可以通过不安全的端点注册新帐户,并且可以通过在客户端中提供正确的 ClientCredentials 来调用安全端点。

我的问题如下:

    据我了解,通过使用 UserName 身份验证,每次客户端调用 Web 服务方法时都会调用我的自定义验证器中的 Validation 方法(这意味着每次都会运行数据库查询以检查凭据与您登录一次直到会话到期的网站相反)。以这种方式做事有什么根本错误吗?

    我想到了另一种可能的方法来管理这个(并且以某种方式模拟网站的工作方式):

    仅将安全端点(用户名身份验证)用于类似于“登录”的操作 如果凭据正确,我将创建一个新的 GUID,将其保存在内存中并在用户名和该 GUID 之间建立关联。 那么所有操作都不需要用户名身份验证,但会有一个附加参数(GUID):如果 GUID 在内存中并与用户相关联,则允许该操作 注销操作会破坏内存中的 GUID。 我假设我可以在此基础上使用 SSL,这样 GUID 就不会以明文形式发送 这是否违背了 Web 服务安全的目的,而我只是想重新发明***?

哪种方法更好?为什么?

更新:添加了错误的配置文件。它来自客户端而不是 Web 服务。现在添加正确的。

【问题讨论】:

这个。是。一个很好的问题。 谢谢。我现在正在等待有人发表一些意见。 【参考方案1】:

您使用 guid 的方法很好。查看其他问题WCF ticket base authentication 和推荐帖子

WCF Custom Message Headers

更新:方法比较。

第一种方法的好处是单个操作的无状态/无会话。客户端和服务都不需要记住先前身份验证调用的详细信息。但是,如果同一个客户端进行多次调用,最好记住ticket,而不是每次都记住并发送用户名和密码。 如果您担心重复调用数据库,您可以缓存一段时间(例如 30 分钟) 用户名和密码(或更好的哈希值)并在进行数据库调用之前比较缓存字典中新请求的详细信息。

第二种方法需要在客户端保持状态(即票证),所以有点复杂。但它更安全,因为您不需要在整个会话期间记住用户名和密码。对于您的场景,我更喜欢票务方法。

顺便说一句,登录后我仍然建议使用https(通常性能损失不是必需的)但它可以防止网络嗅探器窃取票证并使用它代替用户执行一些恶意操作。

【讨论】:

相当不错。我可能最终会这样做。然而,我仍然想知道两者之间的优势/劣势是什么(除了明显的 db 调用)。 感谢您的回答和更新。我将使用您现在描述的简单自定义票务。也感谢您提到 https;本来打算在最后做的。【参考方案2】:

2 是一个合理的想法。它被称为 Window Identity Framework。

与您建议的唯一真正区别在于,在联合安全模型中,您的身份验证与应用程序分离。在 WIF 中,您向颁发令牌(您的 GUID)的受信任机构进行身份验证。令牌中加密的是一组声明(允许的操作)。客户端将令牌作为其 WCF 调用的一部分传递,声明被转移到安全原则,突然间,您的应用程序在不了解 WCF 的情况下执行基于角色的安全性。

WIF 的缺点是它很复杂并且可能需要相当长的时间。

WIF 的优势在于它由安全专家编写。如果您实际上正在构建一个处理金钱或敏感数据的商业应用程序,您应该仅出于这个原因使用 WIF。

这是older article,但对这些想法来说是一个很好的动力。然后是MSDN。

【讨论】:

Windows Identity Foundation 允许在同一个应用程序中实现身份验证,但对于所描述的场景,我认为这太过分了。 +1 告诉我有关 WIF(我个人不知道存在),但正如迈克尔所说,这对于我现在正在做的事情来说太过分了。不过,对于未来的项目,我会牢记这一点。【参考方案3】:

你没有在 WCF 中实现你想要的using sessions 吗?

【讨论】:

我想要的与 Michael Freidgeim 的建议非常接近,如果不完全相同的话。我仍在寻找有关我的问题部分的一些利弊。

以上是关于WCF安全实现设计问题的主要内容,如果未能解决你的问题,请参考以下文章

WCF ClientBase 线程安全吗?

WCF 传输与消息

通过消息头的 WCF 安全性

如何将(声明)安全令牌传递给启用 WIF 的 WCF 服务?

保护来自 Azure 函数的 WCF (asmx) 调用

WCF设计服务协议