Is there a way to secure static files using owin? - c#

I have a problem after implementing Owin and secured all controllers and webapi of a asp.net mvc site, using cookie and bearer token. All static files like javascripts , images and some html files are unsecured and can be downloaded without authorization.
So far I have tried to implement, staticfiles (using Microsoft.Owin.StaticFiles) and setting static files for a request path but without any sucess. For example this?
app.UseStaticFiles("/Scripts");
And I have tried , using web.config settings like this wich is mentiond in katana documentation, but from this point to lets say roleX should have access to imagesX and roleY to imageY ? nothing is mention in the documentation about that or even how to control the files.
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
So question is how do i secure static files using owin and authorization cookie.
Do i have to write a own middle ware , checking each request if it is a static file, or is there any recomended package on nuget that solves this?

OWIN way of doing this would be with some middleware. New asp.net core will not support web.config and modules. Just be sure to plugin your auth middleware before static file middleware. It really depends what's your authentication and authorization framework of choice, but you can check an example here: https://coding.abel.nu/series/owin-authentication/

Related

WebApi authentication failed on server with Angular

I am using Asp.Net web API 2.0 with Angular 6 for UI. The solution contains the projects for angular and web API.
My solution works fine (I am able to access pages and login) when I am running it on Visual Studio on localhost. However when I deploy the build on server I am unable to login and getting the below error:
Access to XMLHttpRequest at 'http://xx.xx.xx.xxx:xxxxx/authenticate' from origin 'http://xx.xx.xx.xxx:xxxxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
To quickly handle CORS for Web API, add below settings in web.config file of Web API inside <system.webServer> section:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
But there is a security risk involved in enabling CORS. A good explanation is here.
Can you please add this following code to startup.cs -> configure()
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin().AllowCredentials());
Browser security prevents a web page from making AJAX requests to another domain. This restriction is called the same-origin policy and prevents a malicious site from reading sensitive data from another site. However, sometimes you might want to let other sites call your web API.
Install-Package Microsoft.AspNet.WebApi.Cors
Open the file App_Start/WebApiConfig.cs. Add the following code to the WebApiConfig.Register method: config.EnableCors();
Next, add the [EnableCors] attribute to the Controller class
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class TestController : ApiController
{
// Controller methods not shown...
}
Now you can access the API's

ASP.NET Core 2.1 get current web hostname and port in Startup.cs

I want to register my WebAPI to Consul service discovery and for that I should provide URL of my WebAPI (for example: http://service1.com) and health check endpoint (http://service1.com/health/check). How can I get that URL?
I found this piece of code:
var features = app.Properties["server.Features"] as FeatureCollection;
var addresses = features.Get<IServerAddressesFeature>();
var address = addresses.Addresses.First();
var uri = new Uri(address);
It returns 127.0.0.1:16478 instead of localhost:5600. I think first one used by dotnet.exe and second one is IIS which forwards 5600 to 16478. How can I get localhost:5600 in Startup.cs?
Well, there are multiple solutions for this problem. Your address is this:
string myurl = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}";
It returns 127.0.0.1:16478 instead of localhost:5600
You got this right yes. One is from IIS and one from dotnet. So, you have a problem of trying to get correct url. Ok, so what happens if you service is behind reverse proxy? https://en.wikipedia.org/wiki/Reverse_proxy
Then your service will not be exposed directly to internet, but requests made to specific url will be passed from reverse proxy to your service. Also, you can configure reverse proxy to forward additional headers that are specifying where the original request came from. I think that most reverse proxies are using X-Forwarded-For (Some of them are using X-Original-Host etc).
So if you have proper header set on RP, then you can get url like this:
url = url.RequestContext.HttpContext.Request.Headers["X-Forwarded-For"]
Url is of type UrlHelper. To simplify this method, you can create extension method (GetHostname(this UrlHelper url)) and then us it in your controller or wherever you want. Hope it helps
I don't think it is possible since there is usually a reverse proxy in production that handles public address and the application itself should not be exposed to public and, therefore, be aware of public address. But there can be some workarounds:
Place URL is some kind of config file that can be updated during deploy steps to have the correct URL.
Application can get full URL of the request like this, so after first actual request to the application we can get hostname.
EDIT: I reread your question. You wanted to know how to do this in Startup.cs. You can, but with fewer fallbacks. Your only choices are configuration or raw DNS.GetHostName(), which are less than ideal. Instead, upon any request to your service, lazily register your API. This is when you have context. Prior to that, your service knows nothing Jon Snow. The first request to your API is likely going to be health-checks, so that will kick off your registration with consul.
A solution I've used is a combination of configuration and headers in a fallback scenario.
Rely first on the X-Forwarded-For header. If there are cases where that doesn't apply or you have a need to... you can fallback to configuration.
This works for your use case, discovery. That said, it also works when you want to generate links for any reason, (e.g. for hypermedia for JSON API or your own REST implementation).
The fallback can be useful when there are reconfigurations occuring, and you have a dynamic configuration source that doesn't require a redeployment.
In the ASP.NET Core world, you can create a class and inject it into your controllers and services. This class would have a method that knows to try config first (to see if overriding is needed) and then the X-Forwarded-For header, and if neither is appropriate, fallback further to HttpContext.Request to get relevant URI parts.
What you're doing is enabling your API to be contextless and resiliency (to change) by giving it some contextual awareness of where "it lives".
This happens when you try to get current URL in Startup.cs. I've faced this issue before. What i did as Solution for my problem is. I Just declared Custom Setting in AppSettings in web.config(For Local) file and web.release.config(For Live)
like following
in web.config
<appSettings>
<add key="MyHost" value="http://localhost:5600" />
</appSettings>
in web.release.config
<appSettings>
<add key="MyHost" value="http://myLiveURL.com" />
</appSettings>
in startup.cs
string hostSetting = ConfigurationSettings.AppSettings["MyHost"];
And different host in release file. so what it helped is i can get Localhost URL in local website from web.config and Live URL from web.release.config.
if you are using Azure for live. it will be more easier for live(you would not need to add setting web.release.config file). add app setting in your website application setting https://learn.microsoft.com/en-us/azure/app-service/configure-common
In Case of ASP.NET Core you can use appsettings.json instead of web.config

