I have some admin pages which must be extra secure e.g. accessible only by a local hostname and from a limited range of IPs. How to achieve that regarding than requests can be forged?
I solve this via Authorization filter. It may seem to be lame but it worked for my project (hosted on IIS behind a firewall)
public void OnAuthorization(AuthorizationFilterContext context)
{
var allowedHosts = "127.0.0.1,192.168.1.15";
if (
(allowedHosts.Contains("127.0.0.1")
&& context.HttpContext.Connection.RemoteIpAddress.ToString() == "127.0.0.1"
&& !string.IsNullOrEmpty(context.HttpContext.Request.Headers["X-Forwarded-For"]))
||
(!(context.HttpContext.Request.Host.Host.Contains("localhost"))
&& !allowedHosts.Contains(context.HttpContext.Connection.RemoteIpAddress.ToString()))
)
{
log();
context.Result =new RedirectResult("/404");
}
}
Have you checked the policies?
Maybe you can make an implementation like this:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class LocalHostOnlyAttribute : Attribute, IAuthorizationFilter
{
[Authorize]
public void OnAuthorization(AuthorizationFilterContext context)
{
// restrict if hostname is not localhost.
if(context.HttpContext.Request.Host.Host !="localhost"){
// return UnauthorizedAccess razor page/view if not allowed.
context.Result = new ViewResult() {ViewName = "UnauthorizedAccess" };
}
}
}
Be warned i'm not sure if this is easily spoofable, so it might create a security risk.
Especially when your request is forwared from nginx or another reverse-proxy. The host will always be localhost!
Maybe check request ip equals the ip of the server (but even this i think is a small security risk).
Related
After a recent security scan, the info sec team said they do not like the fact they can save the .AspNet.ApplicationCookie value, and use it again afterwards allowing the user access to the site.
After reading around, I understand this is standard behaviour but I have to find a way of completely killing a session upon signing the user out.
My understanding is a little thin here so my searching is bringing up very little. Is there a way of going about this?
A late reply, but for those that come across this:
We handled this by adding a custom attribute that validates a thumbprint. We use the attribute for any page behind a login.
The following is a rough example of how this is achieved.
The thumbprint is created at sign in and added to cache:
private void OwinSignIn(tblUser user)
{
var thumbPrint = Guid.NewGuid();
var claims = new List<Claim>
{
....
new Claim(ClaimTypes.Thumbprint, thumbPrint.ToString())
};
MemoryCache.Default.Set(thumbPrint.ToString(), true, new CacheItemPolicy() { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(60) });
}
The attribute then looks for this thumbprint and acts accordingly:
public class ValidateThumbprint : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
var identity = (ClaimsIdentity)filterContext.HttpContext.User.Identity;
var thumbPrint = identity.Claims?.Where(s => s.Type == ClaimTypes.Thumbprint).First().Value;
if (thumbPrint != null)
{
if (MemoryCache.Default.Contains(thumbPrint))
{
return;
}
}
// handle invalid thumbprint
}
I am not sure if this is the best way and most secure way, but it does prevent saving and reusing the cookie after logging out.
I have a simple ASP.Net Core 2.1 MVC app and in one of the controllers, I would like to implement an action that only accepts requests from local (i.e. request originates from 127.0.0.1, or from the same address as the server's IP).
I've been looking for a filter in ASP.Net Core that is suitable for this purpose but can't seem to find one. I can use an AuthorizeAttribute, e.g. [Authorize(Policy = "LocalOnly")]
and registering the corresponding policy in ConfigureServices in Startup:
services.AddAuthorization(options =>
{
options.AddPolicy("LocalOnly", policy =>
{
policy.RequireAssertion(context =>
{
if (context.Resource is AuthorizationFilterContext mvcContext)
{
return mvcContext.HttpContext.Request.IsLocal();
}
return false;
});
});
});
where IsLocal() is an extension method of HttpRequest.
However I don't think this is the right way to do it -- what I'm trying to do is not actually authorization, and since I don't have authentication in my program, the error produced isn't correct either.
Is there a simple and legit way to do what I want with filters? Or is this actually something that should be done in the action logic in controllers? Or perhaps this whole idea of checking for local request isn't very correct to begin with?
Thanks a lot for any help.
Do it as ASP.NET Core middleware.
In the easiest case with a app.Use(...) method.
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
if (!context.Request.IsLocal())
{
// Forbidden http status code
context.Response.StatusCode = 403;
return;
}
await next.Invoke();
});
}
}
The delegate calls return on local requests, stopping the middleware pipeline here.
But I'm not 100% certain what you are trying to archive.
Do you want the service only callable from your internal network? The way easier way to do that would be to use docker containers, add the services which need to communicate to each other to the same network and only expose the application to outside the container which really need to communicate with the outside world.
The extension method of IsLocal() should look as the following:
public static class HttpRequestExtensions
{
public static bool IsLocal(this HttpRequest req)
{
var connection = req.HttpContext.Connection;
if (connection.RemoteIpAddress != null)
{
if (connection.LocalIpAddress != null)
{
return connection.RemoteIpAddress.Equals(connection.LocalIpAddress);
}
else
{
return IPAddress.IsLoopback(connection.RemoteIpAddress);
}
}
// for in memory TestServer or when dealing with default connection info
if (connection.RemoteIpAddress == null && connection.LocalIpAddress == null)
{
return true;
}
return false;
}
}
To determine whether request is local, we can check if the RemoteIpAddress and LocalIpAddress on the connection are the same, or, in case the local IP is unknown for some reason, if the RemoteIpAddress is a loopback address.
You can read more about it in strathweb article.
Note that it will not work if connection.RemoteIpAddress == "::1".
If you do want it to work change it to:
Request.Host.Value.StartsWith(“localhost:”)
So I have a C# MVC app using Identity for its authentication. I now have a need to expose a few things via Web API to some of my clients. Instead of building a separate app, project, deployment... I've simply added an API Controller to my existing project. To keep things simple for my clients, I've decided to use Basic Auth, opting rather to force my clients into using SSL connections to my API.
I've followed this very useful tutorial to implement the Basic Auth in my API:
http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/
Problem is, that their instructions take over Auth for the entire app...
I need my MVC app to keep using the Identity Auth that it is currently using and hopefully roll my own custom attribute (like [APIAuthorize]) so that it only applies to my API Controller.
I can probably hack around and try to get this to work, but as this is concerning security, I decided to ask for some pro help on how to best implement this. Specifically, I need to know 1) what do I do in my Global.asax (if anything) as the above URL suggests I do this:
protected void Application_Start()
{
GlobalConfiguration.Configuration.MessageHandlers
.Add(new BasicAuthMessageHandler(){
PrincipalProvider = new DummyPrincipalProvider()
});
//...
}
But again, this would take over the Authentication to the entire app... 2) What do I need to do in my custom auth attribute to make all of this work seamlessly.
And of course, if there's a better way to do all of this (without creating a separate app or increasing the implementation difficulty to my clients) then I'm all ears.
I us a filter attribute to adorn the actions i wanted to expose to Simple Auth. I cant remember where i got this code from (probably stackoverflow i just don't have the link so i cant claim credit for it)
public class BasicHttpAuthorizeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (Thread.CurrentPrincipal.Identity.Name.Length == 0)
{
// Get the header value
AuthenticationHeaderValue auth = actionContext.Request.Headers.Authorization;
// ensure its schema is correct
if (auth != null && string.Compare(auth.Scheme, "Basic", StringComparison.OrdinalIgnoreCase) == 0)
{
// get the credientials
string credentials = UTF8Encoding.UTF8.GetString(Convert.FromBase64String(auth.Parameter));
int separatorIndex = credentials.IndexOf(':');
if (separatorIndex >= 0)
{
// get user and password
string passedUserName = credentials.Substring(0, separatorIndex);
string passedPassword = credentials.Substring(separatorIndex + 1);
SimpleAES crypto = new SimpleAES();
string userName = crypto.DecryptString(ConfigurationManager.AppSettings.Get(Constants.SIMPLEUSERNAME));
string password = crypto.DecryptString(ConfigurationManager.AppSettings.Get(Constants.SIMPLEUSERPASSWORD));
// validate
if (passedUserName == userName && passedPassword == password)
{
Thread.CurrentPrincipal = actionContext.ControllerContext.RequestContext.Principal = new GenericPrincipal(new GenericIdentity(userName, "Basic"), new string[] { });
}
}
}
}
return base.IsAuthorized(actionContext);
}
}
Then i use it as so
[BasicHttpAuthorize]
public HttpResponseMessage MyExposedSimpleAuthAction()
I have a C#/MVC4 site hosted on Azure as a web role located at http://www.equispot.com. During a check on Google for some searches related to my site, I came across a search result that links to this page:
http://equispot.cloudapp.net/horses-for-sale/quarter-horses/13
Note the difference in the domain name. Now, I have a canonical tag already (view the source on the cloudapp.net link and you can see the canonical rel tag points to the main site at http://www.equispot.com).
Since that's the case, why would Google have indexed the page at the cloudapp.net domain? I recently noticed a drop in my SERPs and I'm wondering if this is part of the reason (I migrated to Azure about the same time as the SERP change). It may be unrelated but still...
How can I prevent these pages from being indexed by Google or how can I prevent my Azure web role from responding to anything except www.equispot.com and equispot.com? When I had this hosted on premise, I just configured IIS to respond only to my domain (my previous provider produced some dupe content for some reason as well).
You can simply check to make sure that the host the application is running under is the domain name you want. If it is not, then simply do a 302 redirect to the domain name you want.
There are several places where you can inspect the request and do the redirect:
- Global.asax
- Custom module
- Override the OnActionExecuting for action methods
I couldn't find a straightforward way to do this using hostHeader configuration in the ServiceDefinition.csdef file so I rolled my own RedirectInvalidDomainsAttribute class to perform a 301 (Moved Permanently) redirect back to my main site during a request for an invalid domain. In case anyone else runs into the same problem, here's the code:
App_Start/FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RedirectInvalidDomainsAttribute());
}
RedirectInvalidDomainsAttribute.cs
public class RedirectInvalidDomainsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var url = filterContext.HttpContext.Request.Url;
if (url == null) return;
var host = url.Host;
if (host.Contains("equispot.com") || host.Contains("localhost")) return;
string subdomain = GetSubDomain(host);
Guid guid;
if (Guid.TryParseExact(subdomain, "N", out guid))
{
// this is a staging domain, it's okay
return;
}
// Invalid domain - 301 redirect
UriBuilder builder = new UriBuilder(url) {Host = "www.equispot.com"};
filterContext.Result = new RedirectResult(builder.Uri.ToString(), true);
}
// This isn't perfect, but it works for the sub-domains Azure provides
private static string GetSubDomain(string host)
{
if (host.Split('.').Length > 1)
{
int index = host.IndexOf(".");
return host.Substring(0, index);
}
return null;
}
}
I have been struggling with redirecting to SSL page code for a while for a ASP.Net Application. I still have not found a good solution. The problem is that I want to have the code be disabled if I am on localhost for example when I am debugging or coding but I can not get it to work. Another issue is that it has to redirect to another url so http://www.xx.com should redirect to https://Secure.xxx.com. Any ideas?
If you want the ssl property to be set at the page level, you should create a custom attribute.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class HttpsAttribute : Attribute{}
[HttpsAttribute]
public partial class PageSecured: YourCustomBasePage {}
Now the base page YourCustomBasePage can check if this attribute is set:
protected override void OnInit(EventArgs e){
if(!Request.IsSecureConnection){
HttpsAttribute secureAttribute = (HttpsAttribute)Attribute.GetCustomAttribute(GetType(), typeof(HttpsAttribute));
if(secureAttribute != null){
//Build your https://Secure.xxx.com url
UriBuilder httpsUrl = new UriBuilder(Request.Url){Scheme = Uri.UriSchemeHttps, Port = 443};
Response.Redirect(httpsUrl.Uri.ToString());
}
}
}
To exclude your local machine from redirecting to HTTPS, you can use a config value in your web.config.
private bool HTTPSEnabled{
get{ return ConfigurationManager.AppSettings["HTTPSEnabled"] == null || Boolean.Parse(ConfigurationManager.AppSettings["HTTPSEnabled"]); }
}
and you add the check to the first condition
if(!Request.IsSecureConnection && HTTPSEnabled)
I use this library in production. I prefer it to setting attributes because it is configuration driven, and configurable to a fine level. That being said, I'm not sure if it can redirect to a subdomain. I'm not even sure why you would want that to happen.