Post from another domain causes session null - c#

We have a payment sub-system which goes like this;
User enters payment data
Payment data sent to the 3rd party service url (with form post / eg. POST https://3rdparty.com/form )
3rd party service checks data and sends some other data (again using form post but this time to our url / eg POST http://localhost/Success)
In other words;
http://localhost/Page1 -> POST https://3rdparty.com/Service -> POST http://localhost/Success
But when second redirection (3rd party service posting data to our side), session object becomes null.
I've mimicked the POST scenario to http://localhost/Success from both same origin and other origin.
When posting from the same origin
http://localhost/Test -> POST -> http://localhost/Success => OK. Session IS NOT null
But when posting from another origin
http://testdomain/Test -> POST -> http://localhost/Success => OK. Session IS null
BTW; this happens regardless of other origin's procotol, be it http or https
What is the source of this behaviour ? I couldn't find anything meaningful...
EDIT :
I've added the code of the ActionFilterAttribute which intercepts every request and checks for session (due to #mxmissile 's comment)
public class LoginFilter : System.Web.Mvc.ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session[Constants.SessionNames.USER_OBJECT_NAME] == null)
// this is where I check the Session object and it's null
// it doesn't have any key in it
{
filterContext.HttpContext.Session.Abandon();
filterContext.Result = new RedirectResult("/Login/Login");
}
}
}
EDIT 2 :
Using the configuration below, which re-writes Set-Cookie header's flags, I was able to modify flags. (suggested at https://blog.elmah.io/the-ultimate-guide-to-secure-cookies-with-web-config-in-net);
<rewrite>
<outboundRules>
<clear />
<rule name="Add SameSite" preCondition="No SameSite">
<match serverVariable="RESPONSE_Set_Cookie" pattern=".*" negate="false" />
<action type="Rewrite" value="{R:0}; SameSite=lax" />
</rule>
<preConditions>
<preCondition name="No SameSite">
<add input="{RESPONSE_Set_Cookie}" pattern="." />
<add input="{RESPONSE_Set_Cookie}" pattern="; SameSite=lax" negate="true" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
But SameSite=Lax wasn't sending cookies to the 3rd party service as stated here https://www.thinktecture.com/identity/samesite/prepare-your-identityserver/ , due to Chrome's 80.x updates
So I've changed SameSite=lax to SameSite=none and it worked.
But as I imagine, this would generate new issues with security. Such as CSRF attacks.
What would be the best way to do it ?

I'll assume that you've got the SameSite=None; Secure working and just point out a few things that might be of use.
What is the source of this behaviour?
The Chromuim SameSite FAQ page has quite good summary of what the change is:
Q: What are the new SameSite changes?
Chrome is changing the default behavior for how cookies will be sent in first and third party contexts.
as I imagine, this would generate new issues with security.
Based on my interpretation of the quote above, I don't believe so: Chrome will send less cookies by default now. By reverting to sending these cookies, you are not increasing your attack surface - you merely restoring your existing flow.
Actually, if you read that page further, there's one thing that is now required - for the cookies to be sent over HTTPS. So you are actually better off by acknowledging the fact that these cookies are sensitive and requiring the user to always opt for secure connections.
Such as CSRF attacks.
I don't believe this point depends on Chrome Cookie policy. It still is valid. If not doing that already - check out the Microsoft docsumentation and see if a standard Razor Html helper will be feasible option for you:
#using (Html.BeginForm("Login", "Login")) {
#Html.AntiForgeryToken()
}
What would be the best way to do it?
Looking at how Google is pushing it as the default behaviour, it seems your best bet is to adopt it I'm afraid.

Related

ASP.NET FormsAuthenticationModule DefaultUrl overriding explicit

