WCF REST client with TransferMode=Streamed - logging entire ("raw") http requests/responses - c#

This is on .Net 4, full framework.
I'm trying to make a simple winforms app that will make some simple WCF REST calls. It's using ChannelFactory and the service contract interface. Of the ~20 methods in the interface, 2 of them involve Stream (an upload and a download method) so the service side (and currently also the client side) using TransferMode=Streamed.
My goal is to include the full HTTP request and response (much like you would see in ethereal/wireshark, or fiddler, or whatever), with headers, in a textbox of the winforms app (just to show what went over the wire)
In trying to use the built-in diagnostics (via SvcConfigEditor) and my own (via implementing IClientMessageInspector and then IEndpointBehavior to add the inspector, then channelFactory.Endpoint.Behaviors.Add to add the behavior :), I'm having 2 issues:
When doing request.ToString() or reply.ToString() in BeforeSendRequest and AfterReceiveReply, it only gets the 'body' and not the headers. Digging around in the objects in the debugger it looks like the reply has them in reply.Properties["httpResponse"], but the request's request.Properties["httpRequest"] has an empty Headers property even though Fiddler shows headers for Content-Type, Host, Accept-Encoding, and Connection. It seems like there's likely a better way to get the 'raw' message that I'm missing (and if there's not, someone probably knows an existing chunk of code to 'reconstruct' the raw one from the Message)
Since the transfer mode is Streamed, the 'body' part just shows up as the string '... stream ...', both in SvcTraceViewer (and the 'raw' svclog - even with logEntireMessage=true) and when doing a ToString(). If the mode is Buffered instead, it shows the actual body fine. I tried making a copy with reply.CreateBufferedCopy(int.MaxValue); but that then caused the actual WCF call to fail with an InvalidOperationException: This message cannot support the operation because it has been copied.
One fallback would be to move the client to Buffered and just change to StreamedRequest for the one upload call and StreamedResponse for the download call (but I'd have to do that programmatically AFAICT, as it's set at the binding level in the config and I don't see anyway of doing it via attributes on the calls), which would take care of the 'body' part and leave me with just the "get the http request headers" (issue #1, specifically request.Properties["httpRequest"].Headers being empty) to deal with, but I'm hoping there's some way of logging the 'raw' messages without doing so, leaving the TransferMode as Streamed.
Thanks!

I can't find any reference right now, but it's a known fact that you cannot capture the contents of a streamed message to WCF tracing. When streaming is enabled, only the headers of the message will be traced.
Here's the source: Configuring Message Logging on MSDN
See towards the end of the page:
Service Level
Messages logged at this layer are
about to enter (on receiving) or leave
(on sending) user code. If filters
have been defined, only messages that
match the filters are logged.
Otherwise, all messages at the service
level are logged. Infrastructure
messages (transactions, peer channel,
and security) are also logged at this
level, except for Reliable Messaging
messages. On streamed messages, only
the headers are logged. In addition,
secure messages are logged decrypted
at this level.

Related

Why bother inspecting an uploaded file's content-type if the sender can lie about what it is?

I'm writing an API that is hidden behind Azure's API Management tool. It can't be accessed unless APIM verifies the requester's access token as well as Azure subscription. In the API, we want to verify with the requester that we will only accept zip files.
if (request.PayloadFile.ContentType != "application/zip")
{
throw new BadRequestException("Unable to accept payload content type");
}
This is not a process attempting to mitigate a bad actor. The purpose is to simply verify that the developer is sending the expected data type. With that said, I made a small client that sends a non-zip format file and I simply set the content-type header in the request:
var fileContent = new ByteArrayContent(await File.ReadAllBytesAsync(filePath));
fileContent.Headers.Remove("Content-Type");
fileContent.Headers.Add("Content-Type", "application/zip");
fileAsFormData.Add(fileContent, "payLoad", fileName: Path.GetFileName(filePath));
The API happily accepts this. So my question is twofold:
Why would we even bother checking ContentType if it can be spoofed?
Is there a way to get the true file type of the payload?
Content-Type is a way for a client to indicate for the server what content you wish to send. Of course you might maliciously "lie" about it, but it would be against your own interest as a client. Servers might implement appropriate logics to parse file content, depending on the Content-Type header.
There are several things to consider:
A server is expected to work properly, only if the client sends valid data: i.e.: clients that want the server behave properly should not lie about the real Content-Type of uploaded files.
It is easier for servers to check a Content-Type header, compared to specifying file types using other methods.
Servers should also validate data and mitigate risks in case of possible misuse of an API. For example, in your case, you should add try-catch around unzipping content, and respond with error if unzipping was not successful.
For your other question, whether real Content-Types can be checked, the answer is generally: no. There are some file types, where the file itself contains some kind of "magic string" that can be used as a signiture for that Content-Type.
You can check some of these common file signitures: https://en.wikipedia.org/wiki/List_of_file_signatures
Please note that, a malicious user might be able to modify file signatures as well. There can be additional security measures to defend against spoofing file contents, e.g.: checksums, but there is no 100% security, unless some private secret is shared among servers and trusted clients, which can be used to securely sign contents.
The real question here is: why would anyone maliciously spoof Content-Types? Or file contents? And what level of security measure is needed to avoid associated risks?

.NET SOAP communication with separate certificates for signing and encryption

I try to configure client communicating with SOAP service written in Java, yet I failed so far. While it's pretty easy to connect with that service using SoapUI (I just need to put these two certificates in adequate places and it just works) it's pretty far from being easy (and intuitive) in C#.
Service uses two different certificates - one for signing message (binary token) and one for TLS encryption. I implemented my own classes for that thing using this tutorial:
https://learn.microsoft.com/pl-pl/dotnet/framework/wcf/extending/how-to-use-separate-x-509-certificates-for-signing-and-encryption
Thanks to that I'm able to connect and send request, but that's not the end of the problems. Next error was "The incoming message was signed with a token which was different from what used to encrypt the body. This was not expected."
According to my research I should just erase whole Security header and configure my client to allow unsecured response. Therefore I should implement my own MessageEncoder what I actually did using this:
https://learn.microsoft.com/pl-pl/dotnet/framework/wcf/samples/custom-message-encoder-custom-text-encoder
I only added one extra function that should erase security header, just like #nuronce did in this thread:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/16de05ed-3776-40e5-b576-139603e4b374/the-incoming-message-was-signed-with-a-token-which-was-different-from-what-used-to-encrypt-the-body?forum=wcf
But that's still not the end of the problems... Right now it says: "The message version of the outgoing message (Soap11 (http://schemas.xmlsoap.org/soap/envelope/) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)) does not match that of the encoder (Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)). Make sure the binding is configured with the same version as the message."
That's weird because in every place with MessageVersion property in my custom encoder classes it's set to MessageVersion.Soap11 as default value that should not be modified, because I don't change anything in my CustomTextMessageEncodingBindingSection.
For every solution WCF gives me another problem and I'm losing my patience for that, because these solutions are not so easy to find and adjust to my needs. I'm also pretty sure that even if I go through my current problem - I will find another one, maybe harder or even impossible to resolve.
Are there other .NET libraries that will allow me to use two different certificates by default and save my code from growing, hard to understand convolutions? And if there is none, how can I deal with WCF?

Client HTTP Handling & ETag

I'm working on a small SilverLight application, which uses the WebClient to request a REST service. According to this:
http://msdn.microsoft.com/en-us/library/dd920295(v=vs.95).aspx
no caching should be in place when using the 'Client HTTP' handling....
The REST service I'm calling use ETag, so with no Caching in place, I would not expect, that a second call to my service, would contain the If-None-Match header ??
Also when the REST service returns '304' (NotModified) I still get some data, indicating that the data is returned from the cache....
The REST service also sets the 'Vary' header to 'Accept' (so that the Accept header, should be taken into account when generating the Cache key), but is doesn't seem like this is working....
So I'm a bit confised, whether the caching is there or not.... seems like it's half-ways there...
Can someone clearify??
TIA
Søren
I would suggest not using ETags on a WCF service. In IIS, the service or the directory containing the service needs to include an HTTP response header setting the content to expire immediately so that there is no possibility of caching on the client. This way a call to the service will not result in an HTTP 304 response from the server.

How to return both stream and file length from WCF Restful json webservice?

Hihi all,
I am able to return stream from my WCF restful json webservice, everything works fine. But when I mixed the stream with another piece of data (both wrap into a custom class), upon consuming the webservice from my client, it gives an error message of "An existing connection was forcibly closed by the remote host".
Any advice how can I achieve the above? What it's required for my webservice is to allow downloading of a file with the file length as an additional piece of information for validation at the client end.
Thanks in advance! :)
There are various restrictions while using Stream in WCF service contracts - as per this MDSN link, only one (output) parameter or return value (of type stream) can be used while streaming.
In another MSDN documentation (this is anyway a good resource, if you want to stream large data using WCF), it has been hinted that one can combine stream and some input/output data by using Message Contract.
For example, see this blog post where author has used explicit message contract to upload both file name & file data. You have to do the similar thing from download perspective.
Finally, if nothing works then you can always push the file length as a custom (or standard such as content-length) HTTP header. If you are hosting in IIS then enable ASP.NET compatibility and use HttpContext.Current.Response to add your custom header.

Getting a raw socket from an IIS request

I'm trying to get the raw data sent to IIS using a HttpHandler. However, because the request is an "GET"-request without the "Content-Length" header set it reports that there is no data to read (TotalBytes), and the inputstream is empty. Is there any way I can plug into the IIS-pipeline (maybe even before the request is parsed) and just kind of take control over the request and read it's raw data? I don't care if I need to parse headers and stuff like that myself, I just want to get my hands on the actual request and tell IIS to ignore this one. Is that at all possible? Cause right now it looks like I need to do the alternative, which is developing a custom standalone server, and I really don't want to do that.
Most web servers will ignore (and rarely give you access to) the body of a GET request, because the HTTP semantics imply that it is to be ignored anyway. You should consider another method (for example POST or PUT).
See this question and the link in this answer:
HTTP GET with request body

Categories