How to intercept a non SSL connection in onAuthorization method? - asp.net-mvc

I am using MVC4, ASP.NET 4.5, C#
I want to add code to my onAuthorization method in global.asa to identify whether the connection is SSL or not, if not then to issue a permanent redirect to a SSL domain. I am using another domain that is SSLed.
In my Login controller I have code along the lines of :
[HttpGet]
public virtual ActionResult LogOn(string Error="")
{
if (Request.IsSecureConnection)
{
return View(viewModel);
}
else
{
return RedirectPermanent("https://www.mysslapp.com/logon");
}
}
I want to add this same functionality to the onAuthorization method so that when actions, covered by the [authorize] filter are called then they must also be accessed by a SSL connection. So I believe my global.asa code needs changing. However it will not accept "Request.IsSecureConnection", as the context is different.
My "pseudo" Global.asa onAuthorization routine is:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
RedirectPermanent("https://www.mysslapp.com/logon");
}
}
The above will not work, but it describes what I am trying to achieve. I would appreciate help on how I need to change the above code to make it work, such that any "adventurous" use of urls, on a non SSL connection will automatically redirect to the SSL site logon page.
Thanks in advance.
EDIT1
Think I have the first bit:
filterContext.HttpContext.Request.IsSecureConnection
EDIT2
if (filterContext.HttpContext.Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
filterContext.Result = new RedirectResult("https://www.mysslapp.com");
}

I think your main issue is that you have 2 separate concerns and you are trying to achieve both in one go. Your 2 concerns are:
Making every URL of domain A 301 redirect to the same URL on domain B
Making domain B redirect all requests to HTTPS
The first one is really easy. Create a new IIS site for domain A, install the IIS rewrite module, add this web.config to the site, and then adjust your DNS (if necessary) to make the site live.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpRedirect
enabled="true"
destination="https://www.mysslapp.com$V$Q"
exactDestination="true"
httpResponseStatus="Permanent" />
<httpProtocol>
<redirectHeaders>
<!-- This is to ensure that clients don't cache the 301 itself -
this is dangerous because the 301 can't change when put in place
once it is cached -->
<add name="Cache-Control" value="no-cache"/>
</redirectHeaders>
</httpProtocol>
</system.webServer>
</configuration>
NOTE: The above configuration is for IIS 7.5. I am not sure if it will work on other versions of IIS.
Now none of the users of domain B will incur the performance hit of the redirect rule, so all is good.
For redirecting your users of domain B to HTTPS, you should not use 301. Why? Because not all browsers respond to 301.
You should also not only allow HTTPS on the domain, but allow both HTTP and HTTPS. Why? Because your users that type myssldomain.com will get an ugly error message instead of a fast redirect to you HTTPS protected site.
So the simple solution to making your whole site redirect to HTTPS is to use the RequireHttps attribute and register it as a global filter. The RequireHttpsAttribute uses a 302 redirect when a request comes in that is not secure.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RequireHttpsAttribute());
filters.Add(new HandleErrorAttribute());
}
}
As for the AuthorizeAttribute, you should leave that out of the equation altogether unless you need some customization that deals with authorization.

Related

Why isn't my authentication cookie being set in MVC 4?

I've got an MVC4 project that I'm working on. When a user's login credentials are valid, I call FormsAuthentication.SetAuthCookie() to indicate that the user is logged in. (I have it wrapped in a class so I can mock the Interface for my unit tests.)
namespace FlashMercy.Shared.Security
{
using System;
using System.Web.Security;
public class Auth : IAuth
{
public void SetAuthCookie(string userId, bool remember)
{
FormsAuthentication.SetAuthCookie(userId, remember);
}
public void Signout()
{
FormsAuthentication.SignOut();
}
}
}
In the debugger, I can confirm that the .SetAuthCookie(userId, remember) line is executing, and userId is populated.
Then, I have a custom authorize attribute to check that the user is logged in:
namespace FlashMercy.Shared.Security
{
using System.Web.Mvc;
public class FlashMercyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new RedirectResult("/");
}
}
}
}
When I debug the application, the filterContext.HttpContext.User.Identity.IsAuthenticated is false even after I've supposedly set the auth cookie. Also, filterContext.HttpContext.User.Identity.Name is empty. I'm not sure what I'm missing here.
Update
If you care to look at the whole source, it's available on GitHub: https://github.com/quakkels/flashmercy.
Problem with your code is that you are using FormsAuthentication, but you didn't add it to web.config. Your web.config should have such section:
<system.web>
<authentication mode="Forms"></authentication>
...
</system.web>
Based on this Mode Asp.Net understand what authentication mode it should use, e.g. Forms, Windows, etc. And without settings it to Forms value - FormsAuthenticationModule just ignores .ASPXAUTH cookie from the request.
PS. I've downloaded your code, and with correct authentication section in web.config it works fine and updates HttpContext.User.Identity.IsAuthenticated to true.
The problem is that you only set the authentication cookie but do not have anything that load it.
It's forms authentication that uses that cookie. So you either have to activate forms authentication or you'll have to load it yourself.
filterContext.HttpContext.User.Identity.IsAuthenticated is false even after I've supposedly set the auth cookie.
This will always be the case if you do not redirect after SetAuthCookie(). The ASP.Net pipeline is in charge of authorizing the user (most of the time before we write code) in the AuthenticateRequest. Setting a Cookie does not update the current User.Identity, this requires code that has already been executed. Just make sure anytime you SetAuthCookie() you immediately redirect (server side is fine) to another URL (probably should anyway, its a good way to seperate logging in a user, and what they should do next SRP).