I have a mature ASP.NET web application using FormsAuthentication (FA) to manage logins. Under certain situations, I would like to redirect the "just logged in" user to a different URL to the one that FA uses. As per standard functionality, FA will redirect to our normal homepage (specified in web.config) unless a redirectUrl was used when it hits a page that requires an authenticated user.
In my system, after the user's username/password is validated I typically use
FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie: true); // Also calls SetAuthCookie()
which handles most situations. However, depending on certain conditions (primarily based on the newly logged in user's role) I want to redirect to a different destination. My thoughts for doing this are to call SetAuthCookie() myself and then use Response.Redirect(myUrl, false); and ApplicationInstance.CompleteRequest().
Despite doing this, the very next request comes in using for the URL defined in my tag of web.config.
<authentication mode="Forms">
<forms loginUrl="~/Login" timeout="120" cookieless="UseCookies" defaultUrl="~/?raspberry=true" />
</authentication>
Here is the actual code I am using (if a different url is required, it is specified by the overrideUrl parameter:
internal static void CreateTicket(string userName, string overrideUrl)
{
// Ref: http://support.microsoft.com/kb/301240
if (overrideUrl == null)
{
FormsAuthentication.RedirectFromLoginPage(userName, createPersistentCookie: true); // Includes call to SetAuthCookie()
}
else
{
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie: true, strCookiePath:FormsAuthentication.FormsCookiePath);
HttpContext.Current.Response.Redirect(overrideUrl, false);
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
If I pass in a value of /special/path for overrideUrl I would like the next request to come in to be '/special/path'. Instead I am seeing /?raspberry=true
Is something else forcing defaultUrl?
Is there a way to "snoop" into the Response object while debugging to see if a Redirect is already in place? or set a breakpoint whenever it gets set so I can look at the call stack?
EDIT: At the end of my method, the Response object is showing the following properties:
RedirectLocation: "/special/path"
Status: "302 Found"
StatusCode: 302
StatusDescription: "Found"
IsRequestBeingRedirected: true
HeadersWritten: false
which all looks absolutely correct.
Thanks for any advise.
Okay, I can see what is causing it. It is the fact that I am using the Login Web Control and running my code as part of the Authenticate event. Looking at the reference source for the Login Web Control, the Authenticate event is raised by its AttemptLogin method (search for it in ref source). After raising the event and seeing that Authentication was successful, it then goes on to:
Call SetAuthCookie itself (I've already done this myself but presumably the only thing I should be doing in my code is determining if authentication was successful or not, and not messing with AuthCookie or redirects)
Performing a Redirect (overwriting my carefully crafted Redirect)
I'm going to have to figure out a solution as there these methods are private (can't override by inheriting the usercontrol) and there appears to be no option for overring or suppressing the user of it's GetRedirectUrl().

Why is the cookie I set in the server not showing in the client?

I am not able to read a cookie on the client that I set on the server.
I am using C# / MVC and am handling the cookie logic outside of the controller.
I pass in the Response, and can see it is set after I call this method from the controller, but once the response gets sent to the client, this cookie is not present.
In the controller, I am calling the SetCookie method like this:
CookieHelper.SetCookie(Response);
I have also tried to pass in System.Web.HttpContext.Current.Response to no avail.
The method I am calling is:
public class CookieHelper {
public static void SetCookie(HttpResponseBase Response)
{
HttpCookie cookie = new HttpCookie("jayscookie");
cookie.Value = "a much longer string than this";
cookie.Expires = DateTime.Now.AddDays(7);
Response.Cookies.Add(cookie);
}
}
I have tried HttpResponseWrapper as well.
I have also tried System.Net.Cookie instead of HttpCookie
My IIS Setting for Session State is represented by this line in the Web.Config:
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" cookieless="false"/>
I have tried without cookieless="false"
The Cookie Settings section of this application in IIS are:
Mode: UseCookies
Name: ASP.NET_SessionId
Time-out: 20 (minutes)
I have visited the following and tried solutions:
Why is my cookie not set?
Cookies not getting set in c#
Why are the cookies not being set when doing a Redirect?
Cookie not setting in asp.net mvc
How to set cookie value?
HttpWebRequest/Reponse return cookies sent
As well as several microsoft documentation pages, such as:
https://msdn.microsoft.com/en-us/library/ms178194.aspx
Is there anything I am missing, or any basic concept I am obviously not taking into account? Setting cookies on the client works fine, just not setting them on the server.
The cookie.Value string was a different value in my application. The value in the application was far too big, and exceeded the limit for cookie size. I will have to figure out a different approach, but that was the issue.
The size limit for cookies is around 4KB, for any who may find this information useful.
Server side can t access client side directly and vice versa. Sorry for poor explain but i'm in train

Reading cookie value : Using URL Rewrite Provider module - Unable to validate at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData

I have requirement to append USERNAME to the URL in server side using URL Rewrite module.
Why?:
I have website site1, when USER logs in to site1, he will see a link to site2., This link is URL or reports. (Tableau).
Authenticated ticket has been created using FormAuthentication in site1.
When USER clicks the link, authenticated username should be passed to site2.
I could append username from client side, but due to security issues I have to append username to URL in server side before it gets executed.
So I have decided to use URL rewrite provider, which grabs the username by decrypting the cookie value as shown below
namespace PlatformAnalysisUrlProvider.PlatformAnalysisProvider
{
class AnalysisRewriteProvider: IRewriteProvider, IProviderDescriptor
{
public void Initialize(IDictionary<string, string> settings,
IRewriteContext rewriteContext)
{
}
public string Rewrite(string value)
{
string[] cookievalues = value.Spli('=');
FormAuthentication ticket = FormAuthentication.Decrypt(cookievalues[1]);
//Decrypt throws error as shown below
}
}
}
Cookie Values
cookievalues [0] = has the key
cookievalues [1] = has the value
Example:
233AWJDKSHFHFDSHFJKDFDKJFHDKJFKDJFHDHFDHFKJHDFKJHDFJHDKJFHDSKJFHDF
It's a cookie value. But decrypt is not happening
I am getting following error
Unable to validate data.
at System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(
Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start,
Int32 length, IVType ivType, Boolean useValidationSymAlgo,
Boolean signData)
Here is my settings in IIS for URL Rewrite
Requested URL: Matches the Patterns
Using: Regular Expression
Ignore Case - Checked
Conditions -
Input : {HTTP_COOKIE}
Type : Matches the Pattern
Pattern : .*
Action Type - Rewrite
Rewrite URL - http://11.155.011.123{HTTP_URL}&USERNAME={PlatformAnalysisUrlProvider:{C:0}}
I have also set up MACHINE KEY as suggested by this forum
I have referred this post for development
One of the stack overflow post suggested that it might be firewall or antivirus issue. But I do not have antivirus installed or firwall enabled.
It really helps if someone direct me to code sample where web site hosted in IIS and URL Rewrite provider is used.
Updating Error Log
MODULE_SET_RESPONSE_ERROR_STATUS
Notification - "PRE_BEGIN_REQUEST"
HttpReason - "URL Rewrite Module Error"
Updating post with Machine Key Info
<MachineKey Description="AES" validation="SHA1"
descriptionKey="******"
validationKey="******" CompatibilityMode="Framework20SP2">
Reason May be - The website where cookie getting created is developed using .NET Framework 4.5. The provider where we reading the cookie is Framework 3.5. Is this may be the cause? OR Do we need config file for Provider project?
Updates - I have added machine key to Machine.config , but it still did not work :(
Alternative Solution
Add App.config to class Library
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!-- ... -->
<add key="SecurityKey" value="somevalue"/>
<!-- ... -->
</appSettings>
</configuration>
Copy config to GAC
Follow this blog - http://techphile.blogspot.in/2007/02/2.html
Encrypt the value (refer here) and create custom cookie during Login
Use the Decrption logic inside custom rewrite provider
The good thing about this is that the error is a general decryption error and not one with URL Rewrite itself, so that gives you a wider area to search for help. The mechanics of URL Rewrite seem to be right.
Decrypting means that it must be encrypted by the same method as you're decrypting it. So it has to be the right cookie and the right decryption method.
Since you're not checking which cookie that you're reading from, you could get unexpected results if the wrong cookie is first in the list of cookies.
Here are some steps that I recommend to troubleshoot this:
Create a simple URL Rewrite rule that will give you the value of your cookie. I created a rule to do that in my example below. You can test it by going to yoursite.com/getcookie. It should redirect to yoursite.com/?Cookie={cookievalue}
Then you can test your code outside of the URL Rewrite provider. You can create a simple console app or winforms app to test the rest of the code.
I recommend adding a check for the existence of the cookie and then a check again for the 2nd value. For example: if (cookievalues[1] != null).
When developing the decryption method, you don't have to worry about URL Rewrite. As long as it works in a test app in .NET then you should be set.
<rule name="Get cookie value" stopProcessing="true">
<match url="^getcookie" />
<action type="Redirect" url="/?Cookie={HTTP_COOKIE}" appendQueryString="false" redirectType="Found" />
</rule>

How can one prevent accessing content directly?

Using ASP MVC 4.5, how can one apply security measures in order to prevent users from accessing content directly?
Like for example preventing the access of images or other files stored on the web server just by entering their link.
Place your image in a non-web accessible folder.
Create a server side script (for example, an HttpHandler) that can read the image file and return its contents in the HTTP response.
In that script, perform your user validation to make sure the user has access to that file.
In the HTML, have the src attribute of the img tag point to your script.
The user can still directly type in the URL to your script to see the image. But you can at least require that the user is logged into your site and is authorized to view the image.
Use an authentication system such as ASP .NET Membership and require certain credentials to access the content. Other than that, there really isn't a way. If a user has a direct link and access to that area of your website, by nature of how web servers work, there isn't a way to stop it.
There are certain security measures you can take to help prevent users from getting a direct link though, a simple one would be disabling a right click.
I have produced the following HTTPHandler in order to prevent hotlinking.
It seems to work on my project, however I do not certainly know if this is the best practice.
public void ProcessRequest(HttpContext context)
{
//write your handler implementation here.
//Http
HttpRequest request = context.Request;
HttpResponse response = context.Response;
//Header - Properites
int Index = -1;
string[] Keys = request.Headers.AllKeys;
List<string[]> Values = new List<string[]>();
//Header - Loop to get key values
for (int i = 0; i < Keys.Length; i++)
{
Values.Add(request.Headers.GetValues(i));
//Check if property "Accept" exists
if (Keys[i].CompareTo("Accept") == 0)
Index = i;
}
//Check if URL and URL Referrer are null
if (context.Request.Url != null && context.Request.UrlReferrer != null && Index >= 0)
{
//Check image types
if (!context.Request.UrlReferrer.AbsolutePath.EndsWith(".bmp") ||
!context.Request.UrlReferrer.AbsolutePath.EndsWith(".jpg") ||
!context.Request.UrlReferrer.AbsolutePath.EndsWith(".jpeg") ||
!context.Request.UrlReferrer.AbsolutePath.EndsWith(".png"))
{
//Check header "Accept"
if (Values[Index][0].CompareTo("*/*") == 0)
{
//Get bytes from file
byte[] MyBytes = File.ReadAllBytes(context.Request.PhysicalPath);
//new HttpContext(context.Request, context.Response).Request.MapPath(context.Request.RawUrl).ToString()
context.Response.OutputStream.Write(MyBytes, 0, MyBytes.Length);
context.Response.Flush();
}
else
//Redirect
context.Response.Redirect("/Home");
}
else
//Redirect
context.Response.Redirect("/Home");
}
else
//Redirect
context.Response.Redirect("/Home");
}
Also the Web.config was modified as follows:
<system.webServer>
<handlers>
<!--My-->
<add name="PhotoHandler-BMP" path="*.bmp" verb="GET" type="MVCWebApplication.Handlers.PhotoHandler" resourceType="File" />
<add name="PhotoHandler-JPG" path="*.jpg" verb="GET" type="MVCWebApplication.Handlers.PhotoHandler" resourceType="File" />
<add name="PhotoHandler-JPEG" path="*.jpeg" verb="GET" type="MVCWebApplication.Handlers.PhotoHandler" resourceType="File" />
<add name="PhotoHandler-PNG" path="*.png" verb="GET" type="MVCWebApplication.Handlers.PhotoHandler" resourceType="File" />
</handlers>
</system.webServer>
Feel free to comment on any improvements.
There is little you can do unless you want to bug your users. One possible (and widely used) thing could be checking your referrer (and make it be on your application), but that can easily be spoofed.
If security for this is something critical, the only thing that comes into mind is having everything downloaded through a script which would check for credentials (or any other security measure you might want), but there's not much else you can do.
If the browser has indeed downloaded something to the local machine, there's absolutely no way you can prevent that user to use that data (you can put some barriers, like avoiding right-clicking, etc., but all of them can be avoided in some way or another).

Best way in asp.net to force https for an entire site?

About 6 months ago I rolled out a site where every request needed to be over https. The only way at the time I could find to ensure that every request to a page was over https was to check it in the page load event. If the request was not over http I would response.redirect("https://example.com")
Is there a better way -- ideally some setting in the web.config?
Please use HSTS (HTTP Strict Transport Security)
from http://www.hanselman.com/blog/HowToEnableHTTPStrictTransportSecurityHSTSInIIS7.aspx
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="HTTP to HTTPS redirect" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="off" ignoreCase="true" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
redirectType="Permanent" />
</rule>
</rules>
<outboundRules>
<rule name="Add Strict-Transport-Security when HTTPS" enabled="true">
<match serverVariable="RESPONSE_Strict_Transport_Security"
pattern=".*" />
<conditions>
<add input="{HTTPS}" pattern="on" ignoreCase="true" />
</conditions>
<action type="Rewrite" value="max-age=31536000" />
</rule>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
Original Answer (replaced with the above on 4 December 2015)
basically
protected void Application_BeginRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.Request.IsSecureConnection.Equals(false) && HttpContext.Current.Request.IsLocal.Equals(false))
{
Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"]
+ HttpContext.Current.Request.RawUrl);
}
}
that would go in the global.asax.cs (or global.asax.vb)
i dont know of a way to specify it in the web.config
The other thing you can do is use HSTS by returning the "Strict-Transport-Security" header to the browser. The browser has to support this (and at present, it's primarily Chrome and Firefox that do), but it means that once set, the browser won't make requests to the site over HTTP and will instead translate them to HTTPS requests before issuing them. Try this in combination with a redirect from HTTP:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
switch (Request.Url.Scheme)
{
case "https":
Response.AddHeader("Strict-Transport-Security", "max-age=300");
break;
case "http":
var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", path);
break;
}
}
Browsers that aren't HSTS aware will just ignore the header but will still get caught by the switch statement and sent over to HTTPS.
The IIS7 module will let you redirect.
<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>
For those using ASP.NET MVC. You can use the following to force SSL/TLS over HTTPS over the whole site in two ways:
The Hard Way
1 - Add the RequireHttpsAttribute to the global filters:
GlobalFilters.Filters.Add(new RequireHttpsAttribute());
2 - Force Anti-Forgery tokens to use SSL/TLS:
AntiForgeryConfig.RequireSsl = true;
3 - Require Cookies to require HTTPS by default by changing the Web.config file:
<system.web>
<httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>
4 - Use the NWebSec.Owin NuGet package and add the following line of code to enable Strict Transport Security accross the site. Don't forget to add the Preload directive below and submit your site to the HSTS Preload site. More information here and here. Note that if you are not using OWIN, there is a Web.config method you can read up on on the NWebSec site.
// app is your OWIN IAppBuilder app in Startup.cs
app.UseHsts(options => options.MaxAge(days: 30).Preload());
5 - Use the NWebSec.Owin NuGet package and add the following line of code to enable Public Key Pinning (HPKP) across the site. More information here and here.
// app is your OWIN IAppBuilder app in Startup.cs
app.UseHpkp(options => options
.Sha256Pins(
"Base64 encoded SHA-256 hash of your first certificate e.g. cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=",
"Base64 encoded SHA-256 hash of your second backup certificate e.g. M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=")
.MaxAge(days: 30));
6 - Include the https scheme in any URL's used. Content Security Policy (CSP) HTTP header and Subresource Integrity (SRI) do not play nice when you imit the scheme in some browsers. It is better to be explicit about HTTPS. e.g.
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.4/bootstrap.min.js"></script>
The Easy Way
Use the ASP.NET MVC Boilerplate Visual Studio project template to generate a project with all of this and much more built in. You can also view the code on GitHub.
If you are unable to set this up in IIS for whatever reason, I'd make an HTTP module that does the redirect for you:
using System;
using System.Web;
namespace HttpsOnly
{
/// <summary>
/// Redirects the Request to HTTPS if it comes in on an insecure channel.
/// </summary>
public class HttpsOnlyModule : IHttpModule
{
public void Init(HttpApplication app)
{
// Note we cannot trust IsSecureConnection when
// in a webfarm, because usually only the load balancer
// will come in on a secure port the request will be then
// internally redirected to local machine on a specified port.
// Move this to a config file, if your behind a farm,
// set this to the local port used internally.
int specialPort = 443;
if (!app.Context.Request.IsSecureConnection
|| app.Context.Request.Url.Port != specialPort)
{
app.Context.Response.Redirect("https://"
+ app.Context.Request.ServerVariables["HTTP_HOST"]
+ app.Context.Request.RawUrl);
}
}
public void Dispose()
{
// Needed for IHttpModule
}
}
}
Then just compile it to a DLL, add it as a reference to your project and place this in web.config:
<httpModules>
<add name="HttpsOnlyModule" type="HttpsOnly.HttpsOnlyModule, HttpsOnly" />
</httpModules>
What you need to do is :
1) Add a key inside of web.config, depending upon the production or stage server like below
<add key="HttpsServer" value="stage"/>
or
<add key="HttpsServer" value="prod"/>
2) Inside your Global.asax file add below method.
void Application_BeginRequest(Object sender, EventArgs e)
{
//if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "prod")
if (ConfigurationManager.AppSettings["HttpsServer"].ToString() == "stage")
{
if (!HttpContext.Current.Request.IsSecureConnection)
{
if (!Request.Url.GetLeftPart(UriPartial.Authority).Contains("www"))
{
HttpContext.Current.Response.Redirect(
Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://www."), true);
}
else
{
HttpContext.Current.Response.Redirect(
Request.Url.GetLeftPart(UriPartial.Authority).Replace("http://", "https://"), true);
}
}
}
}
In IIS10 (Windows 10 and Server 2016), from version 1709 onwards, there is a new, simpler option for enabling HSTS for a website.
Microsoft describe the advantages of the new approach here, and provide many different examples of how to implement the change programmatically or by directly editing the ApplicationHost.config file (which is like web.config but operates at the IIS level, rather than individual site level). ApplicationHost.config can be found in C:\Windows\System32\inetsrv\config.
I've outlined two of the example methods here to avoid link rot.
Method 1 - Edit the ApplicationHost.config file directly
Between the <site> tags, add this line:
<hsts enabled="true" max-age="31536000" includeSubDomains="true" redirectHttpToHttps="true" />
Method 2 - Command Line:
Execute the following from an elevated command prompt (i.e. right mouse on CMD and run as administrator). Remember to swap Contoso with the name of your site as it appears in IIS Manager.
c:
cd C:\WINDOWS\system32\inetsrv\
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.enabled:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.max-age:31536000" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.includeSubDomains:True" /commit:apphost
appcmd.exe set config -section:system.applicationHost/sites "/[name='Contoso'].hsts.redirectHttpToHttps:True" /commit:apphost
The other methods Microsoft offer in that articles might be better options if you are on a hosted environment where you have limited access.
Keep in mind that IIS10 version 1709 is available on Windows 10 now, but for Windows Server 2016 it is on a different release track, and won't be released as a patch or service pack. See here for details about 1709.
If SSL support is not configurable in your site (ie. should be able to turn https on/off) - you can use the [RequireHttps] attribute on any controller / controller action you wish to secure.
This is a fuller answer based on #Troy Hunt's. Add this function to your WebApplication class in Global.asax.cs:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
// Allow https pages in debugging
if (Request.IsLocal)
{
if (Request.Url.Scheme == "http")
{
int localSslPort = 44362; // Your local IIS port for HTTPS
var path = "https://" + Request.Url.Host + ":" + localSslPort + Request.Url.PathAndQuery;
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", path);
}
}
else
{
switch (Request.Url.Scheme)
{
case "https":
Response.AddHeader("Strict-Transport-Security", "max-age=31536000");
break;
case "http":
var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery;
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", path);
break;
}
}
}
(To enable SSL on your local build enable it in the Properties dock for the project)
It also depends on the brand of your balancer, for the web mux, you would need to look for http header X-WebMux-SSL-termination: true to figure that incoming traffic was ssl. details here: http://www.cainetworks.com/support/redirect2ssl.html
For #Joe above, "This is giving me a redirect loop. Before I added the code it worked fine. Any suggestions? – Joe Nov 8 '11 at 4:13"
This was happening to me as well and what I believe was happening is that there was a load balancer terminating the SSL request in front of the Web server. So, my Web site was always thinking the request was "http", even if the original browser requested it to be "https".
I admit this is a bit hacky, but what worked for me was to implement a "JustRedirected" property that I could leverage to figure out the person was already redirected once. So, I test for specific conditions that warrant the redirect and, if they are met, I set this property (value stored in session) prior to the redirection. Even if the http/https conditions for redirection are met the second time, I bypass the redirection logic and reset the "JustRedirected" session value to false. You'll need your own conditional test logic, but here's a simple implementation of the property:
public bool JustRedirected
{
get
{
if (Session[RosadaConst.JUSTREDIRECTED] == null)
return false;
return (bool)Session[RosadaConst.JUSTREDIRECTED];
}
set
{
Session[RosadaConst.JUSTREDIRECTED] = value;
}
}
I'm going to throw my two cents in. IF you have access to IIS server side, then you can force HTTPS by use of the protocol bindings. For example, you have a website called Blah. In IIS you'd setup two sites: Blah, and Blah (Redirect). For Blah only configure the HTTPS binding (and FTP if you need to, make sure to force it over a secure connection as well). For Blah (Redirect) only configure the HTTP binding. Lastly, in the HTTP Redirect section for Blah (Redirect) make sure to set a 301 redirect to https://blah.com, with exact destination enabled. Make sure that each site in IIS is pointing to it's own root folder otherwise the Web.config will get all screwed up. Also make sure to have HSTS configured on your HTTPSed site so that subsequent requests by the browser are always forced to HTTPS and no redirects occur.
I spent sometime looking for best practice that make sense and found the following which worked perfected for me. I hope this will save you sometime.
Using Config file (for example an asp.net website)
https://blogs.msdn.microsoft.com/kaushal/2013/05/22/http-to-https-redirects-on-iis-7-x-and-higher/
or on your own server
https://www.sslshopper.com/iis7-redirect-http-to-https.html
[SHORT ANSWER]
Simply The code below goes inside
<system.webServer>
<rewrite>
<rules>
<rule name="HTTP/S to HTTPS Redirect" enabled="true"
stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAny">
<add input="{SERVER_PORT_SECURE}" pattern="^0$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}"
redirectType="Permanent" />
</rule>
</rules>
</rewrite>
-> Simply ADD [RequireHttps] on top of the public class HomeController : Controller.
-> And add GlobalFilters.Filters.Add(new RequireHttpsAttribute()); in 'protected void Application_Start()' method in Global.asax.cs file.
Which forces your entire application to HTTPS.
If you are using ASP.NET Core you could try out the nuget package SaidOut.AspNetCore.HttpsWithStrictTransportSecurity.
Then you only need to add
app.UseHttpsWithHsts(HttpsMode.AllowedRedirectForGet, configureRoutes: routeAction);
This will also add HTTP StrictTransportSecurity header to all request made using https scheme.
Example code and documentation https://github.com/saidout/saidout-aspnetcore-httpswithstricttransportsecurity#example-code

Categories