How to secure access to SWF file using ASP.NET? - asp.net

We have a swf file that we want to secure and make available only to authorized users.
I embedded the file in an aspx page and that works fine, since ASP.NET handles the aspx page, I can use ASP.NET authorization features and in the web.config restrict the access to roles="AllowedUsers" for example.
However smart users could still get to the file by accessing directly for example www.mysite/flash.swf. We want to make that kind of access secure.
Any help would be greatly appreciated!
Thanks!

Aristos,
You were right. Last afternoon just before I went home I tried creating a custom HTTP handler. And it worked nice. :-) Thanks for answering +1
public class CustomFlashHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (!context.User.Identity.IsAuthenticated)
{
context.Response.Redirect("Default.aspx?ReturnUrl=%2felVideo.aspx");
context.Response.StatusCode = 401;
return;
}
var url = context.Request.CurrentExecutionFilePath;
if (string.IsNullOrEmpty(url)) return;
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.AddHeader("Content-Disposition", string.Format("filename={0}", url));
HttpContext.Current.Response.AddHeader("Content-Type", "application/x-shockwave-flash");
HttpContext.Current.Response.WriteFile(url);
HttpContext.Current.Response.End();
}
public bool IsReusable
{
get { return false; }
}
}
Like Aristos said, you have to map ASP.NET to handle .swf files in IIS.
alt text http://www.freeimagehosting.net/uploads/30424ac60a.png
Then add the custom mapping in the application's web.config
<httpHandlers>
<add verb="*" path="*.swf" type="XXXXX.Web.XXXXX.CustomFlashHandler" validate="false" />
</httpHandlers>
1: href=http://www.freeimagehosting.net/>http://www.freeimagehosting.net/uploads/30424ac60a.png
1: a href=http://www.freeimagehosting.net/>http://www.freeimagehosting.net/uploads/30424ac60a.png border=0 alt="Free Image Hosting">

I think that the most easy and fast solution is to Map this extention (.swf) to handle by asp.net.
I do not know if its works, because I do not have done that, but you can give it a try.
One other way is to place this files, somewhere hidden, or with complex name, and use an .ashx file to just read and send them. In the .ashx file you need to set the correct Response.ContentType for the flash, and just read and send the correct file.

Related

How can I manage robots.txt in Azure app service with a staging slot

