I want to turn off caching for my HTTP responses. Here's my code:
public class CacheControlHeaderAttribute : ResponseFilterAttribute
{
public override void Execute(IRequest req, IResponse res, object responseDto)
{
res.AddHeader(HttpHeaders.CacheControl, "no-store,must-revalidate,no-cache,max-age=0");
res.AddHeader("X-Test", "Hello from ServiceStack!");
}
}
The problem is that it doesn't work. Cache-Control is getting set to private. How do I fix this?
You need to explicitly opt-in for HTTP Caching in ServiceStack which doesn't cache or add the HTTP Cache-Control header by default so I'm assuming it's being added by your Web Server which you'll want to look at configuring instead.
Otherwise adding HTTP Headers can be added using any of the options listed in Customize HTTP Responses.
But if it's not being returned something else fronting your App (e.g. Web Server / Proxy) is using their own Cache-Control headers.
Related
I'm using security headers middleware in a web app to add security headers to all outgoing http requests. Security headers seem to get added to all network requests to internal resources - that is resources that make up the web app such as the javascript scripts and the images used in the web app and the css and html files. However the security headers do not get added to any external http requests such as to an API that I made that the web app uses to get json data. How do I make it just add security headers to everything, rather than just to the web apps own resources?
Below is some of the relevant code that adds security headers middleware
startup.cs
private ILogger<SecurityHeadersBuilder> _logger;
private readonly SecurityHeadersPolicy _policy = new SecurityHeadersPolicy();
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, ISecurityHeadersBuilder securityHeadersBuilder)
{...
app.UseSecurityHeadersMiddleware(
securityHeadersBuilder.AddDefaultSecurePolicy()
);
securityHeadersBuilder.cs
public SecurityHeadersBuilder AddDefaultSecurePolicy()
{
AddFrameOptionsDeny();
AddXssProtectionBlock();
AddContentTypeOptionsNoSniff();
AddNoCache();
AddStrictTransportSecurityMaxAgeIncludeSubDomains();
AddContentSecurityPolicyAllContentFromSelfAndGoogle();
RemoveServerHeader();
return this;
}
public SecurityHeadersBuilder AddFrameOptionsDeny()
{
_policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.Deny;
_logger.LogInformation(string.Format("setting {0} http header value to {1}", FrameOptionsConstants.Header, FrameOptionsConstants.Deny));
return this;
}
There are two type of headers: request headers and _response headers.
The server sets response headers to instruct the browser how to handle a response (block iframing for example).
Therefore it would not make sense to do a request with (for example) the header X-Frame-Options : Deny. Because the client application could alter the value and ignore the security restriction. The server will not handle the value of the header anyway, the user-agent of the browser will use this response header.
If you do a call to an (external) API you should manually add request headers to an HttpClient and make the call. The API in turn can return the (security) response headers.
All the headers that you have in the example code are response headers and should not be set as request headers.
What is the most secure way of providing cross-domain access ?
what are the trade-offs ?
I know we can set cross origin access headers, but this would need my server to know the list of servers beforehand.
You do not need to know the list of servers before hand as you can set Access-Control-Allow-Origin: *, however this is not as secure as it will allow other sites to make use of your services. So make sure when constructing your headers that you allow Access-Control-Allow-Origin from a restricted list. I just use a regular expression to compare against since we allow from multiple. When I verify that there is a match, I return the request origin back in the header. So if I match on something like web.*energydomain.com and the origin is webservices.energydomain.com, then I pass back Access-Control-Allow-Origin: webservices.energydomain.com This tells the calling service (and anyone listening) that I accept from this origin and only this origin, even If I might accept from webstart.energydomain.com.
So using spring we have created a filter.
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
response.setHeader("Access-Control-Allow-Credentials", "true")
//If this is a pre-flight request, make sure that we are allowing them
if ("OPTIONS" == request.method) {
response.setHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
response.setHeader("Access-Control-Max-Age", "604800")
response.setHeader("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Allow-Origin, Vary, Cookie, Key")
//Check to see that the referrer/origin matches the set of allowed origins in the application configuration
String referrer = request.getHeader("Origin")
if (referrer?.matches(ServerProperties.instance.accessControlAllowOriginRegEx)) {
response.setHeader("Access-Control-Allow-Origin", referrer)
}
} else {
//set other headers here and continue chain (we don't bother continuing chain on preflight)
chain.doFilter(request, response)
}
}
You can also do this on htaccess
<FilesMatch "\.(ttf|otf|eot|woff)$">
<IfModule mod_headers.c>
SetEnvIf Origin "http(s)?://(www\.)?(webservices.energydomain.com|webservicesmo.energydomain.com|webservicestest.energydomain.com)$" AccessControlAllowOrigin=$0
Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
</IfModule>
</FilesMatch>
I prefer attempting to reduce the preflight traffic noise and avoid it all together. In particular, I use xdomain. It was pretty simple for me to setup if using angular or jQuery. On your app server, add a proxy.html as stated in the help on the below link. Add some tags referring to the js files on your "client" and viola, no more pre-flights. This wraps in an iframe to avoid need for cors check. You can still control the origins as you do with the CORS preflights as explained above, it just avoids them alltogether.
https://github.com/jpillora/xdomain
All,
We are trying to use ServiceStack at work as a backend API for all our internal and forward-facing sites, but we are running into problems. Here are the issues...
Sites
- site1.xyz.com
- site2.xyx.com
- etc.xyx.com
API
- api.xyx.com
1) We need a custom request header to handle the authentication. Our apps send an app ID to the API server to authenticate the app, so JSONP is out.
2) Since JSONP is out, we need to support OPTIONS. I have this working on Chrome and IE, but IE is throwing that damn security popup. We can solve that with our internal users, but not with our external users.
If anyone has any suggestions as to how to accomplish this, I'd love to hear them. I wish to hell the CORS spec handled subdomains. If it did, this would work and be sick simple...
I'm new to using CORS, but i did get it working with OPTIONS.
public class YourService : Service
{
...
/// <summary>
/// CORS support
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public object Options(YourRequestDto request)
{
// return HTTP Code and Location: header for the new resource
// 204 No Content; The request was processed successfully, but no response body is needed.
return new HttpResult()
{
StatusCode = HttpStatusCode.NoContent,
Headers = {
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Allow-Methods", "GET, OPTIONS" } ,
{ "Access-Control-Allow-Headers", "Content-Type" },
}
};
}
}
My reference was: servicestack REST API and CORS
The web browser should make the OPTIONS request, handled there. Then make the GET, POST, etc after.
This example allows all domains (*), but you could add your own logic to suit.
FYI- i do not think IE has full CORS support until IE 10.
I am running into issue that my second GET request after POST does not get executed at all but returns results prior to POST. I cannot see the request being issued in fiddler at all. If I restart the application then it returns the correct results. I do not have any caching set up. Routes are configured like this:
container.RegisterAutoWired<ComplianceService>().ReusedWithin(ReuseScope.Request);
Routes.Add<CertificateDefinitionList>("/certificates","GET");
Routes.Add<CertificateDefinition>("/certificates/{CertificateDefinitionId}", "GET");
Routes.Add<CertificateDefinitionSave>("/certificates","POST");
There is no implicit Caching in ServiceStack. If you want to Cache responses you need to explicitly request it as seen on the Caching wiki:
public object Get(CachedOrders request)
{
var cacheKey = "unique_key_for_this_request";
return RequestContext.ToOptimizedResultUsingCache(base.Cache,cacheKey, () =>
{
//Delegate is executed if item doesn't exist in cache
//Any response DTO returned here will be cached automatically
});
}
This is probably caused by your proxy which caches results locally. Caching is enabled on http get by default. Specify http caching in your http headers.
Seems like I have a cross domain access problem.
I've seen some solutions that are indicating to add "Access-Control-Allow-Origin: *", but I don't know where I can do this.
Do I need to create some handler?
I'm using WCF Web API.
Error: XMLHttpRequest cannot load http://localhost:8081/Song/0. Origin http://localhost:8080 is not allowed by Access-Control-Allow-Origin.
EDIT
I've noticed that this is only happens when HTTP method is PUT or DELETE.
I can successfully make requests with GET or POST.
I'm making the request using jquery.
$.ajax({
url: Settings.RESTfulEndPointFor('Song/' + songID),
type: 'DELETE',
success: function (response) {
callback(response);
}
});
I don't know why, but it seems like this is resulting in method OPTIONS with Access-Control-Request-Method: DELETE.
Does any one know what causing this?
Any help is appreciated.
I had this problem when connecting to a WCF RESTful service via AJAX calls
My javascript was this:
var GetData= function(){
var data;
$.ajax({
url: this.server + "/data",
async: false,
type: "GET",
success: function (success) {
data = success;
}
});
return data;
};
My service endpoint was opened with this code
ServiceHost host = new ServiceHost(new MyService());
host.Open();
All the important data is stored in the App.config file, I did not have to change that file for this fix.
I knew I had to add the headers somewhere before the response message was sent.
After some searching and hacking I found the Authorization property of a ServiceHost object. The Authorization property is an instance of the ServiceAuthorizationBehavior class whose objects have a property called ServiceAuthorizationManager which is an instance of the ServiceAuthorizationManager class.
By creating a new class that inherits from the ServiceAuthorizationManager and setting it to the ServiceAuthorizationManager property of the Authorization behavior of your ServiceHost instance, you can intercept all calls to your service.
This is how I have implemented my class
public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
HttpResponseMessageProperty prop = new HttpResponseMessageProperty();
prop.Headers.Add("Access-Control-Allow-Origin", "*");
operationContext.OutgoingMessageProperties.Add(HttpResponseMessageProperty.Name, prop);
return true;
}
}
then right after I declare my ServiceHost object (before the host is opened) I add this line
host.Authorization.ServiceAuthorizationManager = new MyServiceAuthorizationManager();
After doing this, rebuilding, and running my service the error message stopped showing up. Hooray!
Lastly, I read an article that described the ServiceHost class was designed for SOAP/WSDL services not RESTful services. For RESTful services the WebServiceHost object should be used.
So
ServiceHost host = new ServiceHost(new MyService());
host.Open();
becomes
WebServiceHost host = new WebServiceHost(new MyService());
host.Open();
You must add references to the following assemblies:
System.ServiceModel.Web
Hope this helps.
Sources:
http://social.msdn.microsoft.com/Forums/en/wcf/thread/97ddb118-fdfd-4651-9e61-4d822861325f
http://www.c-sharpcorner.com/uploadfile/dhananjaycoder/webservicehost-hosting-a-wcf-rest-service/
http://social.msdn.microsoft.com/forums/en-us/wcf/thread/551409FD-DD77-40EF-8B78-DC8B3D7EA0BA
Normally you put this in header of response. So put it in header where you modify/insert other header values like this
header('Access-Control-Allow-Origin: *) //change it according to however header is set in wcf , since this is php syntax
Point is your response should have this header.
The request you are seeing with the OPTIONS method and an Access-Control-Request-Method: DELETE header is called a "preflight request". The CORS specification requires this for requests with methods that have side effects (like DELETE) to ensure the resource is ok with the request.
Check out this section of the spec >>
http://www.w3.org/TR/cors/#cross-origin-request-with-preflight0
Unfortunately I don't know how to make this type of request work with wcf web api.
I have created
AllowCrossDomainRequestHandler : DelegatingChannel
and for each response I'm registering this header:
response.Headers.Add("Access-Control-Allow-Origin", "*");
I got this to work using the following response headers:
res.writeHead(200, {
'Content-Type': 'text/plain',
'Access-Control-Allow-Methods': 'DELETE, POST, GET, OPTIONS',
'Access-Control-Allow-Origin': '*'
});
First, with most web browsers there is no way to actually get around the cross-domain restriction. Most won't even let you change the "accept" header. So you have to use JSONP. JSONP is a way of getting JSON data from a cross-domain service, but it is returned in the form of a javascript snippet - which is allowed. The way it works is that you provide the callback function name to the service, then the cross-domain service returns a simple javascript with the actual JSON values embedded as the parameters to your callback function. This is really easy to do now with WCF WebApi (preview 6). Install it in VS 2010 with NuGet. Once you have it installed, look here for more information.