RequireHttps and routing to https URL - asp.net-mvc-4

I am using the RequireHttps attribute on most of the actions in my User controller which handles the login and my secure https pages.
On my Home Page which is http I have a link to my login page as follows (MVC 4 Razor View):-
Login
The link correctly goes to the login page with an https address. However, when I look in the IIS log I see there are two entries for the login URL, one on port 80 and one on port 443.
Is this an issue I should be concerned about?
I know on my #Url.Action I could force https mode, but not sure if this is the best way. Plus this removes the port, which is annoying when using IIS Express in VS 2012. I'd then have to further extend the #Url.Action to include the hostname:port.
So I am just checking if (a) this should be a concern and (b) if it is a concern whether there are any other ways to forcing the URL to https.

Most tutorials will agree that by having a mixed mode site (both HTTP and HTTPS) you're defeating the purpose of SSL (having certain paths require SSL then switching back to a non SSL connection). Once you switch to HTTPS it's recommended that you force the user to stay using HTTPS for everything, at the very least until they logout. I have one site that uses HTTPS and once you hit the site, I just use a URL Rewrite rule to switch the user to HTTPS, and only HTTPS is allowed.
<rewrite>
<rules>
<rule name="Redirect HTTP to HTTPS" stopProcessing="true">
<match url="(.*)"/>
<conditions>
<add input="{HTTPS}" pattern="^OFF$"/>
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="SeeOther"/>
</rule>
</rules>
</rewrite>
Once you do this it's also recommended to set the authentication cookie to require HTTPS as well. For Forms authentication this is as simple as setting the following in your web.config.
<authentication>
<forms requireSSL="true" />
</authentication>
I would also recommend looking at some of the Headers that can be set to help browsers treat the site correctly, like using Strict-Transport-Security to signify that the site requires SSL. Keep in mind that while Headers are a great supplemental measure you can take, ultimately they are left for the browser to enforce and should not be relied on exclusively.
I recommend these steps because I don't suffer from the symptom you're describing and am hoping they'll help resolve the issue you're noticing in the logs.
AMMENDMENT: Oh and I also forgot to mention, HTTPS is a little more intensive to establish a connection with than plain old HTTP. Not much in the grand scheme of things but still it's something. I'd recommend you utilize HTTP Keep Alive so as to reduce some of that overhead.
UPDATE: Communicating over SSL doesn't imply that you are authenticated. It just means you're talking over a secure/encrypted connection.
Take your Amazon example lets say. If you visit the site, you'll like get just a normal connection over HTTP. You're aren't logged in you're just browsing the site. If you wanted you could switch to HTTPS and you'd still get the same site but you're not logged in yet. Now if you try to login you'll get redirected so that you talk over SSL (if you're not already) as noted by the HTTPS moniker. Even after you actually login you will still be communicating over SSL. Even if you try to manually switch to not using SSL while you are logged in by removing the S from the protocol part of the URL, it'll still send you back to using HTTPS. This is the correct way of doing it. It's generally suggested that you not return to a non encrypted session after authenticating. This is typically to avoid session hijacking since your authentication cookie would never be sent over plain HTTP. Make sure the resources you have on external sources, are on sites that you trust. External resources access while in a HTTP connection should also be over an SSL connection. Again just because you communicate over SSL doesn't mean you're logged into those sources. For my app is 100% access over SSL, but I also have Google analytics and Google maps integration on the site (obviously both are external to my domain). I just make sure that I talk to Google over SSL. I don't have to actually be logged into Google to use any of those things. The same goes for your external images. Just make sure your URL's used to reference those external images are defined using the HTTPS moniker so that it uses SSL.
UPDATE: The reason you're getting two hits in the log like that is because you're login link is being requested over HTTP, the Require HTTPS attribute hits first realizes you're not using SSL and redirects you back to itself with the HTTPS protocol. If you update your ActionLink URL you can get around this, but as you know it gets ugly.
Url.Action("Login", "User", null, "https", Request.Url.Host)

