I work on a site that generates dynamic images for each specific user. Sometimes these images contain depictions of very sensitive data. Lately we have started to see requests for images that belong to a different user in the form of
http://myapp/images/someuid/image1.jpg
obviously, someone figured out they could access another users images if they created the proper URL. we store the images to the file system to help reduce bandwidth.
how can we protect this - some sort of http handler?
is there a way of serving the image to take advantage o -f caching without having to write it to the file system and letting IIS do the dirty work?
Use an .ashx:-
TimeSpan maxAge = new TimeSpan(0, 15, 0); //!5 minute lifetiem.
context.Response.ContentType = "image/gif";
context.Response.Cache.SetCacheability(HttpCacheability.Private);
context.Response.Cache.SetExpires(DateTime.UtcNow.Add(maxAge));
context.Response.Cache.SetMaxAge(maxAge);
context.Response.Cache.SetLastModified(lastModified); // last modified date time of file
context.Response.WriteFile(filenameofGif);
You can include what ever code checks you need to ensure the correct users is accessing the image.
I think the best option would be to deny direct access to the images from the web and create an aspx that will check users permissions and return the right image.
If the images are to be private to a particular user, then you should either store them outside the main application folder or put a web.config in each of those image folders (like someuid) and limit the access in the configuration file - either cutting out everyone (deny="*") or allowing access just for the particular user (allow="john").
In both cases you can use a handler to stream the image to the user, but at least you can check for permissions now. If the requesting user does not have permissions then throw a 401 at him or even display another image like imagenotfound.gif.
However, I am afraid the handler will generate a lot of traffic as there will be one call per image, I don't know how many images you're displaying per user.
Related
I have an APP using restful server. I want to store PDF's, images, etc. in folders on my server. How can I make the folders private on server, yet allow App to access only certain folders depending on their app access.
I have different users in app and security/tokens established, etc. But if they upload an image for their avatar (and now PDF's), they get stored in folders on the server, and I just display with image source=https://blahblah.com/org1/images/user232.jpg.
How can I make that not accessible to outside (like just going to browser), yet make accessible to app if they have correct login privilege's for that organization/user? And then further extend that logic to more sensative PDF's, and other docs uploaded through app. I didn't want to store in SQL since then harder to use simple image display tools and I already have upload and media managers using folders structures.
I can see how to secure if logging onto server through browser (credentials), but can't see how you connect App with that security level and maintain it for the session.
For future readers. Most of the work was done on the restful (ASP.NET) side. I first tried using authorization/Authentication in web.config and having Allow and deny. This allowed a redirect of a user to a login page; however, it didn't do it if they entered an image exactly correct on website.
Found HTTPHandlers (adding in webconfig ) where I could write code that would be executed once the user entered the specific Image address xyz/abc/image.png. I found this a bit feeling like a hack.
So lastly modified my
routes.MapRoute(
name: "staticFileRoute",
url: "publicstor/{*file}",
defaults: new { controller = "Home", action = "HandleStatic" }
And add a function like this to home controller.
[System.Web.Http.HttpGet]
public ActionResult HandleStatic(string file)
{
if (Session["OrgId"] == null) //todo need to add full security check.
{
return View("Login");
}
else //Either coming from app or coming from web interface
{
string mimeType = MimeInfo.GetMimeType(Path.GetExtension(file));
return File(file, mimeType);
}
}
The final bit is on the Xamarin side to now pass security when getting an image. Since just a simple Xamarin.Forms.Image doesn't have a way to pass login info or tokens/authentication I used
https://forums.xamarin.com/discussion/145575/image-from-url-needing-auth
And established an appwide webclient that logged in generally once forcing my restful to go through security validation, then just accessed the images/documents through out my app from that webclient. So far so good. Hopefully there are no holes.
This gives the gist to a future reader.
I have an asp.net web application with forms authentication and users (credentials) are checked against active directory, username is actually samAccountName attribute from AD.
Now I need to enable users to get access to some files which are located on file share, where each user has his own folder.
First proof of concept works like this:
appPool in IIS is configured to run under some domain user, and this user was given R/W access to file share and all user folders
when the user logs into web app only content of the folder on the path "\\myFileServer\username" is visible to him. And same when uploading files they get stored to "\\myFileServer\username".
While this works, doesn't seem to be secure at all. First issue is that user under which application pool runs has access to folders from all users. And even bigger concern is that only username determines to which folder you have access.
So my question is what is the correct/better way to doing this ? I was reading about impersonating the user, but this is not advised anymore if I understood correctly ? And I don't have Windows authentications since the web application must be accessible from internet.
I recommend not running the application under a user account, but creating an application specific account under which it runs with the proper R/W rights, and separate the person who gives these rights from the development team.
Within the application's authentication: after you receive a GET/POST request, you can verify the path to which the current user would read/write data, and cross-reference this with the path the user is authorized to read/write from. If these are incorrect, return a 401 NOT AUTHORIZED response, else, carry on the operation as you do now.
If your endpoints are protected properly, and the application runs under its own account, I don't see any harm in the setup itself. This still however gives the developers a way, through the application, to indirectly access other user's files. Based on how tight these checks must be, you could add additional controls, (like only allowing the application to connect from the production server, and only allowing server transport in a controlled way).
From the Description of your Problem i think Custom HttpHandlers are the right choice for you. You didn't mention what type of files will be present in your Folder , for brevity i will answer by assuming it will be having PDF files.
As you were mentioning that your application will be having different users so for this you need to use .NET built-in authentication manager and role provider. With a simple security framework setup, we'll place a PDF file in the web application, behind a web.config protected folder.then create a custom HTTP handler to restrict access on the static document to only those users who should be allowed to view it.
A sample HTTP Handler:
public class FileProtectionHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
switch (context.Request.HttpMethod)
{
case "GET":
{
// Is the user logged-in?
if (!context.User.Identity.IsAuthenticated)
{
FormsAuthentication.RedirectToLoginPage();
return;
}
string requestedFile =
context.Server.MapPath(context.Request.FilePath);
// Verify the user has access to the User role.
if (context.User.IsInRole("User"))
{
SendContentTypeAndFile(context, requestedFile);
}
else
{
// Deny access, redirect to error page or back to login
//page.
context.Response.Redirect("~/User/AccessDenied.aspx");
}
break;
}
}
}
Method SendContentTypeAndFile :
private HttpContext SendContentTypeAndFile(HttpContext context, String strFile)
{
context.Response.ContentType = GetContentType(strFile);
context.Response.TransmitFile(strFile);
context.Response.End();
return context;
}
private string GetContentType(string filename)
{
// used to set the encoding for the reponse stream
string res = null;
FileInfo fileinfo = new FileInfo(filename);
if (fileinfo.Exists)
{
switch (fileinfo.Extension.Remove(0, 1).ToLower())
{
case "pdf":
{
res = "application/pdf";
break;
}
}
return res;
}
return null;
}
Last step is that you need to configure this HTTP Handler in the webconfig ,
and You can see the more info here
Here is the complete Source Code
You're architecture (and assumptions) seem good for a low/mid security level, but if the nature of your data is very sensitive (medical, etc) my biggest concern about security would be controlling the user sessions.
If you're using forms authentication then you're storing the authenticated identity in a cookie or in a token (or if you're using sticky sessions then you're sending the session Id, but for the case it's the same). The problem arises if user B has phisical access to the machine where user A works. If user A leaves it's workplace (for a while or forever) and he doesn't explicitly close it's session in your web app, then his identity has been left around, at least until his cookie/token expires, and user B can use it since the identity system of ASP.NET hasn't performed a SignOut. The problem is even worse if you use tokens for authorization, because in all the infamous Microsoft implementations of the Identity System you're responsible of providing a way to invalidate such tokens (and make them dissapear from the client machine) when the user signs out, since they would stay valid until it's expiration. This can be addressed (but no completely thus not very satisfactorily for high security requirements) issuing short living refresh tokens, but that's another story, and I don't know if it's your case. If you're going with cookies then when user A signs out it's cookie is invalidated and removed from the request/response cicle, so this problem is mitigated. Anyway you should ensure that your users close their sessions in your web app or/and configure the cookies with short lives or short sliding expirations.
Other security concerns may be related with CSRF, wich you can prevent using the Antiforgery Token infrastructure of ASP.NET, but these kind of attacks are methods that are very far away from the tipical user (I don't know anything about the nature of your user and if your app is exposed to public on internet or it's only accesible on an intranet), but If you worry for such specialised attacks and have so sensitive data, maybe you should go with something more complex than forms authentication (two factor, biometrical, etc)
I have an ASP.NET MVC application with a page that allows users to upload files. The files will be several hundred megabytes.
I am using FineUploader on the client side, which will use FileAPI/XHR if the browser supports it, otherwise will fallback to Iframe/form with enctype="multipart whatever".
So on the server side I need to evaluate Request.Files.Count > 1. If true, this is an old school upload and I save the file like Request.Files[0].InputStream.CopyTo(myFileStream) otherwise I do Request.InputStreawm.CopyTo(myFileStream).
Here's some of the actual code I've written that does this stuff: https://github.com/ronnieoverby/file-uploader/blob/master/server/ASP.NET%20MVC%20C%23/FineUpload.cs
This all works fine, but in my testing I've noticed that neither an ASP.NET MVC controller action nor an HttpHandler will begin processing until the entire file is uploaded, which is bad if the file very large because that means it's occupying a lot of the web server's RAM.
I found this: Streaming large file uploads to ASP.NET MVC which sounds promising, but I really don't have an idea of where the code resides in his application.
So, the question is: how to stream uploaded files to disk while the upload is still taking place in ASP.NET?
Update
I just saw a key detail that didn't sink in before. From the HttpPostedFile documentation:
By default, all requests, including form fields and uploaded files,
larger than 256 KB are buffered to disk, rather than held in server
memory.
Ok, that addresses the concern that the web server's RAM utilization could spike during a large upload. But, there's still a problem: After the file is completely transferred to the web server, the server has to spend time moving it to it's final destination. If the file system operation is a copy (guaranteed if the destination is on another physical disk), then the response is delayed unnecessarily.
Honestly, I could probably live with this by increasing response timeout for the upload handler/action. But, it would be nice to stream the bytes directly to their destination.
You can handle uploads in a completely customized way without buffering using
HttpRequest.GetBufferlessInputStream method. Basically you are getting access to the raw incoming data and free to do whatever you want with it.
I've just created small sample which saves raw request content to a file:
Create handler:
public class UploadHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
using (var stream = context.Request.GetBufferlessInputStream())
using (var fileStream = File.Create("c:\\tempfile.txt"))
{
stream.CopyTo(fileStream);
}
}
public bool IsReusable { get { return true; } }
}
Register in Web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<handlers>
<add name="UploadHandler" verb="POST"
path="/upload"
type="UploadHandler"
resourceType="Unspecified"/>
</handlers>
</system.webServer>
Create a page with a form:
<form action="/upload" method="post" enctype='multipart/form-data'>
<input type="file" name="aa" id="aa"/>
<input type="submit"/>
</form>
If the uploading and streaming is using up valuable server resources then you might wanna take a look at hosting your media files on a cloud of some sort. It's possible with ASP.NET to use a Rackspace, Amazon Cloud API have your users upload the files directly to a CDN network and then serve the content that way, I know this isn't answering your question but many people will or already have and thought I'd get my 2 cents in. Many people still not opting to use the cloud amazes me! once you go CDN you never go back. Furthermore with most CDN's you will also be given a streaming URL for your upload container where it supports lots of different movie types, and its lighting fast, not only for your users to upload too but also your never have slow speeds on your website as a result.
I have a route
routes.MapPageRoute("clientOrder", "Contract/{contractId}/Orders",
"~/ContractOrders.aspx");
The idea is to authorize user to allow access to a certain set of contracts.
For instance user1 has access to pages Contract/001/Orders and Contract/002/Orders
user2 has access only to Contract/003/Orders, etc.
I'm using Forms Authentication and trying restrict access with
CheckUrlAccessForPrinсipal but it checks only physical access to the page not logical.
I tried to check access in Global.asax in Application_AuthorizeRequest but
Request.RequestContext.RouteData there is allways empty so I don't know the requested contractId. I can parse it manually from HttpRequest object. But it is a very dummy and unraliable solution.
Please advice
I believe that the only way is to add some code to check the contractId at the ContractOrders.aspx page level and if the Id doesn't pass the autorization, you manually redirect somewhere to indicate that the access is not granted.
The built-in mechanism always works at the physical level with route maps, so no matter how your route looks like, the engine always checks the access to the resource the route is mapped to, not the route itself.
I am passing a variable to a SWF file that provides access to several other SWF files. You can see the line I am using to assign the value to the variable beneath the THIS LINE comment below.
<script type="text/javascript">
/*THIS LINE*/
var flashvars = {a: "<%= User.Identity.IsAuthenticated %>"};
/*
Some other stuff here...
*/
swfobject.embedSWF("index.swf", "myAlternativeContent", "100%", "100%", "10.0", "expressInstall.swf", flashvars, params, attributes);
</script>
I am concerned that someone using an HTTP proxy could just switch the value of a from False to True if they wanted access. Am I right to be worried?
Is there a different way I should be controlling whether access to the child SWF is allowed?
I would say don't emit anything that they don't have access to. In this case, if they aren't authenticated, don't send any of that script to the browser.
Yes, you should be concerned.
Assuming you can't change the flow (ie: you have to send the script even if they aren't authenticated), then I'd change the "true/false" value to some type of key. The children should verify the key was passed before executing.
If possible, make the key user specific.
This doesn't completely solve the issue, but it would be harder for someone to provide a key that they don't have.
UPDATE:
Based on the very good comments, I have a different route.
Add a web request handler (.ashx file) to the site. Have the client call that to load the swf file. The handler should first test to see if they are indeed logged in. If they are, serve the file. If not just close the connection.
Basically change the embed line to look something like:
swfobject.embedSWF("grabFile.ashx?id=123", "myAlternativeContent", "100%", "100%", "10.0", "expressInstall.swf", flashvars, params, attributes);
Then have a .ashx request handler on your site test for being logged in prior to response.writing the actual contents of the swf file.
Yes, they could. Security doesn't work on the client side, you'd have to control access to the files from the server.
Yes, what is keeping an attacker from writing a static html page that does this:
var flashvars = {a: "AUTHENTICATED"};
yup, this is a vulnerability and you don't need to use a proxy to exploit it. You should refuse access at the server if your users aren't authenticated.
Yes. If the user has Firebug, they could simply look at what the appliation's code looks like when logged in, and then change it to mirror that when they're not logged in. You should handle authentication on a per request basis, and server-side. If you try:
If User logged in:
Put Flash in page
Else:
Put angry message
The user can still copy the Flash snippet when they're logged in, and paste it when they're not logged in, with Firebug, etc.
However, if you use:
Put Flash in page
Listen for requests from the Flash app to the server (for database content):
If the User who requests content is logged in:
Return content
Else:
Return angry message
This will work.
If the entire app is Flash based (ie, it doesn't need database access), the only way to secure it will be to protect folders at the server level (see Amember, et al). Even if you make the Flash application rely on a dongle with the server for authentication purposes, the user can simply download your Flash scripts, decompile them and distribute them for free use. They can still do this with the folder protection, but at least free users won't have that access. Your best bet is to make the application rely on content that comes from your server.