MVC 5 prevents access to content via Iframe

Ever since the upgrade from MVC4 to MVC5, I have noticed an extra server header added to my web pages:
X-Frame-Options: SAMEORIGIN
I understand security benefits of adding this tag, but one of the pages is meant to be included inside an iframe from other projects (on other domains), this extra header is preventing this.
I have verified it is not the hosting IIS7 server that is adding the header, and when I downgraded back to MVC4 - the header is gone.
Does anyone know how to remove this default from MVC5?
MVC5 automatically adds the HTTP header X-Frame-Options with SAMEORIGIN. This prevents your site from being loaded into an iframe.
But we can turn this off in Application_Start in the Global.asax.cs.
Example
protected void Application_Start()
{
AntiForgeryConfig.SuppressXFrameOptionsHeader = true;
}
Update
I have written a post about this MVC5 prevents your website being loaded in an IFRAME
Try something like this in Global.asax:
protected void Application_PreSendRequestHeaders(object sender, EventArgs e)
{
HttpContext.Current.Response.Headers.Remove("X-Frame-Options");
}
EDIT:
Look at answer of Colin Bacon. It is more correct than mine.
In short - don't remove this header if you don't want to run your site in IFRAME because it will open forgery vulnerability. But if you still want to remove it - use AntiForgeryConfig.SuppressXFrameOptionsHeader = true; in Application_Start, it is more cleaner way for doing this.
If you want a little more flexibility, here's an ActionAttribute that adds/removes headers based on a whitelist. If the referrer isn't in the whitelist, then the SAMEORIGIN header is left in place. I was going to paste the code, but SO complains about the length.
https://long2know.com/2016/06/asp-net-anti-forgery-xframe-options/
Personally, I don't think it's a good idea to disable the X-Frame-Options across the whole site.I've created an ASP.NET MVC filter which removes this header and I simply apply this filter to the portions of the site that are used in iFrames e.g. widgets.
public class AllowDifferentOrigin : ActionFilterAttribute, IActionFilter
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Headers.Remove("X-Frame-Options");
base.OnResultExecuted(filterContext);
}
}
Here is a replacement Extension method for the HtmlHelper class. It will first clear all X-Frame-Options headers and then add back a single X-Frame-Options header normally added by the built-in AntiForgeryToken method.
This technique respects the SuppressXFrameOptionsHeader setting, but has the downside of removing all previously added X-Frame-Options headers, even those with values other than SAMEORIGIN.
public static MvcHtmlString AntiForgeryTokenSingleHeader(this HtmlHelper html)
{
string token = AntiForgery.GetHtml().ToString();
HttpResponseBase httpResponse = html.ViewContext.HttpContext.Response;
httpResponse.Headers.Remove("X-Frame-Options");
if (!AntiForgeryConfig.SuppressXFrameOptionsHeader)
{
httpResponse.AddHeader("X-Frame-Options", "SAMEORIGIN");
}
return new MvcHtmlString(token);
}

ASP.NET MVC - Allow internal anonymous users, require windows authentication on external