Only receiving HTTP requests in Google App Engine Flex with .NET. How to only allow HTTPS?

I have a website written in C# using ASP.NET Core MVC5 that I'm deploying onto Google Cloud App Engine Flex. I want to completely disable HTTP and only allow HTTPS. I do not want a redirect from HTTP to HTTPS. How do I go about doing this?
I tried searching and could not find any way to specify it in the app.yaml for .NET and App Engine Flex. So I tried to do it within the webserver code itself. At first as a test, I tried to use the RequireHttps attribute and I also tried an implementation of RequireHttpsOrClose (https://www.recaffeinate.co/post/enforce-https-aspnetcore-api/), which is closer to what I actually want.
When I built the projects and deploy via IIS (i.e. NOT google app engine), everything works fine. However, when I deploy to Google App Engine, they don't work right. When I try to connect to them, it always seems to come in as HTTP, even if the URL I use to connect is set as HTTPS. When I used the RequireHttps attribute, I got a "too many redirects" error, which tells me that it was taking my HTTPS request (e.g. "https://www.test.com") but within the controller, it was seeing it as if I had sent a plain HTTP request (e.g. like it was seeing "http://www.test.com" instead). So it again saw this as a supposed plain HTTP request (though it really wasn't) and attempted to redirect -- once again -- to an HTTPS request, which was again processed as a plain HTTP request. And this seems to continue infinitely, causing a redirect loop.
To test this further, I checked the value of Request.IsHttps, I ALWAYS got false even though I was definitely connecting with HTTPS. So for some reason the site is only receiving HTTP requests even though I'm requesting with HTTPS.
Again, when I test outside of App Engine (by deploying as via IIS on our non-Google web server), everything works perfectly. It is only when I deploy on App Engine that I get this weird behavior.
I can reproduce this by setting up a barebones project with "ASP.NET Core on Google Cloud Platform" template. The only relevant code is my controller, which is barebones and simply returns the dummy values supplied by the template:
[RequireHttps]
[Route("/")]
public class ValuesController : Controller
{
private readonly ILogger _logger;
public ValuesController(ILogger<ValuesController> logger)
{
_logger = logger;
}
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
// When I remove the RequestHttps attribute in order to stop the redirect
// loop, I always get values "not https" and "HTTP/1.1" even though
// my browser is connecting to https://xxxxx.appspot.com
if (Request.IsHttps) return new string[] { "https", Request.Protocol };
return new string[] { "not https", Request.Protocol };
}
}
I publish this with an app.yaml of:
service: testservice
runtime: aspnetcore
env: flex
The web.config is unmodified from the project template:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>
So my questions are:
Why am I only receiving HTTP requests when I am definitely entering a HTTPS URL? Did I do something or set something in Google Cloud or in App Engine to cause this to happen?
Otherwise, is there some other method of disabling HTTP and only allowing HTTPS using App Engine Flex with .NET? All examples I could find of disabling it are for Python using handler in the app.yaml, but that doesn't seem to apply to .NET.
In the flexible environment you cannot use the app.yaml configuration to require HTTPS-only access, that can only be done in the standard environment. See related Correctly assign HTTPS only custom domain to flex env
From Request limits:
SSL connections are terminated at the load balancer. Traffic from the load balancer is sent to the instance over an encrypted channel,
and then forwarded to the application server over HTTP. The
X-Forwarded-Proto header lets you understand if the origin request was
HTTP or HTTPs.
This explains why you cannot use Request.IsHttps to identify if the request was HTTPS.
But you can modify that piece of logic to check the X-Forwarded-Proto instead which should get you what you need. I think it could be something along these lines (not 100% certain, I'm not a .NET user):
if (Request.Headers["X-Forwarded-Proto"] == "https") return new string[] { "https", Request.Protocol };
return new string[] { "not https", Request.Protocol };

ASP.Net Core Web API, deployed under IIS is returning 404

I have a new Web API developed in ASP.NET Core. This Web API is supposed to be deployed in IIS and will have to work over SSL, so I have the [HttpsRequired] attribute on all my controllers. I struggle to make it work while deployed, so for now I relaxed the requirements and commented out those attributes. Doing so, I was able to create two bindings in IIS, one for HTTPS and one for HTTP. Given that my Web API is created in ASP.NET Core, I followed the deployment steps Rick Strahl has in his excellent blog post. I have selected "No Managed Code" for the .NET CLR version. The IIS machine is a 64-bit Windows Server 2012 R2 environment - not sure whether this matters or not. The .NET Core Windows Server Hosting bundle has been installed on the server and I can see the AspNetCoreModule listed in the Modules grid.
If i try to access the Web Api (I created a very simple GET method that returns some information regarding the assembly) with Fiddler, I get a 404 error. For now, i run Fiddler on the same machine, so I tried all combinations (localhost, IP address and full machine name in the domain).
No errors are logged in the EventViewer. Does anyone have any suggestion on how to troubleshoot this issue?
TIA,
Eddie
EDIT1: Here is my controller:
[Route("api/info")]
//[RequireHttps]
public class InfoController : Controller
{
private ITncRepository _repository;
public static ApplicationAssemblyDetails ApplicationAssemblyDetails { get; set; }
public InfoController(ITncRepository repository)
{
_repository = repository;
ApplicationAssemblyDetails = ApplicationAssemblyDetails.Current;
}
[HttpGet("")]
public JsonResult Get()
{
return Json(new WebApiInfoModel()
{
CurrentTime = DateTime.Now,
CurrentUtcTime = DateTime.UtcNow,
AssemblyName = ApplicationAssemblyDetails.ApplicationAssembly.FullName,
VersionNumber = ApplicationAssemblyDetails.VersionNumber,
BinFolder = ApplicationAssemblyDetails.BinFolder,
BuildMode = ApplicationAssemblyDetails.BuildMode,
TradeMark = #" © 2016-2017 * SomeCompany (www.somecompany.com)"
});
}
}
The ApplicationAssemblyDetails is a nuget package that gives some info about the current assembly. WebApiInfoModel is my model class for the Web API Information I want to pass back as a test to the client.
The web.config file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\My_ASP_NET_Core_Web_API.exe" arguments="" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="true" />
</system.webServer>
</configuration>
Finally, to answer your last question, Ignas, I use a Publishing Profile that uses the File system as a method, targets the .NET Framework 4.5.2, using the release configuration. Given that my project is a Web API and not an MVC 6 Web Application, the publishing package creates a stand-alone application. Since the clients need to call my Web API using SSL, I think that it has to be hosted in IIS, so running the standalone application would not work. Of course, for testing purposes, I could try to run it. That's why I commented out the [HttpsRequired] attribute. I will try that and report back, but for now I hope I gave you all the information you required.
I'm having a setup very close to yours (Asp.Net core, Web API, IIS, HTTPS ...) working fine on my end.
I faced the same issue at some point because I was not using the proper path to access my controller/action, it depends on how you deployed it under IIS. For instance, in my case when I use Kestrel directly it goes through a URL like that:
http:// localhost:5000/controllerName/actionName
But I can also contact my Web API via IIS and in that case I need to use a URL like that:
http:// localhost:5001/applicationName/controllerName/actionName
Have you created an application under IIS that could explain you getting a 404 because you would not use the proper path?
For instance, in my case:
screenshot of the asp.net core api under iis
And I'm accessing it, through the URL:
https: //servername:serverport/RequestPortalAPI/ControllerName/ActionName
In the end, it was a matter of properly configuring Widows Authentication. For Fredrik and anyone else reading this post for a solution, these are the steps I performed:
In IIS, in the Authentication form for my Web API, I disabled Anonymous Authentication and I enabled Windows Authentication:
Make sure that "Negotiate" is at the top of the list for Enabled Providers:
In the Application Pools, I configured my Web API to run under an account that the UIT department of my client has given me:
The configuration file of my Web API (web.config) contains the following settings:
Now we are getting into the dark areas of the problem. In order to use Windows Authentication and let the credentials of the caller be passed through to the backend (in my case a SQL Server database), the Web API has to be configured to use Kerberos. I found this after I opened a ticket with Microsoft and I worked closely with one of their engineers. For this to happen, you need to follow these steps:
Create a Service Principal Name (SPN) for your Web API and the domain account it runs under. You need to run this command:
Where hostname is the fully qualified domain name of your Web API. The Domain\Username are the domain account under which the Web API is running. You need special domain privileges, so you may want to involve someone from IT. Also, from now on, you need to access your Web API by the full domain name, not by IP address. IP address won't work with Kerberos.
Also, with the help of your IT person, you need to enable delegation for any service using Kerberos for the domain account under which you run your Web API. In the Active Directory Users and Computers, locate the account you use to run your Web API, bring up its properties, click on the Delegation tab and enable the second option "Trust this user for delegation to any service (Kerberos Only):
We have also made some changes on the server that runs our database, but I am not 100% those are truly required, so I won't add them here because I don't even know if you use SQL Server or some other backend repository.
Let me know if you need those as well and I will add them later.
Good luck,
Eddie

global.asax not being updated

We have a web application where we are using global.asax for url rewriting. We use a compiled version of the site on the live server.
As a part of modification request, we had to add some custom native AJAX code where javascript would call a webservice to update the content of the page. For being able to call the webservice with extension .asmx, we modified the url rewriting code to handle asmx requests seperately.
this arrangement works fine on the local machine, but when we publish the site and deploy it on the live server, the new code doesnt seem to get included. It still skips the condition to check the ".asmx" extension, and throws a page not found exception considering the webservice name as a page name.
We have tried looking all over and googled for such things as well.. but no avail..
any pointers on what might be going wrong.. ?
Assuming your URL rewriting is good (isn't that normally implemented as a HttpModule?) I'd check to make sure that there's an ISAPI mapping in IIS on production that sends .asmx requests to ASP.NET.
If you think your changes to the global.asax haven't been rejitted then you can always stop the application pool, go find your web applications compiled bits in c:\windows\microsoft.net\framework[version]\temporary asp.net files... and delete the jitted version. I've seen ASP.NET miss when it comes to Jitting changes before.
I'd also consider running http fiddler (IE) or tamper data (FireFox extension) on one of the pages that makes calls to the web service. It will tell you exactly how the page is calling the web service and you can validate that the called URL is correct.
There is machine.config file where you can add HttpModules. I also think that you can do that through web.config.
One reason I can think of is that in the Web.config, you might have configured the routing module in the system.web section but not in system.webServer (or at least forgot something in there).
I the similar problem before and the solution was to remove the module & add it again in the system.webServer section of the Web.config like this:
<system.webServer>
<modules>
<remove name="UrlRoutingModule" />
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, e="RoleManager" type="System.Web.Security.RoleManagerModule"/>
</modules>
</system.webServer>
It might be a different module or handler but the idea is basically the same. It's important to "remove" the module first.

Categories