best way to redirect .cfm locations to new locations .net mvc 3 IIS 7.5 - asp.net-mvc

I am trying to determine the best way to redirect a set of files I have from an old site (coldfusion) to new locations on my new site (asp.net mvc 3).
I want to redirect these pages with a 301 status to let engines know that this is a permanent change.
Currently I have in my web.config a custom errors section set up to redirect any 404 to the home page, which works great for all of the old links that are no longer in service, but it's sending a 302 status which I don't want and it's sending all my redirects to home thereby not giving me the SEO that was getting from my old links.
I thought about just adding .cfm as a module mapping in IIS to my .net Isapi and creating all of my pages as cfm with a redirect by adding headers, but then realized that'd be a LOT of work...
is there another "easier" solution to achieve this?

Yes, HttpHandler. in your web.congig you register it to handle all *.cfm URLs ( http://msdn.microsoft.com/en-us/library/46c5ddfy%28v=vs.71%29.aspx ), and implementation will be just your 301 redirect. 10 lines of code at most.
using System.Web;
public class CfmHandler : IHttpHandler {
public void ProcessRequest(HttpContext context) {
// redirect here, you have HttpContext ready
}
public bool IsReusable {
// To enable pooling, return true here.
// This keeps the handler in memory.
get { return true; }
}
}
More at : http://msdn.microsoft.com/en-us/library/5c67a8bd%28v=vs.71%29.aspx

Related

Best Practice for denying/killing specific URL requests ASP.Net IIS 7.5

I have an mvc/forms hybrid webapplication hosted on a windows 2008 r2 instance on Azure. The webserver is IIS 7.5 . For the last 4-5 months my server is getting absolutely hammered by vulnerability scanners checking for php related vulnerabilities. example:
The controller for path '/wp-login.php' was not found or does not implement IController.
from Elmah
So I've gone in and specifically filtered .php and .cgi file extension requests in IIS 7.5 which is working great. However i am still getting hammered for requests like:
The controller for path '/admin/Cms_Wysiwyg/directive/' was not found or does not implement IController.
The controller for path '/phpmyadmin2018/' was not found or does not implement IController.
etc. etc. It's more an annoyance as everything is logged, a 404 is returned and it's all a useless resource throwaway.
Through Elmah i've queried a distinct list of URLs related to all these requests. What is the best way to short-circuit these requests? It would be good if i could optionally ban the IP's but right now there are 700 unique IPs making these requests in the last 3 months alone. Main priority is to just short circuit the requests from the dictionary of URLs I know are bogus and avoid the logging and response from my webserver. Thanks!
half pseudo code, but I think it will be helpful;
in Global.asax.cs:
public class MvcApplication : HttpApplication
{
protected void Application_BeginRequest(Object sender, EventArgs e)
{
var url = HttpContext.Current.Request.Url.AbsoluteUri;
if(checkUrl(url)){
// your code here
}
if (UserIsBanned(GetUserIp()))
{
Response.Write("ban");
Response.End();
}
}
private string GetUserIp()
{
return HttpContext.Current.Request.UserHostAddress;
}

How to intercept a non SSL connection in onAuthorization method?

I am using MVC4, ASP.NET 4.5, C#
I want to add code to my onAuthorization method in global.asa to identify whether the connection is SSL or not, if not then to issue a permanent redirect to a SSL domain. I am using another domain that is SSLed.
In my Login controller I have code along the lines of :
[HttpGet]
public virtual ActionResult LogOn(string Error="")
{
if (Request.IsSecureConnection)
{
return View(viewModel);
}
else
{
return RedirectPermanent("https://www.mysslapp.com/logon");
}
}
I want to add this same functionality to the onAuthorization method so that when actions, covered by the [authorize] filter are called then they must also be accessed by a SSL connection. So I believe my global.asa code needs changing. However it will not accept "Request.IsSecureConnection", as the context is different.
My "pseudo" Global.asa onAuthorization routine is:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
RedirectPermanent("https://www.mysslapp.com/logon");
}
}
The above will not work, but it describes what I am trying to achieve. I would appreciate help on how I need to change the above code to make it work, such that any "adventurous" use of urls, on a non SSL connection will automatically redirect to the SSL site logon page.
Thanks in advance.
EDIT1
Think I have the first bit:
filterContext.HttpContext.Request.IsSecureConnection
EDIT2
if (filterContext.HttpContext.Request.IsSecureConnection)
{
base.OnAuthorization(filterContext);
}
else
{
filterContext.Result = new RedirectResult("https://www.mysslapp.com");
}
I think your main issue is that you have 2 separate concerns and you are trying to achieve both in one go. Your 2 concerns are:
Making every URL of domain A 301 redirect to the same URL on domain B
Making domain B redirect all requests to HTTPS
The first one is really easy. Create a new IIS site for domain A, install the IIS rewrite module, add this web.config to the site, and then adjust your DNS (if necessary) to make the site live.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpRedirect
enabled="true"
destination="https://www.mysslapp.com$V$Q"
exactDestination="true"
httpResponseStatus="Permanent" />
<httpProtocol>
<redirectHeaders>
<!-- This is to ensure that clients don't cache the 301 itself -
this is dangerous because the 301 can't change when put in place
once it is cached -->
<add name="Cache-Control" value="no-cache"/>
</redirectHeaders>
</httpProtocol>
</system.webServer>
</configuration>
NOTE: The above configuration is for IIS 7.5. I am not sure if it will work on other versions of IIS.
Now none of the users of domain B will incur the performance hit of the redirect rule, so all is good.
For redirecting your users of domain B to HTTPS, you should not use 301. Why? Because not all browsers respond to 301.
You should also not only allow HTTPS on the domain, but allow both HTTP and HTTPS. Why? Because your users that type myssldomain.com will get an ugly error message instead of a fast redirect to you HTTPS protected site.
So the simple solution to making your whole site redirect to HTTPS is to use the RequireHttps attribute and register it as a global filter. The RequireHttpsAttribute uses a 302 redirect when a request comes in that is not secure.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new RequireHttpsAttribute());
filters.Add(new HandleErrorAttribute());
}
}
As for the AuthorizeAttribute, you should leave that out of the equation altogether unless you need some customization that deals with authorization.

