问题 使用支持流式传输的basicHttpBinding保护WCF服务


我的问题是关于安全访问WCF服务的最佳(也称为“最不痛苦”)方式,该服务仅暴露给我们公司的内部用户。目标是确保只通过我们每个用户安装的单个Windows窗体应用程序访问该服务。调用该服务时,我希望该服务能够验证它是否已从允许的应用程序中调用。

要保护的服务使用basicHttpBinding,它支持流式传输,所以我相信我仅限于传输级安全性。

以下是该版本的简化版本 <bindings> 和 <services> 我服务的配置文件中的部分。

<bindings>
  <basicHttpBinding>
    <binding name="Service1Binding" transferMode="Streamed"/>    
  </basicHttpBinding>
</bindings>

<services>
    <service name="WCFServiceSecurity.Service1" 
        behaviorConfiguration="WCFServiceSecurity.Service1Behavior">
        <endpoint address=""
            binding="basicHttpBinding"
            contract="WCFServiceSecurity.IService1"
            bindingConfiguration="Service1Binding"/>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
    </service>
</services>

任何人都可以提供一些细节,说明为了实现此服务的安全性我需要采取什么行动?

注意:我是WCF的新手并且根本不熟悉安全性,所以如果我没有提供足够的详细信息,请告诉我。


更新:

由marc_s建议,我想使用某种用户名/密码机制来保护WCF服务。这为答案提供了更多的方向,但我仍然有点模糊 怎么样 实际上这样做。

因为我的服务需要启用流媒体,所以我必须使用basicHttpBinding和传输级安全性(对吗?);除此之外,我的服务中包含的方法只能接受Stream对象。

考虑到这些限制以及我使用用户名/密码验证的偏好...

  • 我应该如何修改服务的配置文件以强制提供用户名/密码凭据?
  • 我的服务如何验证提供的凭据?
  • 我的客户端应用程序在拨打电话时如何通过服务凭证?
  • 这是否需要使用SSL,如果是这样,所有客户端机器是否也需要证书?

更新:

在解释了我向老板提供此服务后遇到的麻烦之后,我获得了尝试Windows身份验证路由的权限。可悲的是,我没有幸运用我的流媒体服务(argh)实现这种类型的身份验证。进行适当的更改后(如概述 这里  - 唯一的例外是我的 transferMode="Streamed")并访问我的服务,我收到以下错误:

HTTP请求流不能与HTTP身份验证结合使用。禁用请求流或指定匿名HTTP身份验证。

然后我偶然发现了以下引用 这里 这提供了一些澄清:

你不能做运输认证。与流媒体。 如果必须使用HTTP请求流,则必须在没有安全性的情况下运行。

安全的工作方式是:

WCF客户端向服务器发出http请求。

服务器回答说:“你没有被授权,请给我一个基本/摘要/等证书。”

客户端获得该响应并使用所附的凭据重新发送其消息。

现在,服务器获取消息,验证凭据,然后继续。   请求Streaming不适用于该安全模式。如果确实如此,它会非常慢,因为客户端会发送整个流,从服务器获取未经授权的消息,然后它必须使用凭据重新发送整个流。

所以现在我正在寻找意见, 您如何保护启用流媒体的WCF服务? 如前所述,某种用户名/密码机制将是首选。你可以在这个盒子外面思考......

任何帮助 非常 不胜感激!


7845
2018-05-10 14:18


起源

如果可能,将绑定更改为StreamedRequest或StreamedResponse,您将能够启用传输身份验证。如果必须使用流式绑定,但我确信您可以将上载和下载拆分到不同的端点以启用此安全性。 - Tri Q Tran


答案:


好吧,我在处理这个问题时发现了许多围绕安全性/流媒体的问题。黑客攻击(呃...嗯......解决方法)我最终最终会创建一个新的DataContract,它继承了MemoryStream,并用BaseStream属性(用于保存我想要的流数据)和适当的属性来装饰它。简单认证。

以下是生成的DataContract:

[DataContract]
[KnownType( typeof( MemoryStream ) )] 
public class StreamWithCredentials : MemoryStream
{
    [DataMember]
    public Stream BaseStream { get; set; }

    [DataMember]
    public string Username { get; set; }

    [DataMember]
    public string Password { get; set; }
}

上面的DataContract最终成为我服务方法的输入参数。我的服务采取的第一个操作是根据已知的有效值对提供的凭据进行身份验证,并在适当时继续。

现在我做 知道 这是  最安全的选择,但我的指示是避免使用SSL(无论如何,我甚至不确定是否可能 - 如上所述 这里)对于这个内部过程。

话虽这么说,这是我能提出的上述问题的最佳解决方案,希望这有助于其他任何受此问题困扰的人。

感谢所有回复的人。


6
2018-05-12 16:25





你可以做很多事情:

  • 将证书添加到允许使用您的服务的每台计算机上,并检查该证书。这只允许您排除“未授权”的计算机 - 您不能将其限制为特定的应用程序
  • 与上面相同,但包括winforms app中嵌入的证书并从那里发送(不要将其存储在机器的证书库中)
  • 需要用户名/密码,只有您的特定应用程序知道并可以传输到您的服务;例如其他人将无法提供适当的凭据

