RequireHttps vs deriving from RequireHttpsAttribute - c#

I'm trying to implement HTTPS on selected pages of my site. Using the attribute RequireHttps works but causes problems testing as we don't have a cert installed locally.
The solution I'm looking for will need to ignore localhost and ignore one test server while working on our second test server where we do have a cert in place.
Some further background on this. The aim is to move the site gradually to https. It's an ecommerce site so obviously portions are already secure and I know that for many reasons moving the entire site to secure is a good thing. I also know that once you move from Page A to Page B where B is secure then it won't go back to HTTP when you move back to A, that's fine.
I want to move the site in stages just in case there are problems with things like mismatched content, site maps, SEO, google ranking etc.
Some of the various solutions I have tried - I've implemented a class derived from the RequireHttps attribute as follows:
public class CustomRequireHttps : RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.Url != null && (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)
&& !String.Equals(filterContext.HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase)
&& !filterContext.HttpContext.Request.Url.Host.Contains("localhost")
&& !filterContext.HttpContext.Request.Url.Host.Contains("testing")))
{
base.HandleNonHttpsRequest(filterContext);
}
}
}
And have applied this attribute to one page but it hasn't worked as intended, it either applies HTTPS to all pages on the site or doesn't work at all.
I have also tried this solution which works but only on localhost and not on the two test servers:
#if !DEBUG
[RequireHttps]
#endif
Then I tried overriding the OnAuthorizartion method like so:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.HttpContext != null && filterContext.HttpContext.Request.IsLocal)
{
return;
}
base.OnAuthorization(filterContext);
}
It worked locally but once I got it onto the server with the test cert suddenly every page is HTTPS which I do not understand as I've only used this derived attribute on one page.
So, what I'm looking to achieve is to implement HTTPS on a select number of pages on my site. This HTTPS request needs to be ignored on localhost and the first test server but, it needs to NOT to be ignored on the second test server which has a cert.
So far it either doesn't work at all or is on every page on the site.
However, and this is the kicker, If I use the RequireHttps attribute it works perfectly on the second test server but causes problems on all servers without a cert. By 'works perfectly' I mean it implements HTTPS only on the pages where I've used that attribute and does not suddenly switch all pages to secure.
Any ideas what I'm doing wrong here?

There can be a lot going on, for example when your links are local, when a switch is made to HTTPS, all pages are HTTPS (not applying require HTTPS doesn't switch back to HTTP). From a security standpoint, you should serve all pages from HTTPS when you need it for a subset of pages (otherwise, you might share secure cookies / login tokens over unencrypted HTTP). So probably your attribute is applied, and all subsequent requests are served over SSL.
Secondly, testing on localhost request uri will serve the page over HTTP on your second server. My opinion to solve this problem is to create a switch in your web.config if the pages should be served over HTTPS. Check this switch in your global filterConfig:
public static class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var useSsl = Convert.ToBoolean(ConfigurationManager.AppSettings["useSsl"]);
if (useSsl )
{
filters.Add(new RequireHttpsAttribute());
}
}
}

Related

Request.UrlReferrer is easily bypassed and not reliable to use as guard?