I have an ASP.NET MVC app running in an Azure app service with one staging slot, and a build and release pipeline in VSTS.
I want the production instance to have Allow / in robots.txt and Disallow / in the staging slot at all times.
Currently we are changing robots.txt manually every time we do a swap but this is error prone
How can I automate this process?
To solve this problem I did consider creating the robots.txt file dynamically based on app settings set in the Azure portal (set to stay with the slot), however this won't work since after the swap happens prod will have the staging Disallow rule.
Can anyone advise the best way to manage this?
Robots are mainly used by search engines to crawl and check pages on the public websites. Staging and other deployment slots are not public (and should not be public — unless you have a good reason for that), and thus it doesn't make much sense to configure and manage it. Secondly, in most cases I would recommend to redirect any public request to your production slot and keep staging offline and active for internal use cases only. This would also help you to manage the analytics and logs coming from the public only, and not being polluted with internal and deployment slots stuff.
Anyways, if you are still inclined to do this, then there is one way that you can manage this. Write your own routing to control the robots file, and then render a content-type: text/plain page, which would be dynamic based on whether it is a staging or production request. Something like this,
// Create the robots.txt file dynamically, by controlling the URL handler
[Route("robots.txt")]
public ContentResult DynamicRobotsFile()
{
StringBuilder content = new StringBuilder();
content.AppendLine("user-agent: *");
// Check the condition by URL or Environment variable
if(allow) {
content.AppendLine("Allow: /");
else {
content.AppendLine("Disallow: /");
}
return this.Content(stringBuilder.ToString(), "text/plain", Encoding.UTF8);
}
This way you can manage how the robots.txt is created and you would be able to control the allow disallow for the robots. You can create a separate controller or an action only in the home controller of your app.
Now that you know how to do, you can setup the environment variables for the production/staging slots to check other requirements.
I use below code and It works for me
[Route("robots.txt")]
public ContentResult DynamicRobotsFile()
{
StringBuilder content = new StringBuilder();
if (System.Configuration.ConfigurationManager.AppSettings["production"] != "true")
{
content.AppendLine("user-agent: *");
content.AppendLine("Disallow: /");
}
return this.Content(content.ToString(), "text/plain", Encoding.UTF8);
}
web.config
<appSettings>
<add key="production" value="false" />
</appSettings>
<system.webServer>
<handlers>
<add name="RobotsTxt" path="robots.txt" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
EDITED
I use this version now.
[Route("/robots.txt")]
public ContentResult RobotsTxt()
{
var sb = new StringBuilder().AppendLine("User-Agent: *");
if (_env.IsProduction())
{
sb.AppendLine("Allow: /");
sb.AppendLine("Disallow: /admin");
}
else
{
sb.AppendLine("Disallow: /");
}
sb.AppendLine(string.Empty);
sb.AppendLine($"Sitemap: {this.Request.Scheme}://{this.Request.Host}/sitemap.xml");
return this.Content(sb.ToString(), "text/plain", Encoding.UTF8);
}
and I use IWebHostEnvironment to detect prod or not
public class SeoController : Controller
{
private readonly IWebHostEnvironment _env;
public SeoController(IWebHostEnvironment env)
{
_env = env;
}
}

ASP.NET MVC5 Customised Inbound Routing

I'm "playing" around with custom inbound URL routing and have came across a problem.
When I pass my custom route a URL to examine, that ends in *.+, my class is not fired when i submit the request.
An example URL would be "~/old/windows.html"
When I step through this in the debugger, my RouteBase implementation doesn't fire. If i edit the url that i pass to the constructor of my route to try to match against "~/old/windows", my implemetation is fired as expected.
Again, If i change the url ro examine to "~/old/windows." the problem reoccurs.
My Route Implementation is below :-
public class LegacyRoute : RouteBase
{
private string[] _urls;
public LegacyRoute(string[] targetUrls)
{
_urls = targetUrls;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (_urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase))
{
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "Legacy");
result.Values.Add("action","GetLegacyURL");
result.Values.Add("legacyURL", requestedURL);
}
return result;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
In the RoutesConfig file I have registered my route like so :-
routes.MapMvcAttributeRoutes();
routes.Add(new LegacyRoute(new[]{"~/articles/windows.html","~/old/.Net_1.0_Class_Library"}));
Can anyone point out why there is a problem?
By default, the .html extension is not handled by .NET, it is handled by IIS directly. You can override by adding the following section in Web.config under <system.webServer> -
<handlers>
<add name="HtmlFileHandler" path="*.html" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
As pointed out here. The above will route EVERY .html file request to .NET, you might want to be more specific by providing a more complete path if you don't want your routing to handle every .html file.
I've found the problem, and I'm sure this will help out a lot of fellow developers.
The problem is with IIS Express that is running via Visual Studio.
There is a module configured in the applicationhost.config called :-
UrlRoutingModule-4.0
This is how it looks in file :-
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />
You need to set the preCondition Parameter to "".
To do this :-
Run you app via Visual Studio
Right click on IIS Express in your system tray, select "Show All Applications"
Click on the project you wish to edit, then click the config URL.
Open the file with Visual Studio, Locate the module and ammend.
Hope this helps anyone else, who ran into a similar problem.

Http Handler is working in iis express and not working in iis server

I am going to implement HttpHandler in order to allow file downloading from my site based on session values. If the session exist allow the user to download the file otherwise redirect to index page which is the login page for the site. My code is working perfect in iis express when I run my website in iis server the handler is not working.
For IIS express the web.config file has the following sections which I have added. The below configuration is working in iis express.
<system.web>
<httpHandlers>
<add verb="*" path="*.pdf" type="QDMS.FileHandler" />
Same add tag for all the files to restrict downloading without session.
</httpHandlers>
</system.web>
The configurations for IIS servers which is not working is below.
<system.webServer>
<handlers>
<add name="Files" path="*.pdf,*.doc,*.docx,*.rar,*.zip,*.ppt,*.pptx,*.jpg,*.png,*.bmp,*.gif,*.html,*.htm,*.pps" verb="*" type="QDMS.FileHandler" resourceType="Unspecified" requireAccess="script" />
</handlers>
</system.webServer>
My File handler is below
using System;
using System.Web;
using System.Web.SessionState;
using QDMS.Old_App_Code;
namespace QDMS
{
public class FileHandler : IHttpHandler, IReadOnlySessionState
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
if (!CheckWetherTheRequestForFileExistOrNot(context)) return;
if (CheckUsersForFileDownloading(context))
context.Response.Redirect("~/index.aspx");
else
{
var rawURL = context.Request.RawUrl;
var dotIndex = rawURL.LastIndexOf(".", System.StringComparison.Ordinal);
var ext = rawURL.Substring(dotIndex);
context.Response.ClearContent();
context.Response.ClearHeaders();
context.Response.ContentType = MIMEEType.Get(ext);
context.Response.AddHeader("Content-Disposition", "attachment");
context.Response.WriteFile(rawURL);
context.Response.Flush();
}
}
public bool CheckWetherTheRequestForFileExistOrNot(HttpContext context)
{
string url = context.Request.RawUrl.ToLower().Trim();
if (url.Contains(".pdf") || url.Contains(".xls") || url.Contains(".xlsx") || url.Contains(".jpg") ||
url.Contains(".bmp") || url.Contains(".rar") || url.Contains(".doc") || url.Contains(".docx") ||
url.Contains(".png") || url.Contains(".gif") || url.Contains(".pptx") || url.Contains(".zip") ||
url.Contains(".ppt") || url.Contains(".pps") || url.Contains(".htm") || url.Contains(".html"))
return true;
else
return false;
}
public bool CheckUsersForFileDownloading(HttpContext context)
{
return (context.Session["FrontHiddenID"] == null) && (context.Session["HiddenID"] == null);
}
}
}
I am sure that in the section in the web.config file is not correct that is why it is not working. So I need suggestions to rectify my handlers section in web.config file.
Any advice and help regarding this issue will be higly appreciated
Your IIS handler should be like this :
<add name="Files" path="*.pdf" verb="*" type="QDMS.FileHandler" resourceType="Unspecified" requireAccess="Script" />
Two differences with your version :
only one file mask, you should register a handler for each file type
requireAccess="Script" with 'Script' having an upper-case 'S'
Hope this will help
To map a file-name extension in IIS 7.0 running in Classic mode
Open IIS Manager.
Expand the node for the Web server computer, expand Sites, and then expand Default Web Site.
Select the node for your application.
The Features View pane is displayed.
In Features View, double-click Handler Mappings.
On the Actions pane, click Add Script Map.
The Add Script Map dialog box is displayed.
In the Add Script Map dialog box, specify the following:
o Request Path. The name or file-name extension to map.
o Executable. The path of the .exe or .dll file that will handle the request. For Classic mode, specify the ASP.NET ISAPI extension (Aspnet_isapi.dll).
o Name. A descriptive name.
Click OK to close the Add Script Map dialog box.
Open the Web.config file for the application.
Locate the httpHandlers element of the system.web section and add an entry for the file-name extension.
To map a file-name extension in IIS 7.0 running in Integrated mode
Follow steps 1 through 3 of the previous procedure.
On the Actions pane, click Add Managed Handler.
The Add Managed Handler dialog box is displayed.
In the Add Managed Handler dialog box, specify the following:
o Request Path. The file name or file-name extension to map.
o Type. The type (class) name of the managed handler. If the handler is defined in the App_Code folder of the ASP.NET application, its type name will appear in the drop-down list.
o Name. A descriptive name.
Click OK to close the Add Managed Handler dialog box.

Prevent FormsAuthenticationModule of intercepting ASP.NET Web API responses

In ASP.NET the FormsAuthenticationModule intercepts any HTTP 401, and returns an HTTP 302 redirection to the login page. This is a pain for AJAX, since you ask for json and get the login page in html, but the status code is HTTP 200.
What is the way of avoid this interception in ASP.NET Web API ?
In ASP.NET MVC4 it is very easy to prevent this interception by ending explicitly the connection:
public class MyMvcAuthFilter:AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest() && !filterContext.IsChildAction)
{
filterContext.Result = new HttpStatusCodeResult(401);
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.HttpContext.Response.SuppressContent = true;
filterContext.HttpContext.Response.End();
}
else
base.HandleUnauthorizedRequest(filterContext);
}
}
But in ASP.NET Web API I cannot end the connection explicitly, so even when I use this code the FormsAuthenticationModule intercepts the response and sends a redirection to the login page:
public class MyWebApiAuth: AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if(actionContext.Request.Headers.Any(h=>h.Key.Equals("X-Requested-With",StringComparison.OrdinalIgnoreCase)))
{
var xhr = actionContext.Request.Headers.Single(h => h.Key.Equals("X-Requested-With", StringComparison.OrdinalIgnoreCase)).Value.First();
if (xhr.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
{
// this does not work either
//throw new HttpResponseException(HttpStatusCode.Unauthorized);
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
return;
}
}
base.HandleUnauthorizedRequest(actionContext);
}
}
What is the way of avoiding this behaviour in ASP.NET Web API? I have been taking a look, and I could not find a way of do it.
Regards.
PS: I cannot believe that this is 2012 and this issue is still on.
In case someone's interested in dealing with the same issue in ASP.NET MVC app using the Authorize attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class Authorize2Attribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new HttpStatusCodeResult((int) HttpStatusCode.Forbidden);
}
else
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
base.HandleUnauthorizedRequest(filterContext);
}
}
}
This way browser properly distinguishes between Forbidden and Unauthorized requests..
The release notes for MVC 4 RC imply this has been resolved since the Beta - which are you using?
http://www.asp.net/whitepapers/mvc4-release-notes
Unauthorized requests handled by ASP.NET Web API return 401 Unauthroized: Unauthorized requests handled by ASP.NET Web API now return a standard 401 Unauthorized response instead of redirecting the user agent to a login form so that the response can be handled by an Ajax client.
Looking into the source code for MVC there appears to be an functionality added via SuppressFormsAuthRedirectModule.cs
http://aspnetwebstack.codeplex.com/SourceControl/network/forks/BradWilson/AspNetWebStack/changeset/changes/ae1164a2e339#src%2fSystem.Web.Http.WebHost%2fHttpControllerHandler.cs.
internal static bool GetEnabled(NameValueCollection appSettings)
{
// anything but "false" will return true, which is the default behavior
So it looks this this is enabled by default and RC should fix your issue without any heroics... as a side point it looks like you can disable this new module using AppSettings http://d.hatena.ne.jp/shiba-yan/20120430/1335787815:
<appSettings>
<Add Key = "webapi:EnableSuppressRedirect" value = "false" />
</appSettings>
Edit (example and clarification)
I have now created an example for this approach on GitHub. The new redirection suppression requires that you use the two correct "Authorise" attribute's; MVC Web [System.Web.Mvc.Authorize] and Web API [System.Web.Http.Authorize] in the controllers AND/OR in the global filters Link.
This example does however draw out a limitation of the approach. It appears that the "authorisation" nodes in the web.config will always take priority over MVC routes e.g. config like this will override your rules and still redirect to login:
<system.web>
<authentication mode="Forms">
</authentication>
<authorization>
<deny users="?"/> //will deny anonymous users to all routes including WebApi
</authorization>
</system.web>
Sadly opening this up for some url routes using the Location element doesn't appear to work and the WebApi calls will continue to be intercepted and redirected to login.
Solutions
For MVC applications I am simply suggest removing the config from Web.Config and sticking with Global filters and Attributes in the code.
If you must use the authorisation nodes in Web.Config for MVC or have a Hybrid ASP.NET and WebApi application then #PilotBob - in the comments below - has found that sub folders and multiple Web.Config's can be used to have your cake and eat it.
I was able to get around the deny anonymous setting in web.config by setting the following property:
Request.RequestContext.HttpContext.SkipAuthorization = true;
I do this after some checks against the Request object in the Application_BeginRequest method in Global.asax.cs, like the RawURL property and other header information to make sure the request is accessing an area that I want to allow anonymous access to. I still perform authentication/authorization once the API action is called.

HttpModule doesn't seem to work

I've created a simple HttpModule to log the uses of my existing webservice. There's a dll containing a single class
public class TrackingModule : System.Web.IHttpModule
{
public TrackingModule(){}
public void Init(System.Web.HttpApplication context)
{
context.BeginRequest+=new EventHandler(context_BeginRequest);
}
public void Dispose()
{
}
private void context_BeginRequest(object sender, EventArgs e)
{
try
{
Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManager.Publish( new Exception("Log attept") );
HttpApplication app = (HttpApplication)sender;
string method = app.Request.RawUrl;
SaveUseToDatabase( app.Request.UserHostAddress, method );
}
catch( Exception ex )
{
try
{
Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManager.Publish( ex );
}
catch{}
}
}
}
After compiling the dll I add it to webservice's bin folder and in webservice's web.config I add:
<system.web>
<httpModules>
<add name="TrackingModule" type="WebserviceTrackingModule.TrackingModule, WebserviceTrackingModule"/>
This works fine on my computer, but when I copy it to production server, nothing happens. No new entries in database, no entries logged by ExceptionManager. As if it's not there at all.
What can I be missing?
Edit:
After performing another test I can add that it works when I add it for a webservice that has it's own top-level virtual directory. It doesn't work for webservices that reside in virtual directories that are subfolders of another virtual directory.
I know that HttpModules settings are being inherited by subdirectories, but it looks like the existence of parent directory gets in the way.
I believe I have found a better solution. Attach the module at runtime instead of in the web config. Check out Rick Strahl's blog post for the details.
OK, I'm answering my own question.
It doesn't work when you define <httpModules> in subdirectory's web.config, even when the subdirectory is configured as an application. The only solution I found so far is to define them within <location> tag in web.config of root application (parent directory).
I don't like it :(
I found the answer to this question in http://forums.iis.net/t/1151924.aspx
oh well, a little
process-of-elimination never fails
me.
After staring at the 3.5-related
web.config code, I realized that my
module needed to be added to the new
section:
<system.webserver>
<modules>
instead of system.web...at least it's
working now.
So to translate that:
If you are having a problem with httphandlers
add your handler to the modules node in system.webserver and see if that works
Copy the format used for scriptmodule.
Does this work?
<add name="TrackingModule" type="WebserviceTrackingModule.TrackingModule" />
And is the context_BeginRequest method definitely being called for each request?

Resources