I have an .net core api api hosted in IIS 8.5.
I want to allow requests from only a certain domain to reach the api.
I have looked at the IIS Rewrite module and see that rules can be setup in there. Is this the best way to do this?
You'll actually find that your web page won't be able to talk to the API right off the bat anyway. This is a form of security so that javascript on a page can't/shouldn't just be able to call out to another domain.
This is called Same-Origin policy, meaning that any script on a page can only make HttpRequests to the same domain. Mozilla explains it better than I can here : https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
So your question actually is, how can I open up my API to cross origin calls, but only for my particular domain. And .net core has got you covered!
First install the following Nuget package :
Install-Package Microsoft.AspNetCore.Cors
Next in the ConfigureServices method of your startup.cs, you need to add the CORS services.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
Now you need to add CORS to your pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors( options => options.WithOrigins("http://example.com").AllowAnyMethod() );
app.UseMvc();
}
Now your API project should allow cross domain calls, but only for your domain. Of course this does not stop people manually calling your API, but it means that malicious scripts on a domain not under your control cannot make calls unbeknown to the user.
More info : http://dotnetcoretutorials.com/2017/01/03/enabling-cors-asp-net-core/
Related
I've built my api in .net core for the first time in core 2.0. The client is built using vs 2017 angular template.
My api is used even by other applications which may not be using windows authentication. For those functions I want to allow anonymous access. For this reason I've to enable both windows authentication and anonymous authentication.
But when enable both I know I cannot get windows user name. In that case how can get the windows user name?
The following code breaks when I enable anonymous authentication along with windows authentication.
[Route("current")]
public ADUser GetCurrentUser()
{
string accountUser = this.User.Identity.Name;
return new ADUser { Name = accountUser };
}
Can someone please help me how did they dealt the following situation. If not can someone tell me how to do the following things in .net core 2.0
Authenticate the users using windows authentication
Protect the api from being accessed by malicious user.
use some basic functions of the api even by anonymous user.
When using windows authentication I need to be able to get windows user name so I check my user, roles database to authorize them accordingly.
[Update]
As I said I know I get windows user name when I enable Windows Authentication and disable all other authentication types in IIS. But I am unable to access functions which I want anonymous users to be able to access even after using [AllowAnonymous].
I can also read from the following snippet that AllowAnonymous doesn't have any affect if only windows authentication is enabled.
When Windows authentication is enabled and anonymous access is disabled, the [Authorize] and [AllowAnonymous] attributes have no effect. If the IIS site (or HTTP.sys or WebListener server) is configured to disallow anonymous access, the request never reaches your app. For this reason, the [AllowAnonymous] attribute isn't applicable.
Thanks
Here's how I solved this:
IIS
Since you want to allow anonymous users to hit some endpoints of your API, you need to enable both anonymous authentication and Windows authentication.
As a side note, you're right saying that [AllowAnonymous] has no effect when only Windows authentication is enabled because IIS, which sits in front of your API, will reject anonymous requests.
ASP.NET Core authentication
Now that anonymous authentication is enabled, IIS will not try to authenticate requests by default, so without any further configuration, all requests will be anonymous as far as ASP.NET Core is concerned.
The answer to this is to indicate to ASP.NET Core that you want to try to run the Windows authentication process on every request. You can do this this way:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Other code omitted for brievity
// This sets the IIS authentication scheme as the default scheme
services.AddAuthentication(IISDefaults.AuthenticationScheme);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Other code omitted for brievity
// This includes the authentication middleware in the request pipeline
// It will try to authenticate every incoming request
app.UseAuthentication();
// MVC comes next, so the authentication will have taken place
// by the time your controller action is executed against the scheme
// used in AddAuthentication
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
MVC
There's now 2 possibilities in your controller:
The client is compatible with Windows authentication, so User.Identity.IsAuthenticated will return true
The client is not compatible with Windows authentication, so User.Identity.IsAuthenticated will fetch the value false
This means that you can either use the [Authorize] attribute on the specific actions that require authentication, or add the AuthorizeAttribute globally to the application and use [AllowAnonymous] on the actions that can be called anonymously.
Deploy your application to IIS and then Open the Authentication menu for the site.
Disable Anonymous and enable Windows Authentication
Add the following to the ConfigureServices method:
//using Microsoft.AspNetCore.Server.IISIntegration;
services.AddAuthentication(IISDefaults.AuthenticationScheme)
For the APIs or action controllers that you want to secure, decorate them with [Authorize] attribute, then you get the logged in user using HttpContext.User.Identity.Name. Use [AllowAnonymous] on actions that you want to allow access.
In case you want to secure and allow access on the same api, then you need to provide your own implementation of the Authorization filter.
For more details check this link
I'm sure I'm probably missing something fairly basic, but is there a way to expose ApiController to be accessed without Authorization? Removing [Authorize] or adding [AllowAnonymous] doesn't seem to help.
namespace Backend.Controllers
{
[MobileAppController, Authorize]
public class RebuildLocalDatabaseCheckController : ApiController
{
[HttpGet, Route("api/LastLocalDatabaseRebuildRequest")]
[AllowAnonymous]
public JToken Get()
{
return JToken.FromObject(DateTimeOffset.Now);
}
}
}
This is for an Azure Mobile App that I've setup for a UWP app. I do want everything else to require authorization, and that part is working fine. I just want to expose this part without needing login from the user.
I have checked your code on my local side and it could work as expected.
For anonymous access, you need to remove the Authorize from your RebuildLocalDatabaseCheckController class for all actions or specify the AllowAnonymous for your specified action. For more details about custom API in azure mobile apps project, you could refer to adrian hall's book here.
Additionally, if you app has been deployed to azure mobile app, for allowing anonymous against your custom API, you need to disable App Service Authentication or enable it with Allow Anonymous requests (no action) as follows:
Additionally, here is a blog about Architecture of Azure App Service Authentication / Authorization, you could refer to it for better understanding of app service authentication / authorization.
I have build a MVC 5 application. Now is time to publish it to production.
The infrastructure consist of two servers: one that can be accessed on internet and the other that is accessed only in intranet.
Server is Windows Server 2012 R2 and uses IIS 8.5 to host the application.
The application consist of backend and frontend. Backend's functionality are accessible only with login.
The problem that I want to solve is: How can I prevent login to the application from Internet and allow it only in intranet?
I think that might be some configuration on web.config to prevent opening of login page.
I have read a lot articles, but all the results are to prevent pages of unauthorized users. In my case login controller has [AllowAnonymous] attribute and it can be accessed without authorization.
If it can be done with a simple configuration in web.config I am going to add a key in web.config to keep track where it is the server on internet or the server on intranet like:
//for server accessible on intranet
<add key="serverType" value="PUBLIC"/>
// or
//for server accessible on intranet
<add key="serverType" value="Private"/>
And in the corresponding controller for login I will check for the value and if it is ConfigurationManager.AppSettings["serverType"] == "PUBLIC" I will redirect it to site public home page.
Does this solution have any security issue?
In this case, I would change the config with transforms or Parameterization (I prefer this one) to use the appropriate authentication type. For example, Anonymous for public and Windows/Forms Auth for private. You would have a transform/parameterization for each server type.
The following posts provide more specifics around ASP.NET security.
https://msdn.microsoft.com/en-us/library/3yfs7yc7.aspx
https://msdn.microsoft.com/en-us/library/7t6b43z4.aspx
http://weblogs.asp.net/jgalloway/archive/2012/08/29/simplemembership-membership-providers-universal-providers-and-the-new-asp-net-4-5-web-forms-and-asp-net-mvc-4-templates.aspx
I'm new to Web API, HTTP and security in general, but I just want to know if this is possible: for a controller to relax security requirements when the HTTP request originated from within the local area network.
My particular application has very low security requirements for clients inside the firewall. For instance, I want internal client apps to be able to make requests of controller actions marked with [AllowAnonymous] so that they don't need to deal with OAuth, etc (which just seems like complete overkill for my internal use scenario).
However, if the same controller actions are available to the public Internet, of course strict security requirements should apply.
Can security be handled differently based on origin? Or is the standard practice to expose both a public-facing and an Internal API?
When you use the [AllowAnonymous] attribute on your controller or action, you tell ASP.NET that it should not check the user's identity at all. That is not what you want for users coming from the internet.
You could remove the [Authorize] attribute from your controller and manually check inside the action if the user is authenticated using:
if (User.Identity.IsAuthenticated || IsLocalUser())
{
// action implementation
}
You can implement this check in a custom authorization attribute.
This still leaves you with the task to determine whether the user is local or coming from the internet. You could check the client IP-address to determine this of course.
Another option would be to enable both Windows authentication and bearer scheme authentication if your local users are part of an Active Directory domain.
Users from your intranet could use Windows authentication to talk to the service, while internet users need to bring a JWT token. This would only work if the client application for users coming from the internet is different than for local users.
DISCLAIMER: I've never tried this last option.
Identifying a request as one from "inside the firewall" isn't always as simple as just investigating the IP address. Although, this may work for you now, it may make it difficult to move environments or modify the environment without affecting application logic.
I would recommend developing a simple middle layer application that simply has the job of calling your main application with enough authorization data to handle security in the same context as your regular app, but this middle layer would in itself not be authorized. You will then just have to make sure that this app is not accessible to users outside of the firewall.
We're developing a web-api back-end ... implemented some calls, done in c# with sqlserver etc.
The backend will have more than one client apps. One would be a website, one would be a mobile app using phonegap, and hopefully we get more client apps using the service ...
We would like to setup the web-api project independent from the website stuff and NOT run into crossdomain issues.
Could a reverse proxy be used for this? Or maybe a vpn in azure? Any other suggestions?
Paul
There are a couple of things you need to do:
Set up your WebApi to support CORS (Cross Origin Resource Sharing). You can do this easily by installing the Cors Nuget package from Microsoft:
Install-Package Microsoft.AspNet.WebApi.Cors
Then you need to enable cors early in the application lifetime, for example in the Application_Start section of global.asax:
GlobalConfiguration.Configuration.EnableCors();
See this link for more details: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api
I see that you are concerned about security. Good. Then you may need to do two more thing to get your CORS to play nicely.
First, you should really look into creating a custom Cors policy provider (see the link above). Since you are hosting your WebApi in Azure, make it easy to configure the allowed origins. This should be a whitelist of only the websites you want to allow on your webapi.
Second, I assume that your user is authenticated on the website. I also assume that the way you call the WebApi is via jQuery or some other provider that uses jQuery as a transport (such as BreezeJS). To pass on the authentication details to your WebApi, jQuery needs to know that it should do that. The easiest way to do this is to set a global setting on your website:
$.ajaxSetup({ crossDomain: true, xhrFields: { withCredentials: true } });
A good tip for knowing exactly what goes wrong (because from experience, something will), is to use Chrome to test. Then you can open this link and see all the details of what is happening on the wire: chrome://net-internals/#events
Happy coding! :)
Create a Cloud Services (Web Role - MVC 4 web application), then deploy your webapi to there. Just make sure your webapi handle CORS issues, so you can call the api from both clients.
More info:
Enabling Cross-Origin Requests in ASP.NET Web API
PS: I'm working in a project that works exactly as you said. A website and mobile app as clients of my webapi on azure and we are doing like this. It's working pretty well.