WCF Security - Transport Level Security with username password - c#

I wish to provide security for a webservice. I initially used BasicHttpBinding with Transport security (ssl) and message security (to pass username and password for Custom Username Password Validation)...
But the consumers of the web service said they cant add a header to the soap message with security details.. and said they would like to pass it on the communication channel for the service.
Is it possible to do custom validation of user, with transport security with WCF?? I can't get it working :(
Any suggestions?
thanks
Neil

I think your consumers are asking for Basic Authentication. That is, where the authentication token is passed in the HTTP Authorization header rather than in the SOAP security header. Confidentiality of the credentials will be provided only by the transport (HTTPS).
The binding configuration for this is:
<basicHttpBinding>
<binding name="HTTPSwithBasicAuthentication">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
On the client, you set the credentials like this
serviceClient.ClientCredentials.UserName.UserName = "username";
serviceClient.ClientCredentials.UserName.Password = "password";
Assuming your service is hosted in IIS, remember to enable Basic Authentication in the IIS configuration.
To do the actual authorization of the users you will could implement a custom ServiceAuthorizationManager to contain your authorization logic. There are lots of examples of how to do that last part on the web.
Alternatively you could configure in a standard ASP.Net membership provider if that is appropriate for your application.
http://msdn.microsoft.com/en-us/library/bb398990(VS.100).aspx

Related

How to prevent duplicate HTTP requests with Windows Authentication