Is it possible to setup Authorization based on the zone of the request? Basically it is an intranet type application, with only little sensitive information.
If the request is performed from within the organization, it is fine to allow anonymous users.
However if it is an external request, they should get the 401 Authorization challenge.
External requests are coming from a single firewall, so an IP/IP range should be fine to specify if it is an external or internal request.
Currently it is configured for Windows authentication in the web.config file.
<authentication mode="Windows" />
<authorization>
<deny users="?" />
</authorization>
It would be easier to handle this rule directly at your firewall.
As an alternative you could configure IP Security at your IIS level and filter by client IP.
But if you have no control over the firewall you could write a custom Authorize attribute that will check the incoming IP address and allow/deny the request:
public class IpBasedAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var ip = httpContext.Request.UserHostAddress;
return IsAllowed(ip);
}
private bool IsAllowed(string ip)
{
// TODO: do your checks here and return true or false
// depending on whether the IP address is allowed to
// access the application or not
throw new NotImplementedException();
}
}
and then you could either decorate individual controllers/actions with this attribute or register it as a global authorization attribute if you want it to apply to all requests:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new IpBasedAuthorizeAttribute());
}

best way to redirect .cfm locations to new locations .net mvc 3 IIS 7.5

I am trying to determine the best way to redirect a set of files I have from an old site (coldfusion) to new locations on my new site (asp.net mvc 3).
I want to redirect these pages with a 301 status to let engines know that this is a permanent change.
Currently I have in my web.config a custom errors section set up to redirect any 404 to the home page, which works great for all of the old links that are no longer in service, but it's sending a 302 status which I don't want and it's sending all my redirects to home thereby not giving me the SEO that was getting from my old links.
I thought about just adding .cfm as a module mapping in IIS to my .net Isapi and creating all of my pages as cfm with a redirect by adding headers, but then realized that'd be a LOT of work...
is there another "easier" solution to achieve this?
Yes, HttpHandler. in your web.congig you register it to handle all *.cfm URLs ( http://msdn.microsoft.com/en-us/library/46c5ddfy%28v=vs.71%29.aspx ), and implementation will be just your 301 redirect. 10 lines of code at most.
using System.Web;
public class CfmHandler : IHttpHandler {
public void ProcessRequest(HttpContext context) {
// redirect here, you have HttpContext ready
}
public bool IsReusable {
// To enable pooling, return true here.
// This keeps the handler in memory.
get { return true; }
}
}
More at : http://msdn.microsoft.com/en-us/library/5c67a8bd%28v=vs.71%29.aspx

How to step out from https to http mode in asp.net mvc

I have made my Login page as Https enabled by adding the attribute [RequireSSL] on controller Action and it works fine. But after successful login it remains in https environment, however the page is non https page.
Can anybody give me workaround how to step out from https to http mode?
Any help in this regard will be greatly appreciated.
You basically need to do the opposite, which is have a [DoesNotRequireSSL] attribute, which effectively does the opposite of the {RequireSSL] attribute, i.e., redirect to http protocol
public class DoesNotRequireSSL: ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var response = filterContext.HttpContext.Response;
if (request.IsSecureConnection && !request.IsLocal)
{
string redirectUrl = request.Url.ToString().Replace("https:", "http:");
response.Redirect(redirectUrl);
}
base.OnActionExecuting(filterContext);
}
}
Also, if you would like to ensure that multiple pages have this behaviour, you can set up a base controller, from which all your non-http controllers can inherit from so you dont have to worry about having to repeat yourself for every page which needs this.
CAUTION: I had a similar question. One important thing I learnt was that your auth cookie will be sent over plain text after switching back to HTTP. See this.
CAUTION 2 : Don't forget to consider the dreaded You are about to be redirected to a connection that is not secure message
If you're writing a bank application you need to be real careful - and also realize the increasing number of users on public wifi connections that could well [esily] be funneled through some sneaky proxy. Probably a much bigger concern for mainstream sites but a concern for us all to be aware of.
See also my other question (no answers at time of writing - but then I only just asked it!)
I know this is quite an old question, but many of the links presented above are dead and this code addresses it for ASP.NET MVC 5 by making some slight modifications to the RequireHttpsAttribute that is included in the System.Web.Mvc namespace:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ForbidHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.HttpContext.Request.IsSecureConnection)
{
HandleHttpsRequest(filterContext);
}
}
protected virtual void HandleHttpsRequest(AuthorizationContext filterContext)
{
// only redirect for GET requests, otherwise the browser might not propagate the verb and request
// body correctly.
if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The requested resource can only be accessed *without* SSL.");
}
// redirect to HTTP version of page
var url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
}
The code comes from this article, which briefly discusses some of the security concerns of forcing users to redirect from HTTPS to HTTP as well.

Resources