asp.net media protection - asp.net

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.

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

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.

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.

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.

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" />

Related

How to fix directory traversal security vulnerability in C# asp.net?

Find Snapshot here While running the [IBM Security AppScan] tool for one of my asp.net mvc web applications, I am getting path traversal vulnerabilities in my code. Please see snapshot attached and sample code to understand the issue better. Is there a way to fix such issues?
Sample Code:
var storagePath = ConfigurationManager.AppSettings.Get("DOCS_STORAGE_PATH") + #"\Attachments";
var strMonth = DateTime.Now.Month.ToString().Length == 1 ? "0" + DateTime.Now.Month : DateTime.Now.Month.ToString();
var strYear = DateTime.Now.Year.ToString();
var strFolder = strYear + #"\" + strMonth + #"\";
storagePath = storagePath + #"\" + strFolder;
if (!Directory.Exists(#"" + storagePath))
{
Directory.CreateDirectory(#"" + storagePath);
}
You need to enforce your input validation to solve your issue.
you should validate storagePath in the first line after the reading of the configuration.
But, anyway, If the path is store in a configuration on your server, I suspect a False Postive from AppScan Source
If you are using asp.net MVC framework then you don't need to worry about directory traversal at-least for the config files, dll , cshtml files etc. IIS will not server these types of sensitive information at any cost. But still its always better to encrypt your config files if it has some sensitive information like password , connection string etc ..
It's better to encrypt the machine key and connection string info in config files for ease of use.
The next point if the files we save our self, it may be in database or in server path.
In both cases we have to be careful about the attacks. Not only directory attack but also file upload attack.
If your file share is readable by the user that your app pool is
running under (Network Service by default) you can remove the virtual
directory completely and create an ASP.NET(any framework)application
that will stream the files to the browser. If you're using MVC it's
simply returning a file result. This has an added benefit in that you
will be able to restrict the users from downloading the files
Note : Since you are taking the path from web.config doesn't meant that, the files under that path are safe against directory attack. If a hacker somehow get the path , then he can try that directly in the browser\hacking tools\etc.. So the objective should be to secure the files not the path
Simple idea is to create another application/method which is capable of serving the file stream based on request. There you can validate the user and serve the file, file can even be saved in database or file system (there app pool user's doesn't have direct access)

Upload files to BMC Remedy Mid-Tier

I'm a Java developer, absolutely new in BMC Remedy system, but a I have just one fast task to solve.
Our Remedy use Java Applet to upload files from Remedy browser UI to FTP server. I should replace it with Javascript (upload files via http to the server side, which then upload it to FTP server).
In general web application, I can add a servlet, which would receive Multipart file, connect to FTP, upload it and respond with params. Piece of cake.
But is it a right way to solve this problem in Remedy? I've read documentation and it all about some sort of plugins for Remedy Mid-Tier and there is nothing about simple servlets.
What is the right way to solve my task? Any source samples would be really helpful.
Thank you.
if you are doing it via the API, you could just get the record ID, and field ID and do this:
//First, we retrieve the form
int[] fieldIds = {1};
String formName = "My:Form:Name";
//Request ID. Field ID = 1. Always 14 chars long.
String requestID = "00000000000001";
Entry entry = arsConnection.getEntry(formName, requestID, fieldIds);
//add the attachment
AttachmentValue attachment = new AttachmentValue("name_of_file.ext", "path/to/file.ext");
entry.put(550000011, new Value(attachment));
arsConnection.setEntry(formName, newEntry,null,0);
to do this, you need the request id. this code is using the java API.

QuickBooks 14 Web Connector (QBWC) Samples

I am very newbie QBWC, I have downloaded sample (WCWebService-ASP.NET(C#)) from QB website and reviewing the "QBWC_proguide", but I am getting an error during add applicaiton in QBWC, that is invalid when I add a qbxml(Add Customer) file.
Unxpected root doc:qbxml
QBWC1051: The new application was not added.
I have couple of questions.
is token ID return from QB? or shall we add and GUID?
I have seen some sample (WCWebservice) - buildRequest, this has CustomerQuery, InvoiceQuery and BillQuery, But I didnt get any reponse(I am doing some mistake here).
Do I need to generate a QWC file for every time to use in QBWC utility to "ADD APPLICATION".
Please advise me to achieve following process basically I am trying to populate Products from QB, and generate order from website again I should update the product count back to QB then Generate a Invoice from Website.
Thanks in advance
Unxpected root doc:qbxml
QBWC1051: The new application was not added.
To troubleshoot the above error, you need to post your .QWC file so that we can see what you're doing.
Here's an example .QWC file:
http://www.consolibyte.com/docs/index.php/QuickBooks_Web_Connector_Overview#Example_.QWC_File
is token ID return from QB? or shall we add and GUID?
You will choose a GUID OwnerID and FileID to put in the .QWC file.
Your web service may also use a GUID for a session token.
I have seen some sample (WCWebservice) - buildRequest, this has CustomerQuery, InvoiceQuery and BillQuery, But I didnt get any reponse(I am doing some mistake here).
The response would come from QuickBooks. Your web service would then parse the response to do something with it.
You can see all available requests and responses by looking in the QuickBooks OSR:
https://developer-static.intuit.com/qbSDK-current/Common/newOSR/index.html
Do I need to generate a QWC file for every time to use in QBWC utility to "ADD APPLICATION".
No. You will generate a .QWC file once for each QuickBooks company file you are connecting. The .QWC file essentially configures the Web Connector, and then after that it's already configured and will use the settings you loaded from the .QWC.

Encrypt and Decrypt documents through asp.net application

My asp.net application is in Web Server A and displays and let download MS-Word or PDF documents that are stored in Web Server B.
For security reasons, I was advised to encrypt and decrypt those documents when serving them up on the webserver A.
Could anyone give me some clue on how to do that?
I've never seen some utility before. My code just give value to a link control and let the user to click on it to display a MS-Word or PDF document, like:
Dim RemoteFolder As String
Dim RemoteFileName As String
RemoteFolder = "http://192.168.32.98/Application/Documents/"
RemoteFileName = "MyWordDocument.doc"
lnkOpenDocument.NavigateUrl = RemoteFolder + RemoteFileName
Using SSL might help, that protects all request/responses between the two servers. Otherwise .Net does have a encryption/decryption library under System.Security:
http://support.microsoft.com/kb/307010 also see this previous post What's the easiest way to encrypt a file in c#?
you can always grab the file from the user, encrypt using one of the above methods, and drop the encrypted file on webserver B. when reading it rather than link directly to the .doc file, link to another asp.net page, pass the ID of the file into that new page and have it pull the file from Webserver B decrypt it and display to the user.

Connecting an ASP.NET application to QuickBooks Online Edition

I am trying to create an ASP.NET page that connects to QuickBooks Online Edition, read a couple of values, and display the results. So far I have downloaded the QuickBooks SDK but I have been unable to find a simple step-by-step example on how to create an asp.net page to connect to QuickBooks Online. The QuickBooks SDK documentation and the SDK itself is very confusing and overwhelming. Anyone know of a simple step by step tutorial on where to get started... or maybe a hint on the very first thing to do.
Yishai's answer is partially correct, but not entirely.
You can have your ASP .NET application log in and issue requests without having to send the user over to the QuickBooks Online log in page if you make sure to set the security preferences correctly when you connect up your application to QuickBooks Online Edition.
During the application registration process/connection process, it will ask you if you want to turn on or off login security with a prompt as below. You must tell it you want to turn off login security if you want to be able to access QuickBooks Online Edition data without forcing the user to log in every time. The prompt is something like:
"Do you want to turn on login security?"
You must select:
"No. Anyone who can log into [Application Name] can use the connection".
Outside of that, Yishai is correct about the process. To re-iterate, in a nutshell:
Register for a QBOE account
Register your integrated application with Intuit's AppReg service
Visit a specific link to tie your AppReg application to your QBOE account (make sure you turn off login security when it asks you!)
Make HTTPS POST requests to Intuit's servers to sign on using the connection ticket Intuit will provide you with
Make HTTPS POST requests to send qbXML requests to Intuit's servers, which you can use to add, modify, delete, and query records within QuickBooks Online Edition.
There is some additional documentation and some example requests on my QuickBooks development and integration wiki, specifically the QuickBooks Online Edition integration page.
I have built a solution that does what you're asking in PHP which adds, modifies, and queries data within QuickBooks Online Edition without requiring the user to log in everytime, and it works like a champ. It pushes and pulls order data between a PHP shopping cart (VirtueMart) and QuickBooks Online Edition. The PHP code is available here:
QuickBooks PHP Framework
As a side note, unless you're very familiar with generating SSL certificates and sending them via HTTPS POSTs, you'll save yourself a whole lot of trouble by using the DESKTOP model of communication, and not the HOSTED model. Just make sure to keep your connection ticket securely encrypted.
Also, Yishai's suggestion to: "One is to programatically hit up their login page and submit the credentials as if you were a user. I'm sure its not "supported" but it would likely work." goes specifically against the security/developer guidelines Intuit and the SDK set forth. If they catch you doing that, they'll ban your application from connecting to QuickBooks.
Here are all the steps I took to get this working. Special thanks to Keith Palmer for his comments, answers, and his website which really helped me get this working.
Register your application at http://appreg.quickbooks.com. This will give you your App ID and Application Name. I used these settings:
Target Application: QBOE
Environment: Production
Application Type: Desktop
(using Desktop made things much easier as far as not needing certificates)
A verification key is sent to your email address which you need to enter on page 2 of this wizard.
Set up your QBOE Connection. Once you finish registering your application in Step 1, you will then have an Application ID. Use this ID in the url below to set up your QBOE Connection:
https://login.quickbooks.com/j/qbn/sdkapp/confirm?serviceid=2004&appid=APP_ID
NOTE: Make sure to replace APP_ID in the above url with the Application ID that was created when you registered your application.
The wizard will take you through the following steps:
Specifying a name for your connection.
Granting Access Rights - I gave All Accounting rights since this was easiest.
Specify Login Security - I turned Login Security Off. This is important since it makes submitting the xml to the QBOE much easier since you do not need to get a session ticket for each user.
You will then be given a Connection Key.
At this point you now have the 3 important pieces of information in order to gain access to your QuickBooks Online Edition (QBOE) account.
Application Name
Application ID
Connection Key
Post the XML to QBOE with the 3 pieces of access information and the actual request into your QBOE database. Here is sample c# code that will post to the QBOE gateway. This will return all customers in your QuickBooks database. Make sure to update the xml below with your Application Name, Application ID, and Connection Key.
string requestUrl = null;
requestUrl = "https://apps.quickbooks.com/j/AppGateway";
HttpWebRequest WebRequestObject = null;
StreamReader sr = null;
HttpWebResponse WebResponseObject = null;
StreamWriter swr = null;
try
{
WebRequestObject = (HttpWebRequest)WebRequest.Create(requestUrl);
WebRequestObject.Method = "POST";
WebRequestObject.ContentType = "application/x-qbxml";
WebRequestObject.AllowAutoRedirect = false;
string post = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<?qbxml version=""6.0""?>
<QBXML>
<SignonMsgsRq>
<SignonDesktopRq>
<ClientDateTime>%%CLIENT_DATE_TIME%%</ClientDateTime>
<ApplicationLogin>APPLICATION_LOGIN</ApplicationLogin>
<ConnectionTicket>CONNECTION_TICKET</ConnectionTicket>
<Language>English</Language>
<AppID>APP_ID</AppID>
<AppVer>1</AppVer>
</SignonDesktopRq>
</SignonMsgsRq>
<QBXMLMsgsRq onError=""continueOnError"">
<CustomerQueryRq requestID=""2"" />
</QBXMLMsgsRq>
</QBXML>";
post = post.Replace("%%CLIENT_DATE_TIME%%", DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss"));
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(post);
post = xmlDoc.InnerXml;
WebRequestObject.ContentLength = post.Length;
swr = new StreamWriter(WebRequestObject.GetRequestStream());
swr.Write(post);
swr.Close();
WebResponseObject = (HttpWebResponse)WebRequestObject.GetResponse();
sr = new StreamReader(WebResponseObject.GetResponseStream());
string Results = sr.ReadToEnd();
}
finally
{
try
{
sr.Close();
}
catch
{
}
try
{
WebResponseObject.Close();
WebRequestObject.Abort();
}
catch
{
}
}
Couple things to note:
As pointed out by Keith Palmer the qbxml version needs to be 6.0 (even though the IDN Unified On-Screen Reference shows 7.0)
I needed to include the onError="continueOnError" attribute.
Setting the WebRequestObject.ContentLength property is required.
Content Type needs to be "application/x-qbxml"
And finally I received many "The remote server returned an error: (400) Bad Request." exceptions which were not helpful at all but in the end I was able to trace them to something wrong with the xml. So if you get this exception look to your xml as the source of the problem.
The outline of what you have to do are outlined in Chapter 7 of the QBSDK documentation (at least in the 7.0 version of the SDK that I have). You have to open a test account and get permission to connect to their servers.
Once you have your account setup, the basic authentication procedure consists of redirecting your user to the QuickBooks Online site to log in, and once the user has done that, QuickBooks calls back your application with an HTTPS post with a ticket, which is basically a session handle that you can use for your requests, so that the system knows you are authenticated. When you get that response, you parse it and send your own login request to the system based on what you got back.
Then (if I understood the documentation correctly) you are basically doing Https POSTS of xml files with the QuickBooks requests, and you get XML responses that you have to parse to get the data you want.
I hope that gets you started.
The rest of the SDK is documentation (which you will need to know how to form your requests and parse your responses) and everything else is concerned with how to communicate with the desktop product. The only thing you are going to need from the rest of the documentation is how to do error handling, which is really only important if you are posting data to QuickBooks. If you are just reading, it doesn't matter (either your request works out or it doesn't, you don't need to worry about if you need to retry or if that would result in duplicate data).
EDIT: Given your specific use case I see two options. (You aren't crazy, just not the typical QuickBooks Online scenario).
One is to programatically hit up their login page and submit the credentials as if you were a user. I'm sure its not "supported" but it would likely work.
The other is to cache the results (which you should probably do anyway) and have an admin screen where someone does log into QuickBooks online and update the results every morning or evening or whatever makes sense.
In most small businesses, they are going to opt for the first option, but the second one is going to work more consistently, robustly and actually be supported by Intuit if you have an issue.
This looks pretty close to what you need: www.QuickbooksConnector.com
Wasn't able to download it yet.

Resources