I'm attempting to build a service in ServiceStack whose sole responsibility will be to interpret requests, and send a redirect response. Something like this:
[Route("/redirect/", "POST")
public class Redirect : IReturnVoid
{
public string Something { get; set; }
}
public class RedirectService : Service
{
public object Post(Redirect req)
{
// make some decisions about stuff
return new HttpResult(){ StatusCode = HttpStatusCode.Redirect, Headers = {{HttpHeaders.Location, "place"}}};
}
}
I did initial testing using fiddler, setting a content-type of application/json and creating an appropriate request body.This did exactly as expected: the service request gave a 302 response and redirected to the expected location.
I've also tested this by using a basic Html form post, with an action of http://myserviceuri/redirect/, which also works as expected and redirects appropriately.
However, i've hit an issue when attempting to use the SS c# client to call the same service. If I call the following code in an aspx code behind or an mvc controller
var client = new JsonServiceClient("uri);
client.post(new Redirect{Something = "something});
I get a 500 and the error message:
The remote certificate is invalid according to the validation procedure.
Which makes sense as it's a development server, with a self-cert. But I get the feeling that, as I can call the service successfully by other means, that this is a red herring.
Should I be using a different type of c# client to make the request, or setting any more custom headers, or something else? Am I fundamentally not understanding what i'm trying to do?
Please let me know if more info is needed. Thanks.
What's happening here is that the JsonServiceClient is happily following the redirect, doing more than what you've expected it to do.
I'll reference a related question and answer for posterity ( - hopefully you've resolved this issue a long time ago...).
POST to ServiceStack Service and retrieve Location Header
Essentially you'd use .net's WebRequest or the ServiceStack extensions mentioned in the answer to see the redirect and act as you see fit.
Related
I want to use server-side response caching (output cache) with asp.net core 2.0 and found out about Response Caching Middleware and wanted to give it a try with a brand new asp.core mvc project.
Here is the description from the link above which makes me think this could be used like output cache.
The middleware determines when responses are cacheable, stores responses, and serves responses from cache.
Here is how my startup.cs looks like.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddResponseCaching();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseResponseCaching();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
and here is the HomeController.cs
[ResponseCache(Duration = 60)]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
there is also a timestamp at the bottom of _Layout.cshtml file so i can tell when the page is rendered, like below.
<p>© 2018 - ResponseCachingMiddleware - #DateTime.UtcNow</p>
Cache-Control headers seem to be fine, this is what I get in headers when I load the page but time stamp keeps getting updated on every refresh every second.
Cache-Control:public,max-age=60
What I'm understanding from MS documentations is Response Caching Middleware is the server-side caching mechanism that takes care of caching the response while Response Caching seems to be just a filter to manipulate response headers for caching.
Can't tell if there is something wrong with my understanding or code and I wanna complain that I'm feeling this way too often since I started prototyping with ASP.Net Core. Maybe you could also suggest better resources as a side topic.
I've checked out this post before
ASP.NET Core 2.0 - Http Response Caching Middleware - Nothing cached
Also checked this out but it seems like the only difference is I'm using mvc.
https://github.com/aspnet/ResponseCaching/blob/dev/samples/ResponseCachingSample/Startup.cs
Thanks
Edit: I'm seeing the message below in the output window, cannot find anything about it on google except the few places I already checked for response caching middleware.
Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware:Information:
The response could not be cached for this request.
Note: I wish I could create #response-caching-middleware tag. Not sure #responsecache is relevant.
I had the same issue, I was about to pull my hairs over it, I'd set app.UseResponseCaching(); as well as services.AddResponseCaching(); and add ResponseCache on top of my action exactly like what was told in Microsoft official Docs, despite the the cache-controll header was set correctly on response returning from server but still nothing cached at server-side.
After couple of hours of sweating on this issue I figured out where the problem arises and why nothing cached at server.
Browsers by default set cache-controll value to max-age=0 for the request (if the request is not caused by back or forward) even though you set cache-controller correctly in your response by adding ResponseCache attribute on top of you action (or controller) since the cache-controller sent by request is set to max-age=0, the server is unable to cache response, I think this must be added to list of Response Caching limitation as well
Anyway you can override browser default behavior by adding few line of code right before calling app.UseResponseCaching(); on the other hand you need to add a custom middle-ware to modify request cache-control header value before calling app.UseResponseCaching();.
See code below, worked for me hope work for you too
app.Use(async (ctx, next) =>
{
ctx.Request.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(60)
};
await next();
}
);
app.UseResponseCaching();
for ensuring that ResponseCaching works as expected you can also use postman but you must set 'Send no-cache Header' to off in the setting, see image below
I had this same confusion recently.
ASP.Net Core's ResponseCaching does provide both client-side caching (through HTTP response headers) & server-side (through a memory cache'd middleware that short-circuits other middlewares if the response is in the cache). The server-side portion reads the HTTP response cache headers to determine if it should do server-side caching (similar to what an ISP or CDN might do).
Unfortunately, debugging the server-side ResponseCaching is tricky because it has weird rules & there's not adequate logging. In my case I pulled down Microsoft's source code to step through it & find the issue with my code.
The note you found in the output window "The response could not be cached for this request" is a clue.
There's 2 parts to the server-side caching of a request. The server has to prime the cache the first time the url is requested. It will serve the cached version the 2nd time it's requested. Pay attention to when the error message shows up, if it's on the 1st or 2nd request. That'll tell you if it couldn't be stored in the cache or if it couldn't be retrieved from the cache.
The rules for both storage & retrieval are in this source code file:
https://github.com/aspnet/ResponseCaching/blob/3bf5f6a1ce69b65c998d6f5c739822a9bed4a67e/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs
Your "Cache-Control:public,max-age=60" header should match these rules just fine.
My guess is you actually had it working, but didn't know how to test it correctly.
There is a counter-intuitive portion of ResponseCaching noted in this issue: https://github.com/aspnet/Home/issues/2607
Essentially, if the browser sends a no-cache or no-store header (when you hit CTRL+F5 or have your debugger tools open), ASP.Net Core's ResponseCaching will honor the browser's request & re-generate the response.
So, to test if your code was working you probably loaded the page, which primed the cache, then you hit CTRL+F5 to force-refresh your browser & you expected the server-side to respond with a cached entry rather than running your WebAPI code. However, it honored the no-cache request header & bypassed the cache (& wrote that message in your output log).
The way to test this would be to clear your browser cache in-between requests (or switch to incognito), rather than using CTRL+F5.
On a side note, honoring the no-cache/no-store request headers was probably a poor design choice since ASP.Net Core's ResponseCache will most likely be used by a server who owns the response, rather than an intermediary cache like a CDN/ISP. I've extended the base ResponseCache with an option to disable honoring these headers (as well as serialize the cache to disk, rather than in-memory only). It's an easy drop-in replacement for the default cache.
You can find my extension here:
https://github.com/speige/AspNetCore.ResponseCaching.Extensions
https://www.nuget.org/packages/AspNetCore.ResponseCaching.Extensions
There are also a few other other gotchas with ResponseCaching to watch out for which you may have already read about in the blog urls you posted. Authenticated requests & responses with set-cookie won't be cached. Only requests using GET or HEAD method will be cached. If the QueryString is different, it'll make a new cache entry. Also, usually you'll want a "Vary" header to prevent caching if certain conditions of a request differ from the previously-cached request (example: user-agent, accept-encoding, etc). Finally, if a Middleware handles a request it'll short-circuit later Middlewares. Make sure your app.UseResponseCaching() is registered before app.UseMVC()
If the Cache-Control header is coming through, then it's working. That's all the server can do from that perspective. The client ultimately makes the decision whether or not to actually cache the resource. Sending the header doesn't force the client to do anything; in fact, the server, in general, cannot force the client to do anything.
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:
...
I have two classes, client and user. User is a field in client (there is a foreign key). I am trying to do a join, getting all the clients and the related user (it is one to one).
I am using Entity Framework and a web service that gives me my data.
I currently am getting all my clients like:
public DbSet<Client> getClients()
{
return context.Clients;
}
I need to also get the related object user. I found an example that tells me to do:
public DbSet<Client> getClients()
{
return context.Clients.include(x => x.User);
}
This throws an exception, I need to be working with IQueryable. If I change my function the connection to the web service does not work.
How do I do what I am trying to do?
EDIT:
The exception I get from the webservice is An error occurred while receiving the HTTP response to http://localhost:60148/WebService.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.
try these links:
Expose IQueryable Over WCF Service
and IQueryable problems using WCF you need to send something else back, like a List, as IQueryable is actually a query, which you can't send back. the links above provide alternative ways to do it, to get around the IQueryable restriction.
I am using Spring Web MVC and have written a controller that shall return binary data to a plugin in a web-page that show 3D content. This plugin uses STK files for it's models, so I have set-up my webapp to produce this kind of contents for *.stk URLs
The springDispatcherServlet is setup to handle *.stk requests in my web.xml
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>*.stk</url-pattern>
</servlet-mapping>
My controller looks like this:
#Controller
#RequestMapping(value="/3dobject", produces="application/octet-stream")
public class Object3DController {
#Autowired
private DesignerService designerService;
#RequestMapping(value="/{article}.stk", method=RequestMethod.GET, headers="Accept: application/octet-stream")
public void getObject3DForArticle(HttpServletResponse response, #PathVariable String article) throws IOException {
response.setContentType("application/octet-stream");
Object3D object = designerService.get3DModelForArticleNumber(article, LanguageCode.NORWEGIAN);
byte[] content = object.getContent();
response.setContentLength(content.length);
IOUtils.write(content, response.getOutputStream());
}
}
When I try to access the data through a browser, I get the following error:
HTTP Status 406 - The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
I have deployed my app to a tomcat server on localhost.
Any ideas of what I must do to make the browser download the binary data?
PS! I changed from *.3do (3d object) to *.stk. With 3do it worked as expected, but the plugin fails with an error (which is hard to interpret), so I have to experiment in order to verify wether or not the URL extension matters...
The RequestMapping.produces() narrows the request and therefore is
only mapped if the Accept matches one of these media types
So you need to set the corresponding Accept header in the request from the plugin. This cannot be done by simply making an HTTP GET Request by typing in a URL into a browser. One plugin I find useful for Chrome is the Advanced REST Client. There are plenty of others that offer similar functionality. Alternatively try using cURL to make the request with the correct header.
I'm writing an app using asp.net-mvc deploying to iis6. I'm using forms authentication. Usually when a user tries to access a resource without proper authorization I want them to be redirected to a login page. FormsAuth does this for me easy enough.
Problem: Now I have an action being accessed by a console app. Whats the quickest way to have this action respond w/ status 401 instead of redirecting the request to the login page?
I want the console app to be able to react to this 401 StatusCode instead of it being transparent. I'd also like to keep the default, redirect unauthorized requests to login page behavior.
Note: As a test I added this to my global.asax and it didn't bypass forms auth:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpContext.Current.SkipAuthorization = true;
}
#Dale and Andy
I'm using the AuthorizeAttributeFilter provided in MVC preview 4. This is returning an HttpUnauthorizedResult. This result is correctly setting the statusCode to 401. The problem, as i understand it, is that asp.net is intercepting the response (since its taged as a 401) and redirecting to the login page instead of just letting it go through. I want to bypass this interception for certain urls.
Ok, I worked around this. I made a custom ActionResult (HttpForbiddenResult) and custom ActionFilter (NoFallBackAuthorize).
To avoid redirection, HttpForbiddenResult marks responses with status code 403. FormsAuthentication doesn't catch responses with this code so the login redirection is effectively skipped. The NoFallBackAuthorize filter checks to see if the user is authorized much like the, included, Authorize filter. It differs in that it returns HttpForbiddenResult when access is denied.
The HttpForbiddenResult is pretty trivial:
public class HttpForbiddenResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.HttpContext.Response.StatusCode = 0x193; // 403
}
}
It doesn't appear to be possible to skip the login page redirection in the FormsAuthenticationModule.
Might be a kludge (and may not even work) but on your Login page see if Request.QueryString["ReturnUrl"] != null and if so set Response.StatusCode = 401.
Bear in mind that you'll still need to get your console app to authenticate somehow. You don't get HTTP basic auth for free: you have to roll your own, but there are plenty of implementations about.
Did you write your own FormsAuth attribute for the action? If so, in the OnActionExecuting method, you get passed the FilterExecutingContext. You can use this to pass back the 401 code.
public class FormsAuth : ActionFilterAttribute
{
public override void OnActionExecuting(FilterExecutingContext filterContext)
{
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.Cancel = true;
}
}
This should work. I am not sure if you wrote the FormsAuth attribute or if you got it from somewhere else.
I haven't used the AuthorizeAttribute that comes in Preview 4 yet. I rolled my own, because I have been using the MVC framework since the first CTP. I took a quick look at the attribute in reflector and it is doing what I mentioned above internally, except they use the hex equivalent of 401. I will need to look further up the call, to see where the exception is caught, because more than likely that is where they are doing the redirect. This is the functionality you will need to override. I am not sure if you can do it yet, but I will post back when I find it and give you a work around, unless Haacked sees this and posts it himself.
I did some googling and this is what I came up with:
HttpContext.Current.Response.StatusCode = 401;
Not sure if it works or not, I haven't tested it. Either way, it's worth a try, right? :)