MVC 5 prevents access to content via Iframe

Ever since the upgrade from MVC4 to MVC5, I have noticed an extra server header added to my web pages:
X-Frame-Options: SAMEORIGIN
I understand security benefits of adding this tag, but one of the pages is meant to be included inside an iframe from other projects (on other domains), this extra header is preventing this.
I have verified it is not the hosting IIS7 server that is adding the header, and when I downgraded back to MVC4 - the header is gone.
Does anyone know how to remove this default from MVC5?
MVC5 automatically adds the HTTP header X-Frame-Options with SAMEORIGIN. This prevents your site from being loaded into an iframe.
But we can turn this off in Application_Start in the Global.asax.cs.
Example
protected void Application_Start()
{
AntiForgeryConfig.SuppressXFrameOptionsHeader = true;
}
Update
I have written a post about this MVC5 prevents your website being loaded in an IFRAME
Try something like this in Global.asax:
protected void Application_PreSendRequestHeaders(object sender, EventArgs e)
{
HttpContext.Current.Response.Headers.Remove("X-Frame-Options");
}
EDIT:
Look at answer of Colin Bacon. It is more correct than mine.
In short - don't remove this header if you don't want to run your site in IFRAME because it will open forgery vulnerability. But if you still want to remove it - use AntiForgeryConfig.SuppressXFrameOptionsHeader = true; in Application_Start, it is more cleaner way for doing this.
If you want a little more flexibility, here's an ActionAttribute that adds/removes headers based on a whitelist. If the referrer isn't in the whitelist, then the SAMEORIGIN header is left in place. I was going to paste the code, but SO complains about the length.
https://long2know.com/2016/06/asp-net-anti-forgery-xframe-options/
Personally, I don't think it's a good idea to disable the X-Frame-Options across the whole site.I've created an ASP.NET MVC filter which removes this header and I simply apply this filter to the portions of the site that are used in iFrames e.g. widgets.
public class AllowDifferentOrigin : ActionFilterAttribute, IActionFilter
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Headers.Remove("X-Frame-Options");
base.OnResultExecuted(filterContext);
}
}
Here is a replacement Extension method for the HtmlHelper class. It will first clear all X-Frame-Options headers and then add back a single X-Frame-Options header normally added by the built-in AntiForgeryToken method.
This technique respects the SuppressXFrameOptionsHeader setting, but has the downside of removing all previously added X-Frame-Options headers, even those with values other than SAMEORIGIN.
public static MvcHtmlString AntiForgeryTokenSingleHeader(this HtmlHelper html)
{
string token = AntiForgery.GetHtml().ToString();
HttpResponseBase httpResponse = html.ViewContext.HttpContext.Response;
httpResponse.Headers.Remove("X-Frame-Options");
if (!AntiForgeryConfig.SuppressXFrameOptionsHeader)
{
httpResponse.AddHeader("X-Frame-Options", "SAMEORIGIN");
}
return new MvcHtmlString(token);
}

ASP.net MVC SPA routing

