URL Rewrite HTTP to HTTPS but now backwards - iis

I have the following UrlRewrite code to change from http to https in the url but now I need it to change from https back to http.
For example when I redirect back from the Account/Logon page to the homepage.
Here are my rewriting rules:
<rewrite>
<rules>
<rule name="RequiresHTTPS-Redirect" stopProcessing="true">
<match url="(.+)" />
<conditions>
<add input="{HTTPS}" pattern="off" />
<add input="{RequiresHTTPS:{R:1}}" pattern="(.+)" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{C:1}"
appendQueryString="true" redirectType="Found" />
</rule>
</rules>
<rewriteMaps>
<rewriteMap name="RequiresHTTPS">
<add key="Account/LogOn" value="Account/LogOn" />
</rewriteMap>
</rewriteMaps>
</rewrite>

Put the logic to redirect back to the non-https page in the login page itself. The problem with redirecting from https to http is that the browser is still going to initiate an ssl connection first to the https url before it can get the redirect, so it's somewhat pointless.

There's a couple of scenarios here:
1. All content is protected by web forms <authorization>:
Your users are hitting the logon page because they've browsed to a part of the site that is protected using the <authorization> element. If this is the case you'll have a return URL passed to you in the query string: ReturnUrl. You can redirect users back to whence they came without SSL using:
return Redirect("http://" + Request.Url.Host + returnUrl);
2. Users have to logon to enable additional features:
Your users are clicking on the Logon link to enable some extra functionality that is being trimmed out on your pages if they aren't logged in. For example being able to post forum messages or view premium content.
In this case you could track where they've been before landing on the logon page. This example is based on the template application that you get with Visual Studio 2010 when you create a new MVC3 application (which you may have used as a template for your project).
In that sample application every page uses a master page Site.Master. Site.Master does a Html.RenderPartial("LogOnUserControl") to render the logon link on every page. Open the LogOnUserControl.ascx and change the code that renders the logon ActionLink to:
else
{
if(!Request.RawUrl.Contains("/Account/LogOn"))
{
Session["WhereWasI"] = Request.Url.AbsoluteUri;
}
%>
[ <%: Html.ActionLink("Log On", "LogOn", "Account") %> ]
<%
}
What we're basically doing to tracking the page the user is on if they aren't logged in. Because the Logon link is also rendered on the logon page itself we need to exclude that, hence the if statement:
if(!Request.RawUrl.Contains("/Account/LogOn"))
Then in your AccountController.cs Logon postback action method you can return the user to where they were on the site but using http insteadof https::
I've also included the redirect to non-SSL should there be a returnUrl supplied by ASP.NET Forms Authentication:
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
//
// 1. All content is protected by web forms `<authorization>`:
// If there was a return URL then go back there
//
if(!String.IsNullOrWhiteSpace(returnUrl))
{
return Redirect("http://" + Request.Url.Host + returnUrl);
}
}
else
{
//
// 2. Users have to logon to enable additional features:
//
if (Session["WhereWasI"] != null)
{
return Redirect(
Session["WhereWasI"].ToString().Replace("https", "http"));
}
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("",
"The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
The example is maybe a bit simplistic but you should be able to get the general idea.

Related

Match the content within a tag from a Non-HTML response in URL Rewrite

When going through a proxy server (A), any self-referential links sent from the apps server (B) need to be re-written to use the proxy as a host instead.
Here's an example:
Response from (B) contains: path
Proxy (A) needs to rewrite as: path
Normally, this is done by creating an outbound rule that inspects html responses for tags that contain urls, looks for references to to the apps server, and rewrites them.
Here's a normal rule GUI version:
<outboundRules>
<rule name="Outbound Links" preCondition="IsHTML" enabled="true">
<match filterByTags="A, Form, IFrame, Img, Input, Link, Script" pattern="(https?:\/\/proxy|^)\/(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="true" />
<action type="Rewrite" value="http://apps/{R:2}" />
</rule>
Where IsHTML is defined as:
<preConditions>
<preCondition name="IsHTML">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="text\/html" />
</preCondition>
The problem is that some of the page content is returned via an XHR request. Minimally, this fails the HTML precondition.
but I can expand the rule to also include content types of xhr
However, URL Rewrite still has trouble parsing the returned text into tags because it is not valid HTML.
Here's an example of what the response looks like:
|6383|updatePanel|ctl00_mainContentPlaceHolder_contentUpdatePanel|
<div id="ctl00_mainContentPlaceHolder_resultsPanel">
path
</div>
...
|0|hiddenField|__EVENTTARGET||0|hiddenField|__EVENTARGUMENT||0|hiddenField|
However, when I do this, I get the error:
Sys.WebForms.PageRequestManagerParserErrorException:
The message received from the server could not be parsed.
You cannot modify XHR requests coming back from the ASP.NET. Doing so would be to attempt a Man In The Middle Attack (which your proxy is acting as), but Microsoft has good reason to prevent.
Here's a dummy message to explore the syntax ASP.NET uses in the response:
1|#||2|52|updatePanel|ctl00_mainContentPlaceHolder_firstUpdatePanel|
<p> New Content For First Update Panel </p>
The header starts with 1|#| | and then the number of updates in the rest of the message (2)
Then each update section follows the pattern:
|char_len|update_type|id_of_field_to_update|
New contents to insert into field
The len in each section must exactly equal the number of characters to follow. So finding and replacing content in these messages is extremely fickle.
The best recommendation is to simply return a relative URL that is server agnostic so the client can be redirected relative to their current domain.

Azure Websites Dropping Custom Headers

An Azure Website I am working on inspects custom headers of incoming requests to decide what to do with the request internally. The request is being sent/received by the website server with the customer headers:
X-HEADER-1: ...
X-HEADER-2: ...
among other standard and non-standard headers.
I verified this by inspecting the FREB logs and looking at GENERAL_REQUEST_HEADERS, which correctly includes my custom headers.
When the application receives the request, those custom headers are not there. I explicitly check for one of them then throw and dump all available headers in the error message.
I have read around that Application Request Routing module can drop these headers. I tried adding this to the website's web.config but still doesn't work:
<system.webServer>
<rewrite>
<allowedServerVariables>
<add name="HTTP_X_HEADER_1" />
<add name="HTTP_X_HEADER_2" />
</allowedServerVariables>
</rewrite>
</system.webServer>
Any idea how I can whitelist my headers to let ARR/Azure let them through?
Update 1
Here is some more info.
This works locally on my dev box. I set up the site in IIS and point it to the project folder and headers are coming in and processed as expected.
It is an ASP.NET MVC website.
Here is the part of the code that reads the header. Again, this works locally.
public class BaseController : Controller
{
public AppControllerBase(...)
{
}
protected override void Initialize(RequestContext requestContext)
{
var header1Value = requestContext.HttpContext.Request.Headers["X-HEADER-1"];
if (string.IsNullOrEmpty(header1Value))
{
var stringBuilder = new StringBuilder();
// append all headers to stringBuilder
var errorMessage = string.Format("SiteId header is not set. Headers: {0}", stringBuilder);
throw new HttpRequestException(errorMessage);
}
base.Initialize(requestContext);
}
...
}
Update 2
I just deployed the same app as an azure cloud service and it worked well. The headers were received and the app read them successfully. Something with web apps is not letting those headers through.
The answer that worked for me was in the comments. Credit goes to #Tarek Ayna.
The custom headers are transmitted when you set X-LiveUpgrade to 0.
For example:
<httpProtocol>
<customHeaders>
<add name="X-LiveUpgrade" value="0" />**
<!-- Prevent iframes -->
<add name="X-Frame-Options" value="SAMEORIGIN" />
<add name="X-XSS-Protection" value="1" />
</customHeaders>
</httpProtocol>
One possibility is to disable ARR if your services are stateless... to do that:
(Inside web.config)
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Arr-Disable-Session-Affinity" value="True" />
</customHeaders>
</httpProtocol>
</system.webServer>

IIS File Download without Extension

I've got a .NET Web API 2 application, I've hooked up the api to send me a file id and from there I get the unique file from the server.
Example:
Download
I need it to be a unique id since there could be multiples of the file in the repo. However, when I try to click the download button I get a :
HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
I was thinking a re-write rule might be a good action, but I dont really want to rewrite it, i just want to allow anything /api/attachment no matter what the rest.
I've already got one rewrite rule since my page is a single-page-application to direct responses to the Default.cshtml like:
<rewrite>
<rules>
<rule name="Default" stopProcessing="true">
<match url="^(?!Lib|api|Assets|Views|Directives|Services|Controllers|signalr).*" />
<action type="Rewrite" url="Default.cshtml" />
</rule>
</rules>
</rewrite>
any thoughts on best way to achieve this?
I was able to resolve by creating an iframe and setting the src to the download like:
$("body").append('<iframe name="downloadFrame" id="download_iFrame" style="display:none;" src="" />');
and then in the C# I set the header like:
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");

IIS 7.5 URL Redirect direct link to media page

I didn't think my implementation through and now I am stuck. I am using IIS 7.5, URL Rewrite 2.0 and Jplayer.
My current implementation is that I have users upload audio files to my server. To listen to these audio files, users are given a direct link to either play in the browser or through Android/iOS in app media players.
Now I have created a landing page that I want to redirect those users direct linking to the audio file to. The landing page is using Jplayer.
The problem is that I have to use the direct link to the audio file to get jplayer to play it. Since I am redirecting the direct link, it is failing to load the media url.
This also becomes a problem since my Android and iOS apps direct link to the .mp3 in order to play the file in their AV players. Is there anyway around this? Is there a better implementation? Not sure what to do at this point.
Jplayer:
$("#jquery_jplayer_1").jPlayer({
ready: function (event) {
$(this).jPlayer("setMedia", {
mp3: "http://192.168.0.28/flows/t/test/test1334187052069.mp3"
}).jPlayer("play"); // auto play;
},
swfPath: "js",
supplied: "mp3",
errorAlerts: true,
warningAlerts: true,
wmode: "window"
});
IIS 7.5 Redirect Rule:
<rewrite>
<rules>
<rule name="FlowURLs">
<match url="^flows/[_0-9a-zA-Z-]+/[_0-9a-zA-Z-]+/([._0-9a-zA-Z-]+)" />
<action type="Redirect" redirectType="Found" url="music.html?flow={R:1}" />
</rule>
</rules>
</rewrite>
A possible solution might be to check the HTTP accept header and see if it's a browser expecting to load a page. If so then you redirect to your player landing page. Otherwise you leave the request as is and let it load the audio file directly.
You can check the accept header with a conditional:
<rewrite>
<rules>
<rule name="FlowURLs">
<match url="^flows/[_0-9a-zA-Z-]+/[_0-9a-zA-Z-]+/([._0-9a-zA-Z-]+)" />
<conditions>
<add input="{HTTP_ACCEPT}" pattern="text/html" />
</conditions>
<action type="Redirect" redirectType="Found" url="music.html?flow={R:1}" />
</rule>
</rules>
</rewrite>

Deny file access but server properly in IIS

I have a js file that includes in the master page.
I want to deny the file access when user type the direct link in the browser address bar.
I've tried the URL filtering IIS,like:
<security>
<requestFiltering>
<denyUrlSequences>
<add sequence="Scripts/Foo/bar.min.js" />
</denyUrlSequences>
</requestFiltering>
</security>
This does work, when i type 'localhost://blah/Scripts/Foo/bar.min.js' I get blocked.
but the page whitch need this js file can not render.
Does anyone have a workaround? Thanks in advance!
I finnaly found this.
and added these sections to web.config:
<security>
<requestFiltering>
<filteringRules>
<filteringRule name="protectjs" scanUrl="true" scanQueryString="true">
<scanHeaders>
<clear />
<add requestHeader="Accept" />
</scanHeaders>
<appliesTo>
<clear />
<add fileExtension=".js" />
</appliesTo>
<denyStrings>
<clear />
<add string="text/html" />
</denyStrings>
</filteringRule>
</filteringRules>
</requestFiltering>
</security>
It works at present, though I know this way is not that reliable.
Not 100% sure but I don’t think there is a way to do this. When browser renders your page it actually sends the same request to the server as the one you do when you manually type in JS file in the browser.
IIS doesn’t have means to distinguish requests you make and the one browser makes in the background while processing your page.
One thing that might work is
adding some kind of ID to your JS file like this Scripts/Foo/bar.min.js?ID=E3CF305B-4444-E011-8FD2-1CC1DEE89A7F
ID is time limited and works only 20 seconds or so after it’s created (enough time for browser to load the page)
creating custom handler that will parse ID and decide if server should return the request or not
So when someone types Scripts/Foo/bar.min.js handler will reject the request but it will also reject request when expired ID is used.
However this is most probably going to be difficult to maintain and also performance intensive for your server.
If you want to hide your JS file why not obfuscate it.
Use HttpModule and Check for HttpContext.Current.Request.UrlReferrer. it will always be null for direct access to the .js file.
public class JSAccessModule : IHttpModule
{
public void Init(System.Web.HttpApplication Appl)
{
Appl.BeginRequest += new System.EventHandler(Rewrite_BeginRequest);
}
public void Rewrite_BeginRequest(object sender, System.EventArgs args)
{
//Block if direct Url is accessed
if (HttpContext.Current.Request.UrlReferrer == null)
{
HttpApplication App = (HttpApplication)sender;
string path = App.Request.Path;
string strExt = System.IO.Path.GetExtension(path);
if (strExt == ".js")
{
HttpContext.Current.Response.Redirect("~/AccessDenied.html");
}
}
}
public void Dispose() { }
}

Resources