Validate SharePoint REQUESTDIGEST for a GET request? - c#

I have a WCF REST service hosted in SharePoint that uses SSOM. The client web application sends AJAX requests containing the REQUESTDIGEST in a X-RequestDigest header. I am using the typical SPWeb.ValidateFormDigest() for my POST requests to prevent CSRF vulnerabilities. Works great.
Here's the weird part. Our client uses HP Fortify, and it is reporting that our GET requests are vulnerable to CSRF attacks. My GET requests are indempotent so this seems silly, but I must be compliant.
To get around it, I want to use ValidateFormDigest() in my WCF method for the GET request the same way I do for POST, but it throws this exception:
"Updates are currently disallowed on GET requests. To allow updates on a GET, set the 'AllowUnsafeUpdates' property on SPWeb."
I tried setting AllowUnsafeUpdates, but that just makes the request succeed without validating the digest!
Is there a way to have SPWeb validate the digest within a GET request?

HP Fortify gives recommendations, not edicts. If you think - and can prove - that the warning is spurious, then justifying the pattern should be treated as being compliant. Code analysis tools are not perfect.

Related

Servce Fabric Reverse Proxy Integration in ASP.NET Core 404 Response

I'm working on an implementation of ICommunicationClient and accompanying stuff for HTTP protocol communication which should be compatible with SF reverse proxy. The most subtle part for me is retry policy. According to Azure docs for 404 errors reverse proxy relies on X-Service-Fabric header to be returned from web service when deciding if it should retry.
ASP.NET Core provides middleware for integration with a reverse proxy which adds X-Service-Fabric header to every 404 response.
Assume we have the scenario when ServicePartitionClient cached the endpoint for a stateless service listening on port 3001. At some point, this service may be moved to another node. On the first node, the Service Fabric runtime allocates a different service with its own endpoints but using the same middleware and listening on the same 3001 port.
When a client tries to call the original service at its old (cached) address it will receive 404 response containing the X-Service-Fabric header. According to reverse proxy policies it shouldn't retry, but for me, it seems like the client will stay connected to the wrong service forever and won't attempt to re-resolve the endpoint.
I can't find any information about this case in the documentation, did I miss something here? Is it safe to rely on this standard middleware and don't do retry attempts on 404 errors with X-Service-Fabric: ResourceNotFound header?
In the described case the communication client will be invalidated by staying connected to the wrong service. It is recommended by Microsoft to use unique URL prefixes for services with dynamically-assigned port to handle those scenarios.
In ASP.NET Core programmers can leverage ServiceFabricMiddleware which checks URL prefixes and returns 410 Gone if they don't match. Then HTTP implementation of ICommunicationClient can retry with re-resolving endpoint only for 410 responses and don't perform any retries on 404 response with X-Service-Fabric: ResourceNotFound header if reverse proxy integration is enabled.
In your given scenario, when your Client encounters a 404, the X-Service-Fabric:ResourceNotFound header isn't the only attribute your code could examine when deciding whether or not to retry some operation.
To simplistically address your concern that your client won't be able to tell the difference between a "friendly" node and a "newly arrived" node, and since you're already working with http headers, you could add a custom HTTP header to outgoing responses to identify that a request comes from your application.
When the client receives a 404, you can simply check for the presence of your custom header to answer the question of whether or not it's a "legit" retry. Of course, adding a custom HTTP header just for the sake of this validation check may be more of a global solution to a local problem. Ed: goes without saying that this should not be used to make security decisions by the application
A more elegant and complex means of accomplishing the same would be to deduce the same result using a different combination of HTTP headers and response attributes (e.g., see if some other headers are expected/unexpected), but this could also be a hyper-local solution to the problem.

Getting a "Unexpected character '<' at line 1, column 0." while trying to connect to Google using GoogleApis library

