Implementing HTTPS Everywhere in ASP.Net MVC application.
Implementing HTTPS Everywhere in ASP.Net MVC application.
HTTPS everywhere is a common theme of the modern infosys topics. Despite of that when I google for implementation of HTTPS in ASP.Net MVC applications, I find only a handful of horrible questions on StackOverflow, about how to implement HTTPS only on certain pages (i.e. login page). There have been numerous rants about security holes awaiting for you down that path. And Troy Hunt will whack you over your had for doing that!
See that link above? Go and read it! Seriously. I’ll wait.
Have you read it? Troy there explains why you want to have HTTPS Everywhere on your site, not just on a login page. Listen to this guy, he knows what he is talking about.
Problem I faced when I wanted to implement complete “HTTPS Everywhere” in my MVC applications is lack of implementation instructions. I had a rough idea of what I needed to do, and now that I’ve done it a few times on different apps, my process is now ironed-out and I can share that with you here.
1. Redirect to HTTPS
Redirecting to HTTPS schema is pretty simple in modern MVC. All you need to know about is RequireHttpsAttribute. This is named as Attribute and can be used as an attribute on separate MVC controllers and even actions. And I hate it for that – it encourages for bad practices. But luckily this class is also implements IAuthorizationFilter interface, which means this can be used globally on the entire app as a filter.
Problem with this filter – once you add it to your app, you need to configure SSL on your development machine. If you work in team, all dev machines must be configured with SSL. If you allow people to work from home, their home machines must be configured to work with SSL. And configuring SSL on dev machines is a waste of time. Maybe there is a script that can do that automatically, but I could not find one quickly.
Instead of configuring SSL on local IIS, I decided to be a smart-ass and work around it. Quick study of source codehighlighted that the class is not sealed and I can just inherit this class. So I inherited RequireHttpsAttribute and added logic to ignore all local requests:
public class RequreSecureConnectionFilter : RequireHttpsAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.HttpContext.Request.IsLocal)
{
// when connection to the application is local, don't do any HTTPS stuff
return;
}
base.OnAuthorization(filterContext);
}
}
If you are lazy enough to follow the link to the source code, I’ll tell you all this attribute does is check if incoming request schema used is https (that is what Request.IsSecureConnection does), if not, redirect all GET request to https. And if request comes that is not secured and not GET, throw exception. I think this is a good-aggressive implementation.
One might argue that I’m creating a security hole by not redirecting to https on local requests. But if an intruder managed to do local requests on your server, you are toast anyway and SSl is not your priority at the moment.
I looked up what filterContext.HttpContext.Request.IsLocal does and how it can have an impact on security. Here is the source code:
public bool IsLocal {
get {
String remoteAddress = UserHostAddress;
// if unknown, assume not local
if (String.IsNullOrEmpty(remoteAddress))
return false;
// check if localhost
if (remoteAddress == "127.0.0.1" || remoteAddress == "::1")
return true;
// compare with local address
if (remoteAddress == LocalAddress)
return true;
return false;
}
}
This is decompiled implementation of System.Web.HttpRequest. UserHostAddress get client’s IP address. If IP is localhost (IPv4 or IPv6), return true. LocalAddress property returns servers IP address. So basically .IsLocal() does what it says on the tin. If request comes from the same IP the application is hosted on, return true. I see no issues here.
By the way, here are the unit tests for my implementation of the secure filter. Can’t go without unit testing on this one!
And don’t forget to add this filter to list of your global filters
public static class FilterConfig
{
public void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RequreSecureConnectionFilter());
// other filters to follow...
}
}
2. Cookies
If you think that redirecting to https is enough, you are very wrong. You must take care of your cookies. And set all of them by default to be HttpOnly and SslOnly. Read Troy Hunt’s excellent blog post why you need your cookies to be secured.
You can secure your cookies in web.config pretty simple:
<system.web>
<httpCookies httpOnlyCookies="true" requireSSL="true"/>
</system.web>
The only issue with that is development stage. Again, if you developing locally you won’t be able to login to your application without https running locally. Solution to that is web.config transformation.
So in your web.config you should always have
<system.web>
<httpCookies httpOnlyCookies="true" />
</system.web>
and in your web.Release.config file add
<system.web>
<httpCookies httpOnlyCookies="true" requireSSL="true" lockItem="true" xdt:Transform="Replace" />
</system.web>
This secures your cookies when you publish your application. Simples!
3. Secure authentication cookie
Apart from all your cookies to be secure, you need to specifically require authentication cookie to be SslOnly. For that you need to add requireSSL="true" to your authentication/forms part of web.config. Again, this will require you to run your local IIS with https configured. Or you can do web.config transformation only for release. In your web.Release.config file add this into system.web section
<authentication mode="Forms">
<forms loginUrl="~/Logon/LogOn" timeout="2880" requireSSL="true" xdt:Transform="Replace"/>
</authentication>
4. Strict Transport Security Header
Strict Transport Security Header is http header that tells web-browsers only to use HTTPS when dealing with your web-application. This reduces the risks of SSL Strip attack. To add this header by default to your application you can add add this section to your web.config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security" value="max-age=16070400; includeSubDomains" />
</customHeaders>
</httpProtocol>
</system.webServer>
Again, the same issue as before, developers will have to have SSL configured on their local machines. Or you can do that via web.config transformation. Add the following code to your web.Release.config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security" value="max-age=16070400; includeSubDomains" xdt:Transform="Insert" />
</customHeaders>
</httpProtocol>
</system.webServer>
5. Secure your WebApi
WebApi is very cool and default template for MVC application now comes with WebApi activated. Redirecting all MVC requests to HTTPS does not redirect WebApi requests. So even if you secured your MVC pipeline, your WebApi requests are still available via HTTP.
Unfortunately redirecting WebApi requests to HTTPS is not as simple as it is with MVC. There is no [RequireHttps]available, so you’ll have to make one yourself. Or copy the code below:
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
public class EnforceHttpsHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// if request is local, just serve it without https
object httpContextBaseObject;
if (request.Properties.TryGetValue("MS_HttpContext", out httpContextBaseObject))
{
var httpContextBase = httpContextBaseObject as HttpContextBase;
if (httpContextBase != null && httpContextBase.Request.IsLocal)
{
return base.SendAsync(request, cancellationToken);
}
}
// if request is remote, enforce https
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
return Task<HttpResponseMessage>.Factory.StartNew(
() =>
{
var response = new HttpResponseMessage(HttpStatusCode.Forbidden)
{
Content = new StringContent("HTTPS Required")
};
return response;
});
}
return base.SendAsync(request, cancellationToken);
}
}
This is a global handler that rejects all non https requests to WebApi. I did not do any redirection (not sure this term is applicable to WebApi) because there is no excuse for clients to use HTTP first.
WARNING This approach couples WebApi to System.Web libraries and you won’t be able to use this code in self-hosed WebApi applications. But there is a better way to implement detection if request is local. I have not used it because my unit tests have been written before I learned about better way. And I’m too lazy to fix this -)
Don’t forget to add this handler as a global:
namespace MyApp.Web.App_Start
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// other configurations...
// make all web-api requests to be sent over https
config.MessageHandlers.Add(new EnforceHttpsHandler());
}
}
}
6. Set up automatic security scanner for your site
ASafaWeb is a great tool that checks for a basic security issues on your application. The best feature is scheduled scan. I’ve set all my applications to be scanned on weekly basis and if something fails, it emails me. So far this helped me once, when error pages on one of the apps were messed up. If not for this automated scan, the issue could have stayed there forever. So go and sign-up!
Conclusion
This is no way a complete guide on securing your application. But this will help you with one of the few steps you need to take to lock down your application.
In this Gist you can copy my web.Release.config transformation file, in case you got confused with my explanation.
Implementing HTTPS Everywhere in ASP.Net MVC application.的更多相关文章
- [转]剖析ASP.Net MVC Application
http://www.cnblogs.com/errorif/archive/2009/02/13/1389927.html 为了完全了解Asp.net MVC是怎样工作的,我将从零开始创建一个MVC ...
- 源码学习之ASP.NET MVC Application Using Entity Framework
源码学习的重要性,再一次让人信服. ASP.NET MVC Application Using Entity Framework Code First 做MVC已经有段时间了,但看了一些CodePle ...
- [转]Creating an Entity Framework Data Model for an ASP.NET MVC Application (1 of 10)
本文转自:http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/creating-a ...
- [转]Implementing User Authentication in ASP.NET MVC 6
本文转自:http://www.dotnetcurry.com/aspnet-mvc/1229/user-authentication-aspnet-mvc-6-identity In this ar ...
- [转]Sorting, Filtering, and Paging with the Entity Framework in an ASP.NET MVC Application (3 of 10)
本文转自:http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/sorting-fi ...
- Migrating an ASP.NET MVC application to ADFS authentication
I recently built an ASP.NET application at work to help track internal use of our products. It's bee ...
- ASP.NET MVC 5 -从控制器访问数据模型
在本节中,您将创建一个新的MoviesController类,并在这个Controller类里编写代码来取得电影数据,并使用视图模板将数据展示在浏览器里. 在开始下一步前,先Build一下应用程序(生 ...
- Demystifying ASP.NET MVC 5 Error Pages and Error Logging
出处:http://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging Error pages and error ...
- 【转】ASP.NET MVC 的最佳实践
[This post is based on a document authored by Ben Grover (a senior developer at Microsoft). It is ou ...
随机推荐
- 网络名词拾遗--part2
网络名词拾遗--part2 关于所谓的连接上限 先要明白服务端和客户端的交互逻辑: 服务端创建socket 与提供对外服务的port端口绑定 开始监听 客户端向这个端口提出请求 服务端接收到这个请求后 ...
- 静态代理、JDK动态代理和CGLib动态代理之前的区别
昨天看了一天的代理方面的知识,刚开始看的时候没看出什么花头来,感觉不实用.一大堆的东西,还不如直接new出来,然后调用方法.后来仔细研究了一下AOP(面向切面)的思想,才发现代理的用处实在太大了.现在 ...
- Tinkoff Challenge - Final Round (Codeforces Round #414, rated, Div. 1 + Div. 2) 【ABC】
老年人题解,语言python3 A - Bank Robbery 题意:给你ABC,以及n个数,问你在(B,C)之间的数有多少个. 题解:对于每个数判断一下就好了嘛 x,y,z = map(int,i ...
- MYSQL时间类别总结: TIMESTAMP、DATETIME、DATE、TIME、YEAR
总结背景: 对于MYSQL数据库日期类型或多有了解, 但并很清晰其中一些规则. 基本都是面向浏览器编码, 这实质上也是一种方式. 但期间遇到两个问题: 时常遇到建表中出现多个datetime或者ti ...
- 使用gulp对js、css、img进行合并压缩
1 概述 最新使用AngularJS框架做单页面项目,其中包括了很多库的和自已写的js.css.img文件,这些文件都不大,但是数量众多,导致web请求文件过多,一次性加载时比较慢.有尝试过使用异步加 ...
- asp.net mvc流程图4.6以前
- Xcode 安装ClangFormat 插件
sudo gem install -n /usr/local/bin update_xcode_plugins 安装ClangFormat git clone https://github.com/t ...
- SharePoint 特殊用户标识
To get claim for All Authenticated Users in PS you need to use:$claim = New-SPClaimsPrincipal -Encod ...
- 解决IDEA授权报错
今天打开电脑,猛然发现IDEA授权失效,然后重新用账号密码登陆,发现被拒绝,各种百度百思不得其解,抱着试试的态度,点击了重置密码 https://account.jetbrains.com/forgo ...
- postgresql 窗口函数排序实例
经常遇到一种应用场景,将部分行的内容进行汇总.比较.排序. 比如数据表名称test.test2 select num,province from test.test2 得到结果: ;"黑龙江 ...