I'm working on an WCF-based client/server application (WCF is self-hosted, not in IIS).
The WCF service has an operation to upload a chunk of data to the server. The contract roughly looks like this:
void UploadChunk(int clientId, byte[] chunk);
We are using Windows Authentication (Kerberos/NTLM) so we cannot use streaming here.
The binding looks like this (client- and server-side):
new BasicHttpBinding
{
Security = new BasicHttpSecurity
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport = { ClientCredentialType = HttpClientCredentialType.Windows },
},
MaxReceivedMessageSize = 0x7fffffff,
ReaderQuotas = { MaxArrayLength = 0x800000 },
};
The client talks to the service via proxy objects derived from System.ServiceModel.ClientBase<TChannel>.
All of this works perfectly fine, but we observed that the WCF client sends each HTTP request twice, once without auth header and once again with the correct auth header. This is problematic because the requests will be pretty big and this behavior causes the request size to be two times the actual chunk size.
I already found out (https://weblog.west-wind.com/posts/2010/Feb/18/NET-WebRequestPreAuthenticate-not-quite-what-it-sounds-like) that setting WebRequest.PreAuthenticate to true remembers the auth header and reuses it for subsequent requests.
However from what I've seen up to now WCF does not expose a mechanism to modify the WebRequest instance.
Is there any solution for this problem`?
For Windows Authentication there will always be a challenge response (401) for your first request .
If you're in control of all clients I think the most practical solution is to implement an operation with a minimal payload.
Operation void IsAuthenticated() should do. For each client proxy instance you would then call IsAuthenticated before UploadChunk.
The IsAuthenticated request would get you over the 401 challenge response without sending the large payload but will authenticate the connection. Subsequent requests for that connection will not be challenged.
Edit :
The behaviour I described seems to only be applicable with IIS 8. So I took a closer look with two http.sys traces, one for an IIS hosted service and one for a self hosted service.
The IIS hosted service seems to utilize some sort of optimization with regards to authentication. The first request for the connection is authenticated using the Authenticator Sspi Authenticator. Subsequent requests are authenticated using the Fast Authenticator.
None of these events are present in the self host trace which leads me to the conclusion that self hosting is not optimized for Windows Authentication.
http.sys - trace IIS
http.sys - trace self host
Then I found this blog entry that proposes a solution using NTLM, a custom binding and the unsafeConnectionNtlmAuthentication setting for the HTTP transport. If you're willing to only use NTLM and the security concerns highlighted in the documentation are not a concern this seems to provide the behaviour you're looking for as per the http.sys trace.
http.sys trace - self host with custom binding
For the server use binding
<customBinding>
<binding name="myBinding">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport authenticationScheme="Ntlm" unsafeConnectionNtlmAuthentication="true"/>
</binding>
</customBinding>
For your client you can use a regular basicHttpBinding with Ntlm security:
<basicHttpBinding>
<binding name="BasicHttpBinding_ITest">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" />
</security>
</binding>
</basicHttpBinding>
Use HttWebRequest to call WCF service, creating full SOAP message manually. That will allow to set PreAuthenticate to true. Make first call with no payload, Authenticate(). Then make request with payload.

Could not establish secure channel for SSL/TLS with authority 'xxxxx.com'

I'm trying to write a client against a customer's SOAP webservice, using VS2013 and WCF. The webservice itself is behind their firewall, so they've established a proxy that I'm trying to contact. (The proxy seems to be implemented using MuleSoft's ESB, which may or may not be relevant.)
I've been given an https: url, and a username/password. When I load the url into a browser, I'm prompted for the username/password, and then I see the .wsdl. The .wsdl specifies an internal url that I can't access, but I figure that's for the actual site.
When I create a Service Reference in VS2013, using the proxy URL, I'm prompted for the username/password three times, then I get a proper client, settings in app.config, etc.
The generated bindings in the app.config are for a basicHttpBinding with security mode Transport, and an endpoint address pointing to that inaccessible internal url.
So, from the generated bindings, I:
Replace the inaccessible internal url with the proxy url I've been given.
Change the security mode to "TransportWithMessageCredentials"
<bindings>
<basicHttpBinding>
<binding name="MyCustomersServiceSoapBinding">
<security mode="TransportWithMessageCredential" >
<message clientCredentialType="UserName" />
</security>
</binding>
<binding name="MyCustomersServiceSoapBinding1" />
</basicHttpBinding>
</bindings>
Replace the ClientCredentials with username and password:
using (var client = new MyCustomersServiceClient())
{
var loginCredentials = new ClientCredentials();
loginCredentials.UserName.UserName = "ausername";
loginCredentials.UserName.Password = "apassword";
var defaultCredentials = client.Endpoint.Behaviors.Find<ClientCredentials>();
client.Endpoint.Behaviors.Remove(defaultCredentials);
client.Endpoint.Behaviors.Add(loginCredentials);
var myData = new MyData
{
};
var result = client.receiveData(myData);
}
When I run it, I get an exception:
Could not establish secure channel for SSL/TLS with authority 'xxxxx.com'.
Browsing around, most of what I find suggests problems with ssl certificates, but I'm not sure that makes sense. If that were the case, I'd expect to see issues when I view the .wsdl through the browser. And I thought that by removing the default client credentials, I'd be bypassing the certificate check. And I am seeing a few posts about more obscure problems that result in this same error message.
I've turned on SOAP message logging, but that's provided me with no information. It shows the failed outgoing message, but nothing of use.
So I've been looking at the traffic in Fiddler. I see two messages, an HTTP message to "Tunnel to" with Result 200, and an HTTPS message to the proxy url with Result 401.
At this point, I see two possibilities:
I need to install an SSL certificate, the way the error message would suggest, or
the problem is simply that I'm not providing the username/password to the service in a way that it understands, and it's rejecting my attempt to connect.
I'm leaning towards the latter. My problem? I know nothing about the system that's hosting the service. I'm passing username/password in what I thought was the usual mechanism for WCF, and it's not working.
So, finally, the questions:
Have I misled myself, and I do need to be messing about with SSL certificates?
If not, what do I do in WCF to pass a username/password to an HTTPS webservice, hosted by MuleSoft
ESB? (Mule EE Core Extensions/3.5.1, if that helps).
Not sure if the issue I encountered shares the same cause as your issue, but just in case I can help someone with this, adding requireClientCertificate=true solved my problem:
<bindings>
<customBinding>
<binding name="bindingName">
...
<httpsTransport requireClientCertificate="true"/>
</binding>
</customBinding>
</bindings>
I had the same error message, but the web service I'm consuming is over HTTPS and requires a SSL certificate as authentication.
Many endpoints have been disabling TLSV1.0 and TLSV1.1 recently
try:
CURL https://<<service host>> -v -TLSV1.0
and
CURL https://<<service host>> -v -TLSV1.2
For instance, https://www.comodo.com doesn't allow TLSV1.0 or TLSV1.1 but does allow TLSV1.2.

WCF Streaming with authentication

I have a WCF service which must be secured with Windows and Certificate authentication.
I know that wsHttpBinding does no support Streaming but according to msdn it can be done using transport security with a customBinding but I am not able to do it...
<customBinding>
<binding name="AuthBinding">
<mtomMessageEncoding maxBufferSize="65535" />
<windowsStreamSecurity protectionLevel="Sign"/>
<httpTransport transferMode="Streamed" maxReceivedMessageSize="2147483647" />
</binding>
</customBinding>
I am going from one exception to another :
The security capabilities of binding 'System.ServiceModel.Channels.CustomBinding' do not match those of the generated runtime object. Most likely this means the binding contains a StreamSecurityBindingElement, but lacks a TransportBindingElement that supports Stream Security
Or
The request message must be protected. This is required by an operation of the contract ('IEntity','WebService.Entity'). The protection must be provided by the binding ('CustomBinding','WebService.Entity')
Rather than being done in a single step, this is two step process.
Enable Transport Security with Certificate Authentication.
Enable Transport Security with Windows Authentication.
Also I am not sure both works together.
But MSDN Article found seems to direct in a correct direction:
How to: Secure a Service with Windows Credentials with wsHttpBinding
Please refer these links from MSDN for further learning (not much help if you are in hurry to deliver some milestone):
Transport Security with Windows Authentication
Transport Security with Certificate Authentication
Update
You have to use custom binding having Mixed (transport with message credentials) security mode.
Three binding elements relate to message level security, all of which derive from the SecurityBindingElement class.
The three are TransportSecurityBindingElement, SymmetricSecurityBindingElement, and AsymmetricSecurityBindingElement. The TransportSecurityBindingElement is used to provide Mixed mode security. The other two elements are used when the message layer provides security.
Additional classes are used when transport level security is provided:
HttpsTransportBindingElement
SslStreamSecurityBindingElement
WindowsStreamSecurityBindingElement
You can get help on:
How to: Create a Custom Binding Using the SecurityBindingElement
and
Security Capabilities with Custom Bindings
This works for me:
<basicHttpBinding>
<binding name="basic" transferMode="Streamed" messageEncoding="Mtom">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>

Kerberos Over HTTPS

I have a WCF service hosted by a Windows Application of my own. I want the service to authenticate the client at the transport level using the client's Certificate and when the client communicates with the service, it must pass its Kerberos Ticket of the client's user account. Looking around on the internet i found this configuration for the service:
<bindings>
<customBinding>
<binding name="Kerberos (MsgHeader) over Transport (Certificate)">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="KerberosOverTransport">
<secureConversationBootstrap />
</security>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
They say that this configuration worked with Microsoft's help. Now, i tried to duplicate this configuration through code, so i used the following snippet:
BindingElementCollection elementCollection = new BindingElementCollection();
elementCollection.Add(SecurityBindingElement.CreateKerberosOverTransportBindingElement());
elementCollection.Add(new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.Soap11 });
elementCollection.Add(new HttpsTransportBindingElement() { RequireClientCertificate = true });
return new CustomBinding(elementCollection);
I configured the endpoint with this binding along with a valid certificate bound to the port. I then added a service reference at the client project in Visual Studio.
To this end, everthing is working great! I also configured the client to send its certificate when communicating with the service, this also worked fine. However, when I try to invoke any method of the service, I get the following error:
An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.
And when I examined the inner exception, I found the following error:
An error occurred when processing the security tokens in the message.
Any help, please? Do i need to configure the client further to send its Kerberos token, or what?
P.S. The service and client machine clocks are synchronized. In addition, the service and the client are on two different machines joined to a domain.

Using WCF to consume service and receieving varying errors depending on binding configuration

I am attempting to consume an intranet web service with WCF. I added reference to the service via the Add Service Reference feature in VS2008. In doing so I was prompted for network credentials to access the service which I provided and the service reference was added.
I then wrote some code that I would expect to fail as it doesn't pass credentials along with the call of the service:
FooServiceClient proxy = new FooServiceClient();
bool isValid = proxy.ValidateBar(baz);
When I use this code I receieve the exception:The HTTP request is unauthorized with client authentication scheme 'Negotiate'.The authentication header received from the server was 'Basic realm="Kerberos"'.Which is the same error I receieve when using either of the two code examples below.
FooServiceClient proxy = new FooServiceClient();
proxy.ClientCredentials.UserName.UserName = "USERNAME";
proxy.ClientCredentials.UserName.Password = "PASSWORD";
bool isValid = proxy.ValidateBar(baz);
or
FooServiceClient proxy = new FooServiceClient();
NetworkCredential creds = new NetworkCredential("USERNAME", "PASSWORD");
proxy.ClientCredentials.Windows.AllowedImpersonationLevel =
TokenImpersonationLevel.Identification;
proxy.ClientCredentials.Windows.AllowNtlm = false;
proxy.ClientCredentials.Windows.ClientCredential = creds;
bool isValid = proxy.ValidateBar(baz);
My gut tells me that I have the security mode configured incorrectly. According to the server manager the end point that I am attempting to bind to is looking for a Basic Http Credential via SSL. Which after reading about WCF-BasicHttp Transport Properties lead me to believe that I should use this configuration:
<security mode="Transport">
<transport clientCredentialType="Windows" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
Unfortunately, I continued to receive the same error.
Again, I am sure my troubles have to do with a configuration issue on my part as I've previously consumed this service in other projects with the outdated Add Web Reference.
You have to really understand what the endpoint on the other end is configured under. If it is self hosted and running under SSL then it should be Transport, but if its running under IIS with SSL then it could possibly be TransportWithMessageCredentials and the Transport credentials might be "None".
It is very tricky to get this to bind correctly.
As far as the Exception you are getting
The provided URI scheme 'https' is
invalid; expected 'http'. Parameter
name:
When you use TransportCredentialOnly you have to use HTTP binding rather than HTTPS, and I am sure you didn't change your endpoint address to HTTP because that's not what the service reference is.
What binding are you using for your intranet scenario? The recommended best practice would be NetTCP with transport security and Windows credentials (assuming all your callers are intranet-clients with an account in your corporate Active Directory)
That would avoid any of the http/https mess.
However, to host netTcp, you either needs WAS (Windows Process Activation Server) which is part of IIS7 and that only runs on Windows Server 2008 (Vista Server) or 2008 R2 (Win7 Server). Or you need to host your service yourself in a e.g. NT Service.
Lots of information still missing! Please update your question accordingly. Thanks!
The below WCF binding configuration ended up being the solution.
<security mode="Transport">
<transport clientCredentialType="Basic" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>

Categories