I'm planning to build a SPA with asp.net MVC4 but I'm not quite sure how I have to Setup my Project because of the Routing. Most SPA's work with hashrouting like this mypage/#/News/today for instance.
What would happen if the browses directly to mypage/News/today if I haven't specified a Controller named News with an action today?
The App should handle both types of Routing, how can I achieve this?
Do I have to build my App in a classic way, like Adding several Controllers with appropriate Actions and views and also build a clientside MVC structure with knockout, jquery etc?
You'll have to let all routes to "pages" fall through to let your SPA handle them (including essentially fake 404s if it's not to a real page in your SPA), but at the same time, need to make sure that you get the correct responses for API calls and/or file requests.
Below is the setup I have (I am using Vue as the js framework but that doesn't matter much for this, and not at all for the server-side piece).
First, add this to your Startup.cs, in addition to your default route setup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.Use(async (context, next) =>
{
await next();
var path = context.Request.Path.Value;
// If there's no available file and the request doesn't contain an extension, we're probably trying to access a page
if (context.Response.StatusCode == (int)HttpStatusCode.NotFound && !Path.HasExtension(path) && !path.StartsWith("/api"))
{
context.Request.Path = "/Home/SpaRedirect"; // attempts to redirect to the URL within the SPA
context.Response.StatusCode = (int)HttpStatusCode.OK; // Make sure we update the status code, otherwise it returns 404
await next();
}
});
...
}
So the newly added SpaRedirect to HomeController looks like this, and just stores the requested URL in ViewData...
public IActionResult SpaRedirect()
{
ViewData["RequestUrl"] = HttpContext.Request.Path;
return View("Index");
}
Then in Index.cshtml, just capture that requested url in session storage so we have it available on the client-side:
<script src="~/dist/main.js" asp-append-version="true">
sessionStorage.setItem("redirectAttempt", #ViewData["RequestUrl"]);
</script>
Then in your boot script file (the entry-point for your SPA), add something like:
let redirectAttemptUrl = sessionStorage.getItem("redirectAttempt");
if (redirectAttemptUrl) {
router.push(redirectAttemptUrl);
sessionStorage.removeItem("redirectAttempt");
}
Which just checks for the presence of a requested url, and then the SPA's router attempts to navigate to it (in the example above it is a vue-router), then removes it from storage.
So this way, if a user attempts to navigate directly to a URL by entering it in the url bar (or via a bookmark) the app will load and take them to the right place, IF it exists... which takes us to the last piece...
Finally, you have to handle "404s" within your SPA, which is done by adding a catch-all route to your routes defs that takes user to a 404 component page you set up, which for Vue would look like this:
// adding an explicit 404 path as well for programmatically handling when something is not found within the app, i.e. return this.$router.push('/404')
{ path: '/404', component: NotFound, name: '404', alias: '*' }, // remove alias to not show the actual url that resulted in our little 404 here
{ path: '*', redirect: '/404' }, // this is the catch-all path to take us to our 404 page
Caveat: I'm no expert so could be missing something, would love additional comments on how to improve this. One thing that this doesn't handle is if the user is ALREADY in the SPA and for some reason edits the URL directly to navigate to someplace else, it would still trigger a server call and full reload, which ideally wouldn't be the case, but this is a pretty trivial issue I'd say.

Why is HttpContext.Request.Url not matching what's in the address bar?

Ok, so I want to force https/ssl on parts of my ASP.NET MVC site. Found lots of great sources including an ActionFilter here: http://blog.tylergarlick.com/index.php/2009/07/mvc-redirect-to-http-and-https/
Whenever I enable the ActionFilter however I end up in a redirect loop. The problem is that if I type https://www.mysite.com/ into the address bar, request.url is always equal to http://www.mysite.com/.
The code for the action filter is below and to my knowledge I'm not doing any url rewriting, redirecting, custom routing or modifying the url beyond the standard setup. Are there any common/uncommon reasons why this might be happening and/or any workarounds or fixes? The site is currently hosted at NetworkSolutions - is there a chance it's related to IIS configuration? Any assistance would be much appreciated.
public class RedirectToHttps : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Helpers just so I don’t have to type so much
var request = filterContext.HttpContext.Request;
var response = filterContext.HttpContext.Response;
// Make sure we’re in https or local
if (!request.IsSecureConnection && !request.IsLocal)
{
string redirectUrl = request.Url.ToString().Replace("http:", "https:");
response.Redirect(redirectUrl);
}
base.OnActionExecuting(filterContext);
}
}
Even though you're not doing any explicit URL rewriting, it is done by the ASP.NET MVC engine.
You can probably retrieve the original URL with HttpContext.Request.RawUrl

Resources