How does SoapUI bypass CORS issue when my ajax call can't do it? - asp.net

This might be a silly question but here is my simple webapi 2 method
public class ProductsController : ApiController
{
Product[] products = new Product[]
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
Now if I run it , on my pc it runs on http://localhost:3145/Products, and I can see the products as XML
It also works using soapui
But now if I try to access this with a html file and this javascript
<script type="text/javascript">
function GetProducts() {
$.ajax({
url: "http://localhost:3145/Products",
dataType: "json",
success: function (data) {
for (var i = 0; i < data.length; i++)
$('#myDiv').append(data[i].Category).append("<br/>");
},
error: function (xhr, status) {
alert(xhr);
}
});
}
</script>
</head>
<body onload="GetProducts()">
<h1>My App</h1>
<div id="myDiv"></div>
I get the CORS error
How is SOAP UI not getting an error here when it is using http as well?
Thanks

Put that index file into some server location, and then browse the page with server url , like, http://localhost/virtual_dir/index.html , else it will say it file:/// domain does not match with http://localhost:port . And you might face CORS issue if you deploy this page to some other domain and start using.
I have seen that you are using webapi , and you might face CORS issue if you place you JS in domain ( "example1.com"), i mean files served from example1.com will have ajax calls to webapi and that webapi may be hosted in example2.com. This will raise CORS issue. Browser restricts ajax call to other domains, unless that domain allow you to invoke. To achieve this, you can follow this link - Angular.js $resource with ASP.Net webapi? (don't go by the title)
I have answered there the same scenario.

I just experienced the same situation with a web page I was developing that needed to send a SOAP request to a web service that was served by a remote server that was not CORS-enabled. This was in a test environment; in production, the web page and web service are served from the same domain and port.
How is SOAP UI not getting an error here when it is using http as well?
Yes, SoapUI and a web browser both use HTTP. But SoapUI is not a web browser, and does not share the same restrictions.
A colleague solved this issue for me by pointing me to CORS Anywhere.
Install Node.js, if you don't already have it.
Install CORS Anywhere (npm install cors-anywhere).
Run CORS Anywhere (node cors-anywhere.js).
Instead of using your original non-CORS-enabled web service URL, use the domain and port in the resulting message from CORS Anywhere ("Running CORS Anywhere on ..."), with your original web service URL as the path component.
You can specify the CORS Anywhere port in an environment variable (or edit the default value in your local copy of cors-anywhere.js).
In my case, the production web service URL is hardcoded in the web page as a relative path. But I've added code that reads a URL from a parameter in the fragment identifier ("hash string"), overriding the default web service URL.
For example:
http://localhost:8081/mywebpage.html#url=http://localhost:8089/remote.domain.com:8085/servicename
where:
http://localhost:8081/mywebpage.html is my web page (I'm using http://, not file://, for this).
http://localhost:8089 is the CORS Anywhere proxy.
remote.domain.com:8085/servicename (you can drop the leading http://) is the remote, non-CORS-enabled web service.

I could point you 2 options to solve this:
Disable your browser CORS: Due the CORS enforcement is done by the browser you can just disable this during development in yourself browser. If you use Chrome, you must just set an parameter otherwise I may guess looking to the underneath image you are using Firefox, for this you have an extension to do this: https://addons.mozilla.org/pt-PT/firefox/addon/cors-everywhere/
Allow CORS in SOAP UI: It can take a little bit more effort than above solution but it fits very good when you need to share with mutiple teammates or just to make the solution attached to the mock service. To do this,you must add a response for the root of resource path that responds to OPTIONS and with headers you need for CORS with status 204 (or 200).
After, in the MockService you just need to add a script to grab these same headers in all calls that passes through.
Here is the article to solve this step-by-step:
https://medium.com/#andrelimamail/how-to-deal-with-cors-in-soap-ui-mock-services-or-anyother-f4cc55b3dccd

Related

ServletUriComponentsBuilder.fromRequest return http scheme instead of https

I have a web application composed by frontend code that invokes the same api implemented by two backend modules. This api returns a url in a JSON object. The backend modules are both written with spring mvc but in different versions.
The url-building is the same and it is something like this:
#GetMapping(path = "/app1/menu", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public JsonObject getMenu(HttpServletRequest req) throws IOException {
JsonObject menu = new JsonObject();
menu.addProperty("href", ServletUriComponentsBuilder.fromRequest(req)
.replacePath(req.getContextPath())
.path("/index.html")
.toUriString());
return menu;
}
As you can see this code simply adds a constant to the incoming request and returns it.
The first app uses spring mvc 4 (4.3.5.RELEASE precisely).
The second module uses the 5.1.4.RELEASE version.
When all these apps are deployed on a load balanced server (2 tomcat instance with a load balancer upfront) and https the problem shows up.
Say that the request url is, for app1, something like this:
https://example.com/context/app1/menu
The app1 returns correctly
https://example.com/context/index.html
For the app2 the request issued by the frontend is
https://example.com/context/app2/menu
And the answer is
http://example.com/context/another_index.html
So it looses the https scheme
It seems that the ServletUriComponentsBuilder.fromRequest has changed behaviour?
I have taken a (quick I admit) look at the commits in the git repo but haven't
found anything ....

The anti-forgery cookie token and form field token do not match when using WebApi

I have a single-page app (user loads a bunch of HTML/JS and then makes AJAX requests without another call to MVC - only via WebAPI). In WebAPI I have the following:
public sealed class WebApiValidateAntiForgeryTokenAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(
System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException(nameof(actionContext));
}
if (actionContext.Request.Method.Method == "POST")
{
string requestUri = actionContext.Request.RequestUri.AbsoluteUri.ToLower();
if (uriExclusions.All(s => !requestUri.Contains(s, StringComparison.OrdinalIgnoreCase))) // place some exclusions here if needed
{
HttpRequestHeaders headers = actionContext.Request.Headers;
CookieState tokenCookie = headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName]) // __RequestVerificationToken
.FirstOrDefault();
string tokenHeader = string.Empty;
if (headers.Contains("X-XSRF-Token"))
{
tokenHeader = headers.GetValues("X-XSRF-Token").FirstOrDefault();
}
AntiForgery.Validate(!string.IsNullOrEmpty(tokenCookie?.Value) ? tokenCookie.Value : null, tokenHeader);
}
}
base.OnActionExecuting(actionContext); // this is where it throws
}
}
Registered in Global.asax:
private static void RegisterWebApiFilters(HttpFilterCollection filters)
{
filters.Add(new WebApiValidateAntiForgeryTokenAttribute());
filters.Add(new AddCustomHeaderFilter());
}
Occasionally, I see the The anti-forgery cookie token and form field token do not match error in my logs. When this is happening, both tokenCookie.value and tokenHeader are not null.
Clientside, all of my AJAX requests use the following:
beforeSend: function (request) {
request.setRequestHeader("X-XSRF-Token", $('input[name="__RequestVerificationToken"]').attr("value"););
},
With Razor generating the token once on my SPA page:
#Html.AntiForgeryToken()
I have my machine key set in Web.config.
What could be causing this?
Update
I just checked logs and I'm seeing this sometimes as well:
The provided anti-forgery token was meant for user "", but the current user is "someuser#domain.com". a few seconds ago
This occurs when a user refreshes their instance of the SPA while logged in. The SPA then drops them into the landing page instead of the inner page for some reason (User.Identity.IsAuthenticated is true) - then they can't log in because of this error. Refreshing pulls them back inside. Not sure what this means, but I figured more info can't hurt.
Appendix
https://security.stackexchange.com/questions/167064/is-csrf-protection-useless-with-ajax/167076#167076
My answer will recommend to not try to use CSRF protections based on tokens in AJAX calls, but rather to rely on the native CORS features of the web browser.
Basically, any AJAX call from the browser to the back-end server will check for the domain origin (aka the domain where the script was loaded from). If the domains match (JS hosting domain == target AJAX server domain) the AJAX calls performs fine, otherwise returns null.
If an attacker tries to host a malicious AJAX query on his own server it will fail if your back-end server has no CORS policy allowing him to do so (which is the case by default).
So, natively, CSRF protections are useless in AJAX calls, and you can lower your technical debt by simply not trying to handle that.
More info on CORS - Mozilla Foundation
Code example - use your console inspector!
<html>
<script>
function reqListener () {
console.log(this.responseText);
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.reuters.com/");
oReq.send();
</script>
</html>
Run it and look at the Security error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://www.reuters.com/. (Reason: CORS header
‘Access-Control-Allow-Origin’ missing).
Mozilla is pretty clear regarding the Cross-site XMLHttpRequest implementation:
Modern browsers support cross-site requests by implementing the Web
Applications (WebApps) Working Group's Access Control for Cross-Site
Requests standard.
As long as the server is configured to allow requests from your web
application's origin, XMLHttpRequest will work. Otherwise, an
INVALID_ACCESS_ERR exception is thrown.
I try to give an answer the same, also if in the comments we exchange, yours it seems a not related scenario with mine..
A such type of issue can be due to the XMLHttpRequest.setRequestHeader() behaviour, because this function "combines" the values of an header that has been already assigned in the context of an http request, as stated by MDN and Whatwg:
If this method is called several times with the same header, the
values are merged into one single request header.
So, if we have a SPA for example that executes all ajax POSTs setting a given http header, in your case:
beforeSend: function (request) {
request.setRequestHeader("X-XSRF-Token", $('input[name="__RequestVerificationToken"]').attr("value"););
}
the first ajax POST request sets a clear header ("X-XSRF-Token") and so, server side, you should have a "valid" header value to compare to.
But, in absence of a page refresh, or a new GET request, all subsequent ajax POSTs, as well as stated in the MDN and Whatwg documentation, will make a dirty assignment of the same header ("X-XSRF-Token"), because they combine the new values with the olds.
To avoid this issue, you could try to reset "X-XSRF-Token" value (but there isn't much documentation on that and it seems a not reliable solution...)
beforeSend: function (request) {
request.setRequestHeader("X-XSRF-Token", null); //depends on user agents..
//OR.. request.setRequestHeader("X-XSRF-Token", ''); //other user agents..
//OR.. request.setRequestHeader("X-XSRF-Token"); //other user agents..
request.setRequestHeader("X-XSRF-Token", $('input[name="__RequestVerificationToken"]').attr("value"););
}
Other solutions can rely on some client-side state handing mechanism that you have to implement on your own, because it is not possible to get values or state access of the http request headers (only response headers can be accessed).
Update - revision of the following text:
So, if we have a SPA for example that executes all ajax POSTs recycling the XMLHttpRequest object for each calling and setting a given http header, in your case:
...

Override OPTIONS response in NancyFX / Visual Studio Web Server

I've build a RESTful web service using NancyFX which I'm now trying to POST to from a separate domain. Of course when I do that, I see a failed OPTIONS message in the console because this is a Cross Site POST and I need to ensure that Nancy responds correctly to the OPTIONS message being sent by the browser. However, when I define a route in my Nancy module:
this.Options["/options/"] = _ => this.OptionsRequest();
private dynamic OptionsRequest()
{
return this.Response.AsJson(Request)
.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type");
}
The code never gets hit. I can set a breakpoint on the OptionsRequest() method in debugger and see that the code is never getting hit. However I can issue an OPTIONS request in postman, and the server returns a response (interestingly, it seems to return a response from all URIs, not just the /options/ route I've defined).
Is there a default Nancy OPTIONS behaviour I have to override in order to specify routes for OPTIONS, or is this something to do with the service being hosted in the Visual Studio Development Web Server (Casini)? I've tried everything I can think of and I'm still stumped as to why I can't define behaviour for this particular verb.
Turns out this was a bug in Nancy v0.17.1 (see https://github.com/NancyFx/Nancy/pull/1093). Upgrading to 0.18.0 fixed the issue and allowed my OPTIONS route to work correctly.

Jquery.Get ashx page

JS method
$.post('http://localhost:21067/HandlerServices/Product/ProductHandler.ashx', 'action=productlist', function (data) { console.log(data); console.log('hi') });
This ashx code is working but i recive nothing in response
This is ashx.cs code
context.Response.ContentType = "text/plain";
if (!string.IsNullOrEmpty(context.Request.QueryString["action"]))
{
string action = context.Request.QueryString["action"];
switch (action.ToLower())
{
case "productlist":
context.Response.Write("ersoy");
break;
}
}
I have query 1.9.0 version. In response tag not appeare anything.
Before i used it many times but now i cant understand where is the bug.
You are violating the same origin policy restriction that's built in browsers. Your ASP.NET MVC application containing this javascript file is hosted on http://localhost:2197 but you are attempting to perform an AJAX request to http://localhost:21067 which cannot work.
There are some workarounds such as using JSONP (works only with GET requests) or CORS (works only in modern browsers that support it). If for some reason you cannot use some of those techniques you could have a server side controller action inside your ASP.NET MVC application which will perform the actual call to the remote domain and act as a bridge between the 2. Then from your client script you will send the AJAX request to your own domain.

determining which server (in a web farm) the asp.net ajax request came from?

I was thinking how we can find out which server the page was served from: I'd do so by doing something like put a hidden variable on the page which has the IP or server name from the server it got processed. But what do I do for asp.net ajax requests: those that happen as a partial postback? I'd have to put the hidden variable in the update panel, but what if there are many update panels in the page?
I checked out another SO post, but the solution was for iis 7. What is the equivalent for iis6? And how can we read the header? Where to look?
You can set IIS6 custom headers via IIS MMC by opening a site's properties then clicking on the HTTP Headers tab:
You can also use adsutil (found in c:\InetPub\AdminScripts):
cscript adsutil set w3svc/1/root/HttpCustomHeaders "X-Served-By:Server-001"
The command above will configure the HTTP Headers for the default website.
Be careful when using adsutil as this will overwrite any existing headers already configured.
To set multiple headers do:
cscript adsutil set w3svc/1/root/HttpCustomHeaders "X-Served-By:Server-001" "X-Powered-By:ASP.NET"
Update:
With regard to accessing the response headers on the client, if you're using an ASP.NET AJAX update panel then add this script to the end of your page:
<script type="text/javascript" language="javascript">
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endPageRequest);
function endPageRequest(sender, args) {
var allHeaders = args._response._xmlHttpRequest.getAllResponseHeaders();
var headers = allHeaders.split('\n');
// At this point you have a string array of response headers.
// Or you can get an individual header:
var header = args._response._xmlHttpRequest.getResponseHeader("MyHeader");
}
</script>
This will hook into the page request manager such that when the Ajax request completes you also get visibility of the underlying XMLHttpRequest object which has a copy of the response headers.
You can do something similar with jQuery:
$.ajax({
url: "/Home/HeadTest",
success: function (data, textStatus, xhr) {
var header = xhr.getResponseHeader("MyHeader");
}
});

Resources