I was trying to make a guard for some action so it is not accessible unless the request comes from a certain host. Here is the sample code.
public ActionResult test()
{
if (Request.UrlReferrer == null || Request.UrlReferrer.Host != "mydomain.com") { return Content("Blocked!"); }
else { return Content("Authorized!"); }
}
Everything seems to work well until I went to mydomain.com "typed the link in the addressbar" , opened the browser console and typed
window.location.href = "https://domainholdingthatacion.whatever/ActionRoute/test"; //trying to get unauthorized access
It worked! It enters the else branch. I need your input because I have no idea if I am using it wrong as Request.UrlReferrer is not meant to be used for that or It is inherently vulnerable.
UrlReferrer is not safe to use for authorization. Any browser or client can decide to set the referrer url to whatever they want. Additionally some browsers block refuse respecting this header (though that's mostly only true between different domains) for privacy reasons.
There's the Authorize annotation specifically for this.
Authorize attribute in ASP.NET MVC
https://www.red-gate.com/simple-talk/dotnet/asp-net/thoughts-on-asp-net-mvc-authorization-and-security/
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-3.1

Does anyone else have issues losing session variables in Firefox but not in Chrome or IE?

We are experiencing some issues with returning variables stored in session, which in turn is causing our controller authorization code to redirect some users back to the login page. The users only seem to be having this issue with the Firefox browser and the application works normally for them when using Chrome or Edge. It's almost as if their session is being dropped while navigating through the site (mostly immediately after login while being redirected to the home page, however sometimes the user can get through few links before being kicked out).The issue also only happens after if is running in our test or production sites, reproducing the issue locally troublesome. I was wondering if anyone else has experience this type of issue before and if they were able to find a work around or resolve it. Our sessionstate is set to InProc.
Based on other threads I have read on here I have tried the following so far:
Ensured that Session_Start is in Global.asax, Tried setting a "dummy" session variable in the Session_Start method "Session["Init"] = 1", added this to our web.config file under the section
, as well as some other solutions I have found that turned out not to resolve our issue.
This is the section of code in question.
public class AuthorizeSessionAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
HttpSessionStateBase session = filterContext.HttpContext.Session;
Controller controller = filterContext.Controller as Controller;
if (controller != null && session != null && session["CurrentRole"] == null)
{
controller.HttpContext.Response.Redirect("~/Account/Login");
}
base.OnAuthorization(filterContext);
}
}
I just can't see any obvious reason as to why this would only happen in Firefox but work correctly in Chrome or IE. Any input on this would be appreciated. Thanks.

How to prevent unauthorized child action to return 401

Problem
Long story short, how can you prevent unauthorized ChildActions to return a 401 code and return an empty result instead.
Context
My application uses NTLM to authenticate on the network. I also want to handle anonymous identification, for non NTLM capable devices or if someone goes through some proxy.
One the the web application, there is controller/actions that absolutely require to be logged in to be viewable and some other that are viewable as anonymous. For the ones that authentication is mandatory where I initially used the following:
[Authorize()]
public class SomeController : BaseController
Which works as expected and returns a 401 Status Code (Unauthorized).
The problem starts on the page that allows anonymous. Because those pages have parts that uses RenderAction to render pieces that requires authentication.
On local developpement server on front end everything looks fine, but with IIS any page that have any small piece that requires authentication it returns 401 page. So I created a custom Authorize attribute:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public bool Ignore401ChildAction { get; set; }
public CustomAuthorizeAttribute()
: base() {
this.Ignore401ChildAction = true;
}
protected override void HandleUnauthorizedRequest( AuthorizationContext filterContext ) {
base.HandleUnauthorizedRequest( filterContext );
if( this.Ignore401ChildAction && filterContext.IsChildAction ) {
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
}
}
And then on frontend it seems to produce correct behavior with and without IIS. However in the backend, even if the page renders completly it returns a 401
So I added the following to my CustomAttribute instead of TrySkipIisCustomErrors
filterContext.HttpContext.Response.StatusCode = 200;
filterContext.Result = new EmptyResult();
In anonymous everything is fine, but when NTLM is enabled server doesn't request authentication when the controller doesnt have that CustomAuthorize attribute. It seems like its only requesting the crendetials when their mandatory instead of when they're available.
Thank you,
EDIT
After lot of searching, digging and fiddling, realized that true problem is that Anonymous identification will take precedence over Windows/NTLM. If Anonymous and Windows authentication are enabled, Windows will only execute if anonymous authentication fails.
SOLUTION(not perfect and kind of hackish but it works)
Add the following function to your BaseController then call from the views that do not mandatory require authentication, or simply call it from your _Layout view.
NOTE: Even though page will render correctly as anonymous, a 401 status code will still be returned.
[CustomAuthorize]
public ActionResult Auth() {
return new EmptyResult();
}

URL Routing across multiple subdomains

