views:

334

answers:

5

Does anyone know a good practice of securing media for asp.net?

I need to host a variety of media that require permission to a view a specific image/video. i.e. a specific user may or may not have permission to view a media file - and this fact may be changed on the fly.

I don't care if they can download a media file that they have access to, I just don't want them to even be aware of items they should not have access to.

I've already considered url obfuscation - this seems quite lame to me.

I have form authenticated users (and I'm not willing to change this).

I would like to keep the media file folder structure unrelated to permissions.

+11  A: 

Build an HttpHandler that all media must be accessed through. Then, prior to retrieving the file and sending it down to the user, you can perform any validations that you'd like. Keep all of your media outside of the main wwwroot path, or deny access to that folder using permissions.

More info on this topic here:

http://www.15seconds.com/Issue/020417.htm

Lusid
lol - that's like leveling the world trade center with several nukes when you could more readily and precisely do it with some thermite...seriously - there are also public images - the technique should work only with specific files/folders.
mson
Ummmm... k. So, you're saying something like this in your web.config wouldn't do exactly what you are asking?<add verb="*" path="protected/*" type="MyProject.MyHandler, MyProject" />You could then authorize any hits to /protected/whatever on a case by case basis using your current auth scheme.
Lusid
The best part is that you don't have to rely on users mangling the URL to get to other files. Force the handler to require users to be authenticated, then perform a check in the database to determine if they have rights to that file. Then sharing urls won't even work without sharing logins as well.
Lusid
This really isn't heavy solution--it's the right way to do it because it's clear, easy, and light. +1
Michael Haren
+1 for this solution. I'm not sure this solution warrants the snippy reply by the poster (mson), it's very valid and easy to do.
Jon Tackabury
sorry - i didn't mean to be snippy. it seemed that writing an httphandler was quite a bit of effort. i'm lazy and wanted to see if there were an easier way... this solution is looking like the answer though.
mson
A: 

I'd suggest a table holding the files to which each user has access:

UserID int
FileID varchar

then a table for your files:

FileID    UniqueIdentifier
FileType  char(4)  <- so you know which extension to use.
etc...

On the hard drive, name the file the FileID (UniqueIdentifier) and the FileType (the extension, eg. .jpg). The fileID in the permissions table will hold the UniqueIdentifier generated in the other table.

You can pass this via the URL knowing with relative safety that the user won't be able to guess the name of any other file.

Update: this is, by the way, much simpler than writing an HttpHandler or dealing with file permissions. However, while the chances of someone guessing another file name are infinitesimal it is not airtight security as one user may give another one access to the file.

Mark Brittingham
this is trivial to circumvent
mson
I disagree in the case of guessing a file name. If users will conspire to share file names, then you are correct. Thus, whether it applies depends on the nature of the site. A music site for kids? Forget it. A corporate site holding competing artwork from various design companies? Works fine.
Mark Brittingham
someone at a company suggested this technique for sensitive data. they started moving in this direction. i demonstrated the unsecured extraction of the media files within a matter of an hour. if it's not secure, it's not secure.
mson
Interesting - how did you get the identity of the file? Were they using UniqueIdentifiers? I'd like to know more as I have some pretty low-security files that I hide this way (I don't really care if someone sees them, I just don't want them readily guessable).
Mark Brittingham
+2  A: 

I use an xml file like this to set which users/groups have access to a file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root[
    <!ELEMENT file ANY>
    <!ATTLIST file name ID #REQUIRED>
]>
<root>
    <file name="file.doc" users="155,321" groups="grp5" />
    <file name="file2.doc" users="321" groups="" />
</root>

files are stored above http root so they cannot be accessed by URL.

When a user tries to access GetFile.aspx?file=file.doc I load the XML, get the line with

XmlNode xnFile= XML.GetElementById(wantedFile);

, then I call a function

 HasAccess(Context.User, xnFile);

Which checks if the user is logged in and compares the permissions, and if it is ok for this user to have the file, I read the files from disk and write them out with

FileInfo thisFile = new FileInfo(secretLocation + wantedFile);
Response.Clear();
Response.Buffer = false;
Response.BufferOutput = false;
Response.ClearContent();
Response.ClearHeaders();
Response.AddHeader("Content-Length", thisFile.Length.ToString());
Response.AddHeader("Content-disposition", "filename=" + thisFile.Name);
Response.ContentType = "application/none";
Response.WriteFile(secretLocation + wantedFile);
Response.Close();
Response.End();
Response.ClearContent();
Response.ClearHeaders();

Actually now I have more than a thousand files, and I think of writing the file data to the database as the XML got corrupted twice in 5 years, probably due to crashes or simultaneous use.

Spikolynn
+1 exactly what i would do
rizzle
the image needs to be part of the page, not a separate file.Also - if the secretlocation is discovered, doesn't the user have all the files?
mson
You compose your HTML so it looks as it is part of the page. Secret location is not visible for user, just for the application:c:\mysite\secretLocation vs. c:\mysite\webroot
Spikolynn
I'm puzzled - how is this different than obfuscation? Would an authenticated user be able to share an image (which they are authorized for) with another authenticated but unauthorized user?
mson
file is not visible on the internet. A program reads the file from a special location and serves it _only_ to authorized users.
Spikolynn
A: 

brownpaperpackage.aspx?id={guid}

In the Load event of media.aspx, you verify the user is authenticated, then verify the user has the right to view the media, and if they do, then load the media as a stream and feed it to the page's Response as Spikolynn demonstrated.

Why do it this way? Its simple to code and you get all the benefits of ASP.NET and IIS' authentication services, from which you can find the user requesting the media. Its trivial to map that user to an access list for your media objects. And the Page has the request object right there. You're also hiding the name of the media, so you can't tell what's going on from the URL.

How do you keep people from accessing your media directly? Your media files cannot be stored in the IIS virtual directory. If they are, there's a possibility that they can be downloaded directly. You can store them in a database as a byte array (blob) or store them on disk outside of the web virtual directory. Users must go through ASP.NET to access the files

How do you keep track of what users have access to what media? You keep track of your users thorugh asp.net membership. That means each user has an ID in the aspnet_users table. Create a table for your media with an id and a filename (or a blob containing the actual media). Then you just need to create a third table that connects the two. This table would contain a user id and a media id, signifying this user can view this media. With the user id (from asp.net Membership) and the media id (from the URL) you just need to

select count(*) from UserMedia where UserId = @UserGuid and MediaId = @MediaIdFromUrl

and if the count > 0 the user can view the media.

An example of how you'd use the URL:

<asp:image 
  runat="server" 
  ImageUrl="brownpaperpackage.aspx?id=53a2ea4(snip)76ca8b" />
Will
+1  A: 

From your comment in the Spikolynn answer

I'm puzzled - how is this different than obfuscation? Would an authenticated user be able to share an image (which they are authorized for) with another authenticated but unauthorized user?

I guess that you try to prevent unauthorized sharing of media.

This is something a lot of companies (Microsoft, Apple, IBM, etc) have put considerable amount of money to solve. The solution was DRM, and now they are removing it, because it failed.

So, my answer is that you can not prevent sharing if the user is willing to put some effort to avoid it.

You can just keep the honest people honest by applying some techniques as Spikolynn or Lusid explain in their answers.

Eduardo Molteni