Add this to your global.ascx
protected void Application_BeginRequest()
{
if (!Context.Request.IsSecureConnection)
Response.Redirect(Context.Request.Url.ToString().Replace
("http:", "https:"));
}
This will cause all the requestes to be converted to https instead of http

I write an extension for Url.Action that generate the right protocol depending on the Attribute decooration. I let you check
public static class Extensions
{
public static string SecuredAction(this UrlHelper helper, string action, string controller)
{
bool requireSSL = false;
var route = System.Web.Routing.RouteTable.Routes.Select(r => r as System.Web.Routing.Route) .Where(r =>
r != null && r.Defaults != null && r.Defaults["controller"] != null && r.Defaults["controller"].ToString().Equals(controller, StringComparison.InvariantCultureIgnoreCase)
&& r.Defaults["action"] != null && r.Defaults["action"].ToString().Equals(action, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if(route == null)
return helper.Action(action, controller);
//Get the method and check if requiere ssl
MemberInfo method = route.DataTokens["TargetActionMethod"] as MemberInfo;
requireSSL = method.CustomAttributes.Any(a => a.AttributeType == typeof(RequireHttps)) || method.DeclaringType.CustomAttributes.Any(a => a.AttributeType == typeof(RequireHttps));
//Return the correct protocol
if(helper.RequestContext.HttpContext.Request.IsSecureConnection && !requireSSL)
return helper.Action(action, controller, null, "http");
if (!helper.RequestContext.HttpContext.Request.IsSecureConnection && requireSSL)
return helper.Action(action, controller, null, "https");
return helper.Action(action, controller);
}
}

Related

Need to make actionscript POST data over https

I have an application that was recently given an AWS certificate (and put in an ELB - classic I think).
The web application has a Flash movie that makes web calls (to same site URL) in order to fetch data using Zend Framework 1 models. The page in browser does not change.
When I request the site over https, all of the imported items have been changed over to https protocol, but when the Flash movie initializes, it makes non-secure requests over http.
It makes these non-secure requests when I load the site over http, or https.
The reason I mentioned the AWS ELB is because I was told that the ELB is doing some kind of redirect to port 80.
If I request the site over https, and immediately do print_r on $_SERVER array I am only seeing HTTPS as a REDIRECT key, and not seeing $_SERVER['HTTPS'] set, which I think is important.
In summary, the Flash movie, inside a Zend 1.12 site, is making POST requests over http, and I'd like it to make the same requests, but over https.
It is a very old Flash movie, and although I've opened the swf file with a decompiler, I do not know much about actionscript to see where (in the many code files) I'd be able to instruct the movie to call https instead of http.
My theory is that when the site is properly running as SSL/https that the flash movie may ?possibly? start making https calls since at the moment is "is" using the address bar URL, but there also could be that ELB redirect stuff happening that's gumming it up as well.
Update: I found (what appears to be) evidence that if https is detected in the URL it's given, that it will then make secure requests...
FILE: mx.rpc.remoting.RemoteObject
mx_internal function initEndpoint() : void
{
var chan:Channel = null;
if(endpoint != null)
{
if(endpoint.indexOf("https") == 0)
{
chan = new SecureAMFChannel(null,endpoint);
}
else
{
chan = new AMFChannel(null,endpoint);
}
channelSet = new ChannelSet();
channelSet.addChannel(chan);
}
}
Thanks,
Adam
I was able to restore the original/old unedited Flash SWF File, and instead modified the PHP Code that passes in a variable and value called "endpoint".
In the code sample I provided, it checks if endpoint has https in it (which I initially thought that it did).
I added code to modify the value of "endpoint" when HTTP_X_FORWARDED_PROTO was "https", sample below: (the $request->getBaseUrl() is from Zend Framework).
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$endpoint = sprintf(
'%s://%s%s',
$_SERVER['HTTP_X_FORWARDED_PROTO'],
$_SERVER['HTTP_HOST'],
$request->getBaseUrl()
);
} else {
// use existing (and working) value for endpoint
}
With that code in place, the FLASH movie loads in, and operates properly
whether site is loaded with http or with https

Prevent http page from redirecting to https page

I have a website (userbob.com) that normally serves all pages as https. However, I am trying to have one subdirectory (userbob.com/tools/) always serve content as http. Currently, it seems like Chrome's HSTS feature (which I don't understand how it works) is forcing my site's pages to load over https. I can go to chrome://net-internals/#hsts and delete my domain from Chrome's HSTS set, and the next query will work as I want without redirecting to an https version. However, if I try to load the page a second time, it ends up redirecting again. The only way I can get it to work is if I go to chrome://net-internals/#hsts and delete my domain from Chrome's HSTS set after each request. How do I let browsers know that I want all my pages from userbob.com/tools/ to load as http? My site uses an apache/tomcat web server.
(Just FYI, the reason I want the pages in the tools directory to serve pages over http instead of https is because some of them are meant to iframe http pages. If I try to iframe an http page from an https page I end up getting mixed-content errors.)
HTTP Strict Transport Security (or HSTS) is a setting your site can send to browsers which says "I only want to use HTTPS on my site - if someone tries to go to a HTTP link, automatically upgrade them to HTTPS before you send the request". It basically won't allow you to send any HTTP traffic, either accidentally or intentionally.
This is a security feature. HTTP traffic can be intercepted, read, altered and redirected to other domains. HTTPS-only websites should redirect HTTP traffic to HTTPS, but there are various security issues/attacks if any requests are still initially sent over HTTP so HSTS prevents this.
The way HSTS works is that your website sends a HTTP Header Strict-Transport-Security with a value of, for example, max-age=31536000; includeSubDomains on your HTTPS requests. The browser caches this and activates HSTS for 31536000 seconds (1 year), in this example. You can see this HTTP Header in your browsers web developer tools or by using a site like https://securityheaders.io . By using the chrome://net-internals/#hsts site you are able to clear that cache and allow HTTP traffic again. However as soon as you visit the site over HTTPS it will send the Header again and the browser will revert back to HTTPS-only.
So to permanently remove this setting you need to stop sending that Strict-Transport-Security Header. Find this in your Apache/Tomcat server and turn it off. Or better yet change it to max-age=0; includeSubDomains for a while first (which tells the browser to clear the cache after 0 seconds and so turns it off without having to visit chrome://net-internals/#hsts, as long as you visit the site over HTTPS to pick up this Header, and then remove the Header completely later.
Once you turn off HSTS you can revert back to having some pages on HTTPS and some on HTTP with standard redirects.
However it would be remiss of me to not warn you against going back to HTTP. HTTPS is the new standard and there is a general push to encourage all sites to move to HTTPS and penalise those that do not. Read his post for more information:
https://www.troyhunt.com/life-is-about-to-get-harder-for-websites-without-https/
While you are correct that you cannot frame HTTP content on a HTTPS page, you should consider if there is another way to address this problem. A single HTTP page on your site can cause security problems like leaking cookies (if they are not set up correctly). Plus frames are horrible and shouldn't be used anymore :-)
You can use rewrite rules to redirect https requests to http inside of subdirectory. Create an .htaccess file inside tools directory and add the following content:
RewriteEngine On
RewriteCond %{HTTPS} on
RewriteRule (.*) http://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Make sure that apache mod_rewrite is enabled.
Basically any HTTP 301 response from an HTTPS request indicating a target redirect to HTTP should never be honored at all by any browser, those servers doing that are clearly violating basic security, or are severaly compromized.
However a 301 reply to an HTTPS request can still redirect to another HTTPS target (including on another domain, provided that other CORS requirements are met).
If you navigate an HTTPS link (or a javascript event handler) and the browser starts loading that HTTPS target which replies with 301 redirect to HTTP, the behavior of the browser should be like if it was a 500 server error, or a connection failure (DNS name not resolved, server not responding timeout).
Such server-side redirect are clearly invalid. And website admins should never do that ! If they want to close a service and inform HTTPS users that the service is hosted elsewhere and no longer secure, they MUST return a valid HTTPS response page with NO redirect at all, and this should really be a 4xx error page (most probably 404 PAGE NOT FOUND) and they should not redirect to another HTTPS service (e.g. a third-party hosted search engine or parking page) which does not respect CORS requirements, or sends false media-types (it is acceptable to not honor the requested language and display that page in another language).
Browsers that implement HSTS are perfectly correct and going to the right direction. But I really think that CORS specifications are a mess, just tweaked to still allow advertizing network to host and control themselves the ads they broadcast to other websites.
I strongly think that serious websites that still want to display ads (or any tracker for audience measurement) for valid reasons can host these ads/trackers themselves, on their own domain and in the same protocol: servers can still get themselves the ads content they want to broadcast by downloading/refreshing these ads themselves and maintaining their own local cache. They can track their audience themselves by collecting the data they need and want and filtering it on their own server if they want this data to be analysed by a third party: websites will have to seriously implement thelselves the privacy requirements.
I hate now those too many websites that, when visited, are being tracked by dozens of third parties, including very intrusive ones like Facebook and most advertizing networks, plus many very weak third party services that have very poor quality/security and send very bad content they never control (including fake ads, fake news, promoting illegal activities, illegal businesses, invalid age rating...).
Let's return to the origin of the web: one site, one domain, one third party. This does not mean that they cannot link to other third party sites, but these must done only with an explicit user action (tapping or clicking), and visitors MUST be able to kn ow wherre this will go to, or which content will be displayed.
This is even possible for inserting videos (e.g. Youtube) in news articles: the news website can host themselves a cache of static images for the frame and icons for the "play" button: when users click that icon, it will start activating the third party video, and in that case the thirf party will interact directly with that user and can collect other data. But the unactivated contents will be tracked only by the origin website, under their own published policy.
In my local development environment I use apache server. What worked for me was :
Open you config file in sites-availabe/yoursite.conf. then add the following line inside your virtualhost:
Header always set Strict-Transport-Security "max-age=0". Restart your server.

Can't turn off requirehttps in asp.net 5 MVC 6 site

I have an asp.net 5 MVC 6 site I am working on. I turned on require HTTPS like so:
services.AddMvc(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
It worked great, and I have been working like this for a little while. Today I need to turn it off, so I commented out the options filter, but it is still requiring HTTPS.
I have not used the [RequireHttps] attribute on the controllers or actions themselves.
I have gone into the properties and unchecked "Enable SSL" and pasted the http url in the "Launch URL" box.
I have shutdown IIS Express and relaunched the site. It doesn't seem to matter what I do, it continues to try to redirect to HTTPS.
Is it possible IIS Express or Kestral has cached something I need to delete?
Anyone have any suggestions of what else could be forcing it to HTTPS?
The RequireHttpsAttribute implementation will send a permanent redirect response (301) back to browser:
// redirect to HTTPS version of page
filterContext.Result = new RedirectResult(newUrl, permanent: true);
This means that when you initially had the attribute enabled and requested a url like http://localhost:62058/, the server will respond with:
301 (Moved permanently)
Location: https://localhost:62058/
If you look at the definition of the 301 response code you will see that browsers will cache it by default:
The requested resource has been assigned a new permanent URI and any
future references to this resource SHOULD use one of the returned
URIs. Clients with link editing capabilities ought to automatically
re-link references to the Request-URI to one or more of the new
references returned by the server, where possible. This response is
cacheable unless indicated otherwise.
The new permanent URI SHOULD be given by the Location field in the
response. Unless the request method was HEAD, the entity of the
response SHOULD contain a short hypertext note with a hyperlink to the
new URI(s).
After you remove the RequireHttps filter, the browser will still use the cached response:
So all you need to do after removing the RequireHttps filter is rebuild and clear the browser cache!

How To Authenticate Across Subdomains

I'm working on a web application which actually consists of two applications under the hood. One application is called account and handles all things related to user accounts such authentication, registration and management of the account. I also have an application we'll just call web.
The thing is that account listens on https://account.domain.com using SSL/TLS, and web listens on http://www.domain.com.
What options do I have for having people log in and authenticate account.domain.com and then redirecting them to www.domain.com where they're actually then logged in. As far as I know, you can't set up a cookie on account.domain.com and then have it work on domain.com as that would be a security risk.
Some background details about my applications:
Written in the Go programming language.
Makes use of the Gorilla Toolkit for most of the HTTP/HTTPS interfacing, URL routing and handling POST/GET parameters.
Both applications live on the same virtual server.
What I'm looking for is a secure way to authenticate and manage a session across all subdomains of and the actual domain domain.com. I'm not particularly well versed in this subject, so aside from setting cookies, I don't know much.
I'm not familiar enough with gorilla but something like should work:
var store = sessions.NewCookieStore([]byte("something-very-secret"))
func init() {
store.Options = &sessions.Options{
Domain: "domain.com", //this
HttpOnly: true,
}
}
Basically you just have to set the cookie's domain to .domain.com (with the prefix .), there's a more detailed explanation in https://stackoverflow.com/a/1063760/145587
//edit
According to #Volker, the dot isn't needed (see comments).

Tomcat FORM authentication - login form not coming up as https

This is driving me nuts.
I'm using Tomcat 6, declaritive authentication, form based. No framework involved.
Everything works ok - some pages authenticated, some not, some use https, some http. Everything as I want it. EXCEPT...
I want the login page to always use https.
The login page comes up nicely as https if:
a) I go to it directly in the browser.
b) I click on a page in the application that is configured for https (and requires authentication).
BUT the login page comes up as http if:
a) I click on a page in the application that is configured for http (and requires authentication).
I've a feeling I'm up against some sort of accepted default here and that an answer might be "why would you want an https login to get to a non-https page?".
Its like this:
a) I want passwords to be encrypted.
b) I want users to login to show which role (group) they belong to in order to enable/disable parts of the web site.
c) I dont want a drop in performance due to https except where absolutely necessary.
I guess if the login page is forced to be https (like I want it to be) then there has to be a mechanism to put it back to http.
If anyone has some pointers/ideas around this whole area I'd be very very greatful.
web.xml fragments:
<security-constraint>
<display-name>Security Constraint A0S1</display-name>
<web-resource-collection>
<web-resource-name>A0S1</web-resource-name>
<url-pattern>/login/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login/form_login.jsp</form-login-page>
<form-error-page>/login/error.jsp</form-error-page>
</form-login-config>
</login-config>
This was about ensuring login page is https even when the page requiring authentication is not set as CONFIDENTIAL in web.xml.
I've ended up writing a little servlet that allows me to switch to https (or http) rather than rely on web.xml CONFIDENTIAL config settings. The CONFIDENTIAL settings dont seem to work when you arrive at a jsp page through the login or through another servlet.
So now the config for the FORM authentication in web.xml points to a servlet (SSLSwitch) which takes a couple of arguments (url + desired protocol http/https) and redirects to the actual login page with https:
/SSLSwitch?the_url=/login/form_login.jsp&the_target=https;
/login/error.jsp
SSLSwitch Servlet active code fragment:
String contextPath = request.getContextPath();
String encodedUrl = response.encodeURL(contextPath + url);
String fullUrl = target_domain + encodedUrl;
response.sendRedirect(fullUrl);
The jsp login page itself follows the usual FORM login rules (action="j_security_check") and you end up on the requested page after login ok.
I now need to look at what I can do to improve session security after switching from https to http. Maybe a filter to check user's IP doesnt change during a session.
Steven.