I find myself in a difficult situation. We're working on an ASP.NET MVC 2 application which is comprised of multiple sections. It is a design goal to have these sections span across several subdomains. Each subdomain will have its own controller.
The challenge is that our hosting provider's control panel allows two forms of redirection for subdomains, and neither of them seem to fit the bill. The choices are:
Redirecting to URL. Choice is given whether to redirect to an exact destination or a destination relative to the request URL.
Redirecting to a specific folder in my hosting space.
I'll try to illustrate the intended behaviour. Assuming the default route is {controller}/{action}/{id}, I'd like the URL http://subdomain.welcome.com/a/b be handled by the MVC Application like http://welcome.com/subdomain/a/b.
The URL redirection could solve this problem, except for the fact that the user sees a URL change occur in the browser. We don't want the client to see the redirection occur.
Redirecting to our MVC apps root folder doesn't work at all. The app doesn't pick up the request and a 4xx error gets passed back by IIS.
edit:
In the interest of finding an answer, I'll simplify this a bit. The "redirect to URL" doesn't do what I want so that leaves redirecting to a folder.
If I'm redirecting a subdomain to the root folder of my MVC App and IIS wont pick up the requests, is this a limitation of IIS or my provider?
Can you make your hosting website host headers respond to *.mydomain.com? Meaning, can your website take request for any sub domain of your primary domain? If so, then reference this post on how to handle subdomain routing in MVC apps and you should be good to go.
I would update the code in the post to this however, to make the code more succinct. In any case, make sure you have your 404 errors in place for people attempting to go to subdomains that don't exist.
public class ExampleRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var url = httpContext.Request.Headers["HOST"];
var index = url.IndexOf(".");
if (index < 0)
return null;
var subDomain = url.Substring(0, index);
var routeData = new RouteData(this, new MvcRouteHandler());
routeData.Values.Add("controller", subdomain); //attempts to go to controller action of the subdomain
routeData.Values.Add("action", "Index"); //Goes to the Index action on the User2Controller
return routeData;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//Implement your formating Url formating here
return null;
}
}
Not sure if this is overkill (this is actually used to serve pages from a zip file or resource file, etc), BUT... perhaps you could use a Virtual Path Provider?..
Implement a class that inherits from VirtualPathProvider, and register it in global startup like so:
HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
Then implement a class that inherits from VirtualFile and serve it from the GetFile() override in your virtual path provider implementation:
public override VirtualFile GetFile( string virtualPath )
{
if( IsVirtualFile(virtualPath) )
return new MyVirtualFile(virtualPath);
return base.GetFile(virtualPath);
}
Note: IsVirtualFile is a function you would have to implement, based on the rules you have regarding the URL format, etc.

Redirect away from HTTPS with ASP.NET MVC App

I'm using ASP.NET MVC 2 and have a login page that is secured via HTTPS. To ensure that the user always accesses those pages via SSL, I've added the attribute [RequireHttps] to the controller. This does the job perfectly.
When they have successfully logged in, I'd like to redirect them back to HTTP version. However, there isn't a [RequireHttp] attribute and I'm struggling to get my head around how I might achieve this.
The added (potential) complication is that the website when in production is hosted at the route of the domain, but for development and testing purposes it is within a sub directory / virtual directory / application.
Am I over-thinking this and is there an easy solution staring me in the face? Or is it a little more complex?
After a bit of digging, I went along the lines of rolling my own as there didn't appear to be a good built-in solution to this (as mentioned, there is a great one for MVC2 applications in the form of [RequireHttps]). Inspired by çağdaş's solution to this problem and I adapated to come up with the following code:
public class RequireHttp : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// If the request has arrived via HTTPS...
if (filterContext.HttpContext.Request.IsSecureConnection)
{
filterContext.Result = new RedirectResult(filterContext.HttpContext.Request.Url.ToString().Replace("https:", "http:")); // Go on, bugger off "s"!
filterContext.Result.ExecuteResult(filterContext);
}
base.OnActionExecuting(filterContext);
}
}
I can now add this to my Controller methods and it behaves (seemingly) as expected. If I redirect to the Index action on my controller from a HTTPS protocol, it will redirect to HTTP. It only allows HTTP access to the Index ActionResult.
[RequireHttp]
public ActionResult Index() {
return View();
}

Categories

Resources