I'm using Google Apis, instead of Google Sign In, to connect to Google on my app because I'm developping with Xamarin.
This is the library I'm using : https://github.com/xamarin/google-apis
When I'm logging in, i get this error :
Authentication Error
Unexpected character '<'. At line 1, column 0.
Maybe it's because my AccessTokenUrl is not good, but I've tried many things. I know for a fact that my ClientId, my RedirectUrl and my Secret are okay.
When logging in, Google asks correctly for the good permissions that I want, but after I accept, this is when I receive the error.
I've tried finding the request to see if there was the '<' in it but had no luck accessing it.
Is there a good way to connect to Google with Xamarin using this library or I'm just doing something bad?
Should I just do it nativaly on iOS and Android?
Thanks
I just encountered a similar problem using Xamarin.Auth to hit a custom OAuth service (i.e. not Google). In my case, the accessTokenUrl pointed to an action on a controller that was entirely protected by the [Authorize] attribute. Naturally, the user was required to login before hitting the /oauth/authorize endpoint using a web browser, but the request to /oauth/token to exchange the resulting authorization code for an access token was not inside the same web browser/session. It was trying to get back token data in JSON format but was being redirected to an HTML login screen. Once I changed the token endpoint for anonymous access, things started working (Note: A valid authorization code cannot be obtained without authenticating).
General Recommendations
The error message strongly suggests that the response coming back is HTML (or at least some form of XML). This could be an authentication redirect as it was in my scenario, or possibly some sort of error page. I would first start by setting up a proxy. I used Charles Proxy to uncover some interesting information. You will need to configure SSL on the proxy to see anything except high level information. This will show the exact requests coming out of your app to the OAuth application.
Another technique I used was simulating the requests that the OAuth2Authenticator would be making in a web browser and/or Postman. The first request would be to authorize:
https://your.domain.here/oauth/authorize?client_id=<some_client_id>&redirect_uri=https%3A%2F%2Fyour.domain.here%2F&response_type=code&scope=<some_scope>&state=<some_state>
That endpoint should be protected, so you will likely be redirected to something like this:
https://your.domain.here/Account/Login?returnurl=%2Foauth%2Fauthorize%3Fclient_id%3D<some_client_id>%26redirect_uri%3Dhttp%253A%252F%252Fyour.domain.here%252F%26response_type%3Dcode%26scope%3D<some_scope>%26state%3D<some_state>
After authenticating, the authorize endpoint should redirect to your redirect URI with the authorization code and state included as query string parameters. You will use the code in the next step.
Lastly, using a fresh web browser (i.e. new session), you should hit the token endpoint with your new authorization code and other client information.
https://your.domain.here/oauth/token?client_id=<some_client_id>&client_secret=<some_secret>&grant_type=<your_grant_type>&code=<your_authorization_code>&redirect_uri=https%3A%2F%2Fyour.domain.here%2F
If the response is not JSON data, it should give you an indication about what is failing with Xamarin.
Got the same error.
Solved by using https://accounts.google.com/o/oauth2/token as AccessTokenUrl

ADFS call: (411) Length Required

I'm trying to get a SAML token from an ADFS instance. I followed the example found here (http://blogs.msdn.com/b/willpe/archive/2010/10/25/windows-authentication-adfs-and-the-access-control-service.aspx) to configure everything. The ADFS instance is configured by a different party, but setup as found in the article.
At first sight, it all seems to be working ok. But when I go into code and run a sample which should get me a token, I receive a (411) Length Required) response from the server. It doesn't matter much if I switch endpoints, errors stays the same. Seems like the response isn't even getting to the endpoint at all.
The Length Required error seems to have something to do with a missing Content-Length: 0 header line. I checked with Fiddler; it's not there. But the call is generated from within the .NET framework, so there really isn't that much I can do about that.
Any idea why the server might throw this error? Should I try to change the request, or disable the check on the server side? Or an option I haven't thought of perhaps?

Consuming a web service using POST instead of the going the usual WSDL route