编辑2:好的,所以用户名/密码方法似乎失控了....如果你只有基本的传输安全(SSL)进行基本保护,然后使用 MessageContract 要定义SOAP消息的标题和正文,在标题中包含特定值,然后只检查服务标题中元素的存在?

像这样的东西:

[DataContract]
class YourRequestData
{
 ...
}

[MessageContract]
public class YourRequest
{
  [MessageBodyMember]
  public YourRequestData bodyData { get; set; }

  [MessageHeader]
  public string AppThumbprint { get; set; }
}

然后在您的代码中的服务器上检查它的存在性和有效性 AppThumbprint 码:

public Stream RequestStream(YourRequest request)
{
  if(AppThumbprintIsValid(request.AppThumbprint))
  {
     .... begin your streaming
  }
}

这可能最终比用户名/密码安全方案容易得多。

渣子


1
2018-05-10 16:49



我想避免将证书添加到客户端计算机,因为应用程序的用户经常更改。我想实现您提到的用户名/密码方法,但我不确定如何将这些值传输到实际服务。服务公开的方法已启用流式传输,因此我无法将这些值作为参数传递(由于启用流式传输服务的方法签名限制);你能推荐一种方法将un / pw组合传递给服务并从服务中进行身份验证吗? - Steve Dignan
在我修改我的服务配置并运行服务之后,我得到错误“BasicHttp绑定要求BasicHttpBinding.Security.Message.ClientCredentialType等同于安全消息的BasicHttpMessageCredentialType.Certificate凭证类型。为UserName凭证选择Transport或TransportWithMessageCredential安全性。 ”。我认为启用流媒体的端点需要传输级安全性?此外,在<endpoint>标记中,我认为绑定值是basicHttpBinding而不是basicHttpConfiguration。 - Steve Dignan
感谢Marc的所有帮助,但遗憾的是我不认为这种方法(编辑2)将适用于我的特定场景。被调用的服务方法必须接受并返回Stream。你的答案提出了一个有趣的想法;我想我可以继承一个Stream并使用一些un / pw属性来装饰它;这个Stream可能是我的服务方法的输入参数(不过分安全,请注意)。但鉴于WCF的明显灵活性,我不得不假设有更好的方法......嗯。 - Steve Dignan


如果我错了请纠正我,但是:

如果您正在为WCf服务使用表单身份验证(在asp.net上),只需在服务中添加一个登录方法,在其中创建所需的cookie(formsAuthentication.Authenticate())。随着响应自动发送,客户端可以调用流API而无需额外的参数(要求它是STREAM),并且您可以在关闭返回流之前检查流API中的身份。

至于保护对整个WCF的访问,我觉得在.net应用程序中嵌入证书是一种方法。他们将不得不使用你的应用来实现它。

你可以告诉asp.net/wcf不要提供wsdl,或者更准确地说,不要自动生成wsdl。没有wsdl访问,他们就很难生成代理....


1
2018-01-30 07:52





如果你想使用 basicHttpBinding (对于互操作)您只能在消息级别传递您的凭据。您必须将安全配置设置为 TransportWithMessageCredential

要做到这一点,你必须创建一个SSL通道,所以你需要在服务器端有一个证书,而对于一个拥有一个的客户来说并不是必需的。


1
2017-07-05 21:37





可以对Streaming和SSL使用Windows身份验证,但必须使用 TransportWithMessageCredential

<basicHttpBinding>
    <binding name="FileService.FileServiceBinding" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" transferMode="Streamed">
        <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
        <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="Windows" />
        </security>
    </binding>
</basicHttpBinding>

您需要设置代码 proxy.ClientCredentials.UserName.UserName 和 proxy.ClientCredentials.UserName.Password


1
2017-10-04 12:01



我认为TransportWithMessageCredential不能用于basicHttpBinding,只能用于wsHttpBinding? - Andrei Dvoynos


如果这将是一个存在于Intranet上的应用程序,则最简单的方法是在Active Directory中创建一个新组,并且只允许该组的成员使用该服务。
 

您可以使用以下内容添加身份验证(使用Windows凭据):

<basicHttpBinding> 
 <security mode="TransportCredentialOnly"> 
  <transport clientCredentialType="Windows" /> 
 </security> 
</basicHttpBinding> 

然后可以通过将接口修改为您的服务方法来进行授权:

<PrincipalPermission(SecurityAction.Demand, Role:="MyAppsUsers")> _ 
Public Function MyMethod() As String Implements IService.MyMethod 

这是WCF中安全性的良好链接。它最后有很多方法(标题为'如何 - 使用带有Windows身份验证和TransportCreditals的basicHttpBinding'可能对您有用)。
Wcf Secruity

[免责声明:我也是WCF的新手并没有做过这个确切的案例,所以如果稍微偏了就道歉!]


0
2018-05-10 17:17



由于我们试图确保只从单个应用程序访问WCF服务,我不认为使用Windows凭据会这样做,因为这些用户可能会从应用程序外部调用服务(如果我错了,请纠正我) 。提供用户名/密码到服务似乎是要走的路(从我有限的经验)。感谢WCF安全指南的链接,我也将查看它,看看我可以应用于我的场景。 - Steve Dignan