Cross domain HttpWebRequest return 403 forbidden - c#

I have 2 sites that sits on 2 domains. I need to send information from one site to the other.
I checked that the asmx is reachable running unit test but when I run my code from the second domain I get a 403 forbidden response.
The code is extremely simple:
try
{
var webRequest = (HttpWebRequest)WebRequest.Create("http://www.blahblahblah.com/Services/ChatNotification.asmx?fromId=" + 525808 + "&toId=" + 525808);
var webResponse = webRequest.GetResponse();
var stream = webResponse.GetResponseStream();
}
catch(WebException ex)
{
var response = (HttpWebResponse)ex.Response;
}

Related

Http.WinHttpException sent on SOAP request .NET CORE 2.2. The message received was unexpected or badly formatted

We have three IHostedService in our .NETCore2.0 webapp performing operations periodically. Two of them are in polling on an external system asking for new data; the third sends to the same external system some data gathered by our webapp. Every request is SOAP and it's done with the following code:
try
{
#region PFC Certificate
// Pfx certificate management
string certPath = GetCertPath();
string certPass = GetCertPassword();
X509Certificate2Collection X509collection = new X509Certificate2Collection();
X509collection.Import(certPath, certPass, X509KeyStorageFlags.PersistKeySet);
#endregion
if (X509collection.Count > 0)
{
X509Certificate2 x509 = X509collection[0];
var request = CreateSOAPWebRequest(url, x509);
byte[] bytes;
bytes = Encoding.ASCII.GetBytes(xmlRequestContent);
request.ContentType = "application/xml; encoding='utf-8'";
request.ContentLength = bytes.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(bytes, 0, bytes.Length);
requestStream.Close();
if (request == null) throw new Exception($"url:{url}: Request NULL - xml: {xmlRequestContent}");
try
{
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
{
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream responseStream = response.GetResponseStream())
{
// Response deserialization
string responseStr = await new StreamReader(responseStream).ReadToEndAsync();
T result = new T();
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringReader reader = new StringReader(responseStr))
{
result = (T)(serializer.Deserialize(reader));
return result;
}
}
}
}
}
catch (WebException ex)
{
_logger.LogError(ex);
throw;
}
}
return default(T);
}
catch(Exception ex)
{
_logger.LogError(ex);
throw;
}
The CreateSOAPWebRequest method is defined as:
private HttpWebRequest CreateSOAPWebRequest(string url, X509Certificate certificate)
{
Uri uri = new Uri(url);
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(uri);
webRequest.Proxy = null;
webRequest.Headers.Add("SOAP:Action");
webRequest.KeepAlive = true;
webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.Accept = "text/xml";
webRequest.Method = "POST";
webRequest.AuthenticationLevel = AuthenticationLevel.MutualAuthRequired;
if (certificate != null)
webRequest.ClientCertificates.Add(certificate);
return webRequest;
}
The first two hosted services worked very well together for years since the third cames in: some requests go OK in the beginning, then this exception is thrown and no one of the services is able to send the SOAP request anymore (until we restart the webapp):
The SSL connection could not be established, see inner exception. Authentication failed, see inner exception.
---> The SSL connection could not be established, see inner exception.
---> Authentication failed, see inner exception.
---> The message received was unexpected or badly formatted
This is thrown on the line
HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync()
This seems to be a certificate/security/SSL problem. But the requests work well in the beginning and/or without the third hosted service, so we thought it could be a syncronization problem between the services and we detached the third one by running it on a separate cloned webapp, alone, but we got the same error on the second SOAP call anyway (while the first worked).
We were able to reproduce this error in debug only by disabling the service in the production environment and running the webapp locally in debug mode, reading and sending production data.
We have no idea on what is causing this, so thank you in advance for every advice.
I finally figured it out. The whole thing was a bit misleading while the exception was telling the truth: at some point in our code, after the SOAP request, the flow COULD go through this:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
So everything was fine until this instruction and of course everything was broken after this instruction.

Ways to structure our HTTP Post method so it doesn't get blocked by firewalls

An issue we've faced since the beta launch of our software is that not all of our users are able to be authenticated (we authenticate using web requests) as a result of something happening. We're not sure exactly what is causing it, but we're slightly confident that it's caused by our POST requests. Below is the C# method we use to do the request.
public static string getResponse(string url, string postdata)
{
try
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] byte1 = encoding.GetBytes(postdata);
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
myHttpWebRequest.Method = "POST";
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.ContentLength = byte1.Length;
Stream newstream = myHttpWebRequest.GetRequestStream();
newstream.Write(byte1, 0, byte1.Length);
WebResponse response = myHttpWebRequest.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
return reader.ReadToEnd();
}
catch
{
return "";
}
}
Perhaps we haven't structured it in a way that all firewalls will accept it, for example.
With that said, we do not know if it's the issue of the port, the request itself, or the user's issue. Has anyone ever faced this issue before? What did you do to fix it?
Are there "standards" to how you structure your POST so most devices/firewalls will accept it? Are we using the right port?
I have updated my catch statement to catch Web Exceptions:
catch (WebException ex)
{
ex.writeToDebuggerOrTxtFile;
}
Since at the moment we are not entirely sure about what is causing the issue, that is, we don't know if it's the user, firewall, or ports, that's causing the user being unable to authenticate, we have to first isolate the issue. If the status codes return something like HTTP 500, 401, 403, etc, then this indicates that the request is failing on the server.
EDIT: Upon sleeping over this, I saw a bit of an issue with this answer.
Let me explain.
Generally, all HTTP responses should succeed, even if the status code may return a code that indicates an error. I think the right way to approach this is to simply not just look at the WebExceptions thrown (since most "failed" responses should return a success), but to also look at purely the status codes.
A failed response would still succeed in bringing a response headers back, but the right way to approach it, I think, is to look at the status codes themselves.
Here's a node.js script I whipped up to check HTTP response headers:
var http = require("http");
var fs = require("fs");
var i = 0;
var hostNames = ['www.google.com'];
for (i; i < hostNames.length; i++){
var options = {
host: hostNames[i],
path: '/'
};
(function (i){
http.get(options, function(res) {
var obj = {};
obj.url = hostNames[i];
obj.statusCode = res.statusCode;
obj.headers = res.headers;
for(var item in res.headers){
obj.headers[item.replace(/\./,'\\')] = res.headers[item];
}
console.log(JSON.stringify(obj, null, 4));
}).on('error',function(e){
console.log("Error: " + hostNames[i] + "\n" + e.stack + "\n");
});
})(i);
};

About: get REST - returning a NotFound exception C#

Attempting to retrieve quota information from Google Drive via REST in C#. Here is a code snippet:
public async Task<string> HasQuota(string accessToken) {
var url = string.Concat(new string[] {
" https://www.googleapis.com/drive/v2/about",
"?access_token=" + accessToken,
"&fields=quotaBytesTotal%2CquotaBytesUsed"
});
// Create the request.
var request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";
// Create the response.
var response = await Task<WebResponse>.Factory.FromAsync(
request.BeginGetResponse,
request.EndGetResponse,
request
);
if (((HttpWebResponse)response).StatusDescription == "OK") {
using (Stream stream = response.GetResponseStream()) {
StreamReader reader = new StreamReader(stream, Encoding.UTF8);
return reader.ReadToEnd();
}
}
return null;
}
When I obtained the access_token I scoped the authentication request with https://www.googleapis.com/auth/drive.file and my test user granted permissions. So as far as I aware I have the code requirements to execute this call.
However I get an NotFound exception when I attempt to execute this method. The documentation says I can pass the access_token in the query string or add the Authorization header to the request i.e
request.Headers["Authorization"] = string.Format("Bearer {0}", accessToken);
Any ideas why I might be getting this exception?
Thanks
I hadn't enabled the Drive API option in the console...

C# - HttpWebResponse redirect checker

I'm trying to code a redirect checker, the solution I have was just banged together this morning so it's not the most efficient but it does everything I need it to do apart from one thing:
It only ever checks two sites before stopping, no errors occur, it just stops on the "request.GetResponse() as HttpWebResponse;" line for the third page.
I've tried using different sites and changing the combination of pages to check but it only ever checks two.
Any ideas?
string URLs = "/htmldom/default.asp/htmldom/dom_intro.asp/htmldom/dom_examples2.asp/xpath/default.asp";
string sURL = "http://www.w3schools.com/";
string[] u = Regex.Split(URLs, ".asp");
foreach (String site in u)
{
String superURL = sURL + site + ".asp";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(superURL);
request.Method = "HEAD";
request.AllowAutoRedirect = false;
var response = request.GetResponse() as HttpWebResponse;
String a = response.GetResponseHeader("Location");
Console.WriteLine("Site: " + site + "\nResponse Type: " + response.StatusCode + "\nRedirect page" + a + "\n\n");
}
Aside from the fact that it will break if a WebException is ever thrown, I believe the reason it's just stopping is that you never dispose of your response. If you have multiple URLs served actually by the same site, they'll use a connection pool - and by not disposing of the response, you're not releasing the connection. You should use:
using (var response = request.GetResponse())
{
var httpResponse = (HttpWebResponse) response;
// Use httpResponse here
}
Note that I'm casting here instead of using as - if the response isn't an HttpWebResponse, an InvalidCastException on that line is more informative than a NullReferenceException on the next line...

Getting connection closed error when trying to read response from FitNesse REST URI

Have you run into this problem? I run code remarkably similar to that from a this previous question, When in nUnitTest mode and the URI includes "/?test&format=xml" the nUnit test fails with and IOException, "Unable to read data from the transport connection: The connection is closed."
However the Fiddler trace that was running at the time shows the very xml I expected.
I've recreated the request headers exactly (almost) as they are sent when sent through the browser.
Finally, if I leave off the "/?test&format=xml" from the URI, I get the html I would have otherwise expected.
SOURCE CODE:
public virtual bool Run()
{
var request = CreateRequest();
var response = GetResponse(request);
var responseString = ReadResponse(response);
this.SetResults(responseString);
return this.IsSuccessful;
}
protected internal virtual HttpWebRequest CreateRequest()
{
var address = TestConfig.Address;
var request = (HttpWebRequest)WebRequest.Create(address);
request.Accept = "*/*";
request.UseDefaultCredentials = true;
request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
return request;
}
protected internal virtual HttpWebResponse GetResponse(HttpWebRequest request)
{
var response = (HttpWebResponse) request.GetResponse();
return response;
}
protected internal virtual string ReadResponse(HttpWebResponse response)
{
var stream = response.GetResponseStream();
var responseString = ReadResponse(stream);
stream.Close();
response.Close();
return responseString;
}
protected internal virtual string ReadResponse(Stream stream)
{
var reader = new StreamReader(stream);
var responseString = reader.ReadToEnd();
return responseString;
}
The error message "Unable to read data from the transport connection: The connection is closed." doesn't really tie up with the fact you're seeing Fiddler getting a html response body back.
Check the StatusCode of the HttpWebResponse (should be 200 if ok), also wrap the request in try/catch block (example from http://msdn.microsoft.com/en-us/library/system.net.httpwebresponse.statuscode(v=vs.80).aspx)
try
{
// Creates an HttpWebRequest for the specified URL.
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
// Sends the HttpWebRequest and waits for a response.
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
if (myHttpWebResponse.StatusCode == HttpStatusCode.OK)
Console.WriteLine("\r\nResponse Status Code is OK and StatusDescription is: {0}",
myHttpWebResponse.StatusDescription);
// Releases the resources of the response.
myHttpWebResponse.Close();
}
catch(WebException e)
{
Console.WriteLine("\r\nWebException Raised. The following error occured : {0}",e.Status);
}
catch(Exception e)
{
Console.WriteLine("\nThe following Exception was raised : {0}",e.Message);
}
If you're creating and disposing of the HttpWebRequest object quickly you might be getting the socket going into a time_wait state as it is shutting down, then you can't re-open it again until it has completely closed. If this is the case, look into using another port or changing the time the connection lives for.

Categories