This is how I have currently managed to consume a particular Microsoft web service. Notice that it is located on an HTTPS server and that it requires a username, a password, and a .cer file to be installed in the operating system's "root certificate authorities".
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.NegotiateServiceCredential = true;
binding.Security.Message.AlgorithmSuite
= System.ServiceModel.Security.SecurityAlgorithmSuite.Default;
binding.Security.Message.EstablishSecurityContext = true;
EndpointAddress endpoint = new EndpointAddress("https://address.of.service");
//"GreatClient" was created for me automatically by running
//"svcutil.exe https://address.of.service?wsdl"
GreatClient client = new GreatClient(binding, endpoint);
//Username and password for the authentication. Notice that I have also installed
//the required .cer certificate into the system's "root certificate authorities".
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
//Now I can start using the client as I wish.
My question is this: How can I obtain all the information necessary so that I can consume the web service with a direct POST to https://address.of.service, and how do I actually perform the POST with C#? I only want to use POST, where I can supply raw XML data using POST directly to https://address.of.service and get back the result as raw XML data. The question is, what is that raw XML data and how exactly should I send it using POST?
(The purpose of this question: The reason I ask is that I wish to consume this service using something other than C# and .NET (such as Ruby, or Cocoa on Mac OS X). I have no way of knowing how on earth to do that, since I don't have any easy-to-use "svcutil.exe" on other platforms to generate the required code for me. This is why I figured that just being able to consume the service using regular POST would allow me to more easily to consume the service on other platforms.)
What you are attempting to do sounds painful to do now and painful to maintain going forwards if anything changes in the server. It's really re-inventing the wheel.
If you haven't considered it already, I would:
(a) Research whether you can use the metadata you have for the service and use a proxy generator native to your target plaform. There aren't many platforms that don't have at least some tooling that might get you part of the way if not all of it. Perhaps repost a question targetting Ruby folk asking what frameworks exist to consume an HTTPS service given it's WSDL?
(b) Failing that, if your scenario allows it I would consider using a proxy written in C# that acts as a facade for the service which translates it into something easier to consume (for example, you might use something like ASP.NET MVC WebAPI which is flexible and can easily serve up standards compliant responses over which you can maintain total control).
I suspect one of these may prove easier and more valuable than the road you are on at the moment.
I had to go through something similar when porting .NET WCF code to other platforms. The easiest approach I found was to enable message logging on the WCF client. This can be configured to save both envelope and body and once everything is working on the .NET side of the house, you can use the message log to have "known-good" XML request/response to port to other platforms.
I found this approach to be more elegant since I didn't have to add an additional behavior to log messages, and it can be easily enabled/disabled/tweaked in the config. The Service Trace Viewer Tool that ships with Visual Studio is also handy for reviewing the log files.
I think when you say that the service should be consumed from other platforms, which do not have proxy class generation logic, you can go with REST services. This will allow you to create input as simple string concatenation instead of complex XML. Though its applicability depends on the situation.
Check this discussion : http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/6907d765-7d4c-48e8-9e29-3ac5b4b9c405/
As far as the certificate is concerned, refer http://msdn.microsoft.com/en-us/library/ms733791.aspx on how to configure it.
I know this is not a very precise answer, but you will be the best person to evaluate above procedure, hence posted. Hope it helps.
What I'll do:
1- Create a small c# app that can post on this webservice (using svcutil). And modify it to show the XML send/received. To view the xml there are several ways: logging, wireshark etc. To add it directly to the small app there is another question here that give a good answer.
2- Once you know what you have to send, you can do it in c# like this:
// implement GetXmlString() to return the XML to post
string xml = GetXmlString();
// create the url
string url = new UriBuilder("http","address.of.service",80).ToString();
// create a client object
using(System.Net.WebClient client = new System.Net.WebClient()) {
// performs an HTTP POST
client.UploadString(url, xml);
}
I'm not a .NET programmer but I've had to interoperate with a few .NET services and have lots of SOAP/WSDL experience. Sounds like you've captured the XML for your service. The other problem you'll face is authentication. OOTB, .NET web services use NTLM for authentication. Open-source language support for NTLMv2 can be hit and miss (although a quick google search pulled up a few possibilities for ruby), and using NTLM auth over HTTP may be something that you have to wire together yourself. To answer a question above: where are the auth creds? If the service is using NTLM over the wire, authentication is happening at some layer below HTTP. If the service is using NTLM to authenticate HTTP, your NTLM creds are in the HTTP Authorization header. You should be able to tell with wireshark where they are. You'll also probably need a SOAPAction header; this can also be sniffed with wireshark. For the C# client, I'm sure there are docs explaining how to add headers to your request.

Server side redirect truncating request payloads

I'm on IIS 6 and I have an ASP.Net 4.0 site that's a single page to serve as a SOAP reverse proxy. I have to modify the return content in order to delete a trouble node from the response and add a tracking node.
In order to facilitate its function as a reverse proxy for all addresses, I have the 404 on the server set to a custom "URL" of "/default.aspx" (the page for my app)
For requests without a payload, it works perfectly - such as for ?WSDL Urls. It requests the proper URL from the target system, gets the response and sends it back - it's pretty utterly transparent in this regard.
However, when a SOAP request is being made with an input payload, the Request.InputStream in the code is always empty. Empty - with one exception - using SOAPUI, I can override the end point and send the request directly to /default.aspx and it will receive the input payload. Thus, I have determined that the custom 404 handler is - when server-side transferring the request - stripping the payload. I know the payload is being sent - I have even wiresharked it on the server to be sure. But then when I add code to log the contents of Request.InputStream it's blank - even though Request.ContentLength shows the right content length for the original request.
I've also been looking for a good way to use ASP.Net to intercept the requests directly rather than allowing the normal IIS 404 handler to take care of it but even with a wildcard mapping, I can't seem to get the settings right nor am I fully confident that it would help. (But I'm hoping it would?)
Finally, I don't have corporate permission to install MVC framework.
Thus, I need either some configuration for IIS I am missing to make this work properly or some other method of ensuring that I get the request payload to my web page.
Thanks!
What about using an HTTP Handler mapped to all requests?
You'll need to add a wildcard application mapping as detailed here and correctly configure your HTTP Handler.

Categories

Resources