Setup:
I am writing the Admin utility for an eLearning Package. Using this utility, the Tutors can write their courses, add/upload images, etc.
My problem is regarding security vulnerabilities when uploading files, specifically, image files.
The following code is my controller code for the POST that uploads a new image file:
[HttpPost]
public virtual ActionResult StepImage(int CourseId, int StepOrder, HttpPostedFileBase file)
{
service.CourseId = CourseId;
service.StepOrder = StepOrder;
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var fileExtension = Path.GetExtension(fileName);
if ((fileExtension == ".jpg") || (fileExtension == ".gif") || (fileExtension == ".png"))
{
service.StoreImageFileName(fileName);
var path = Server.MapPath("~/[path to where images are uploaded]/" + service.CourseId + "/");
if(!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
file.SaveAs(path + fileName);
}
else
{
// Refactor notice : Validation for invalid file extension
}
}
else
{
// Refactor notice : Validation for no file chosen
}
return RedirectToAction(MVC.Admin.StepEditor.Actions.Edit(CourseId, StepOrder));
}
You can see from the above code that I check for the file extension and only allow .jpg, .gif and .png.
Questions
I tried storing the files under the App_Data folder, but this resulted in a 403 forbidden response when the Views try to display the images.
So I put them in ~/Images/...
Is there a security risk with this? Can someone upload an .exe file with a .jpg extension and get it to execute baddie code?
It has to be said that the risk is low, as only Tutors will have permissions to use the page that uploads the file, but you just need one disgruntled tutor... Or they give their login details to a student... or whatever.
Any other security risks in that code?
PS:
The basics are taken from Scott Hanselman's and Phil Haack's blog posts on the subject of uploading files using ASP.NET MVC 2 +:
Phil Haack post
Scott Hanselman post
You get a 403 response when you place the images in the App_Data folder because IIS prevents any browser from directly accessing files in App_Data.
Placing them in ~/Images/ works but depending on the security on that folder this could mean that anyone can list the contents of ~/Images/ and/or view the images. By default, IIS forbids listing the contents of any folder but viewing the images by anyone is allowed. So, if someones knows the filename of the images, they can view them.
A solution is to use URL based authentication. Only the tutors (or whoever needs to) will be able to see the images then.
If you want complete control over who sees what on what pages, you could still place the images in the App_Data folder and then stream them to who- or whatever needs them like this.
About uploading an .exe as a jpg, this is certainly possible. It will even work for any type of file.
However, you save the file to the disk and don't do anything yourself with it. That makes the risk very small that the .exe-as-jpg is executed on your server. Unless someones finds an exploit in .net code that forces .net to execute the jpg, this is very unlikely.
You also show the jpg to the user in your views. That carries once again the security risk that possibly the user's browser has an exploit that forces it to execute the .exe-as-jpg on the user's computer. However, I think that is still very unlikely to happen (but not impossible. It has been done before e.g. see this).
To prevent this, you would need code on your server side that checks every image to see if it really is an image. However, these (possibly non-existent) security risks are very small and even exploiting this risks would require someone who knows quite a lot of computers and programming. Personally, I wouldn't worry about this.
Absolute security is impossible (Well, short of never turning your computer on. If it doesn't do anything, nothing can go wrong after all). It all depends how much security you want and how much time & money you can invest.
Related
I need to create upload of images to my webserver in my Angular 2 app. Can anybody provide me some guidance how to achive this?
These are the prerequisities:
ASMX web service communicating in JSON.
post method used for communication.
JPEG / PNG up to 1MB of size.
Concept I wanted to follow (but failed)
Load the content of JPEG to variable, encode it using the Base64 coding and post it to ASMX service that will accept two parameters (token for authentication and encoded data.
What exactly is my problem
Web service was the easy part, it is done and working, but I can't manage to get the file content for enconding. I used this:
component.html
...
<input type="file" (change)="fileChangeEvent($event)" />
...
component.ts
private fileChangeEvent(fileInput: any) {
let image = fileInput.target.files[0] as File;
...
}
As you have probably guessed, the problem is in the File class, because it provides me only basic info about the file (name, size, last modif, ...) but I can't get the content of the file. Or at least I don't know how to get it. I also checked other questions here on SO, but all of the answers had something special that did not met my requirements. And maybe I'm just blind, but I can't see where the content is get.
So, is there anybody, who is able to provide me some guidelines to follow?
Thank you very much in advance.
I have left this question open for experienced guys, who could be able to answer it. There is no answer though and I found out the answer yesterday. So, after some research and modification of search phrase, I found out the answer. There is a FileReader type which can be used for reading the content of the file. Here is the source of the answer:
Getting byte array through input type = file
Thanks to original answer now I know how to do it.
I am trying to download an image and displaying it in a view in rails.
The reason why I want to download it is because the url contains some api-keys which I am not very fond of giving away.
The solution I have tried thus far is the following:
#Model.rb file
def getUrlMethod
someUrlToAPNGfile = "whatever.png"
file = Tempfile.new(['imageprependname', '.png'], :encoding => "ascii-8bit")
file.write(open(data).read)
return "#{Rails.application.config.action_mailer.default_url_options[:host]}#{file.path}"
end
#This seems to be downloading the image just fine. However the url that is returned does not point to a legal place
Under development I get this URL for the picture: localhost:3000/var/folders/18/94qgts592sq_yq45fnthpzxh0000gn/T/imageprependname20130827-97433-10esqxh.png
That image link does not point anywhere useful.
My theories to what might be wrong is:
The tempfile is deleted before the user can request it
The url points to the wrong place
The url is not a legal route in the routes file
A am currently not aware of any way to fix either of these. Any help?
By the way: I do not need to store the picture after I have displayed it, as it will be changing constantly from the source.
I can think of two options:
First, embed the image directly in the HTML documents, see
http://www.techerator.com/2011/12/how-to-embed-images-directly-into-your-html/
http://webcodertools.com/imagetobase64converter
Second, in the HTML documents, write the image tag as usual:
<img src="/remote_images/show/whatever.png" alt="whatever" />
Then you create a RemoteImages controller to process the requests for images. In the action show, the images will be downloaded and returned with send_data.
You don't have to manage temporary files with both of these options.
You can save the file anywhere in the public folder of the rails application. The right path would be something like this #{Rails.root}/public/myimages/<image_name>.png and then you can refer to it with a URL like this http://localhost:3000/myimages/<image_name>.png. Hope this will help.
In my web page (rendered by Rails), I'd like to let the user right-click on a photo to bring up the browser's Save As dialog, to let the user save the photo to their hard drive.
However, the photos on my server have unusual filenames (long hex names) with no file extension. The filename prompt in the Save As dialog has this ugly filename. If the user hits save, they'll end up with a poorly-named file, with no file extension.
The web page is aware of the photo's real file name (the name that came off the camera, for example). Is there a way for me to programmatically override the Save As dialog's filename prompt with a filename of my choosing?
I'm aware of the Content-Dispostion header, and that via this header a filename can be specified. However, I think that in order to be able to make use of this header, I need to load/render the entire file to the browser. If the asset to be made available for download is a movie, that loading of the file could timeout the browser...like, if it's a 100meg video.
Thoughts?
-A
I think I understand the problem here because I encountered (and resolved) at least part of it myself not too long ago.
I have some large mp3's and I link to them on my website
A few problems
I needed to set my content-disposition header to attachment in order to prevent files from automatically streaming whenever a user clicked the download button
my files are on a remote server
my files are large (100MB)
large files can tie up rails controllers if not handled properly
Now, Michael Koziarsky advises in this article that the best way to keep your rails processes free when serving large files, is to create a download action in your controller, and the do something like this (note the use of x_sendfile=>true):
def download
send_file '/path/to/podcast.mp3', :type => 'application/octet-stream', :disposition => 'attachment', :filename=>'something.mp3', :x_sendfile=>true
end
:x_sendfile tells apache to let the file through without tying up a rails controller process. The rest of the code sets the filename and the content-disposition header.
Great, but I'm on heroku, like everyone else nowadays. So I can't use x_sendfile.
I found that I couldn't modify the nginx configuration file either as it's locked down by heroku so it was not possible to get x-accel-redirect (nginx equivalent of x-sendfile) working
So, I decided to add a perl script (see below) to the cgi-bin on our asset-host and this script sets the content-disposition to attachment and gives our file a name too.
Instead of doing a restful download like this:
link_to "download", download_podcast_path(#podcast.mp3)
we just link to the mp3 making sure that we go in through the cgi-bin so that the perl script gets called on every mp3 that leaves the server
# I'm using haml
%a{:href=>"http://afmpodcast.com/cgi-bin/download.cgi?ID=#{#podcast.mp3}"}
download
The result is that my rails controller is no longer called into action when someone downloads a file
I found the perl script here and chopped it up a bit to work for me:
#!/usr/local/bin/perl -wT
use CGI ':standard';
use CGI::Carp qw(fatalsToBrowser);
my $files_location;
my $ID;
my #fileholder;
$files_location = "../";
$ID = param('ID');
open(DLFILE, "<$files_location/$ID") || Error('open', 'file');
#fileholder = <DLFILE>;
close (DLFILE) || Error ('close', 'file');
print "Content-Type:application/x-download\n";
print "Content-Disposition:attachment;filename=$ID\n\n";
print #fileholder
My code, is on github but you'll likely have all sorts of problems using it on your machine as i make heavy use of ENV variables that I store in bashrc and I have no documentation or tests ^hides^
You could do some smart server side url rewrite, like for example rewriting foo.mpeg to youveryuglyfilenamewithoutextension.
Set the Content-Disposition to "attachment; filename="...that's fine. "attachment" explicitly means it's not to be rendered in the browser, file renaming works nonetheless (or possibly particularly for that case).
Based on your comments, you have a few problems.
You want to set the filename using your Rails app.
The file is on a remote host and your Rails app is acting as a middleman.
The file might be big, so you want the file to be sent out to the browser as you receive it instead of queuing the whole thing.
Streaming only with Rails is tricky for a few reasons.
You would need an HTTP client that lets you access the message body as you receive data instead of blocking until you have everything. Net::HTTP is not that client. I'm not sure what library would be better suited.
Once you have a more event-driven way to get your file in pieces, you can pass a proc to the render:
render :text => proc { |response, output| ... }
output can be used like an IO object. Some servers may buffer before sending anyway, though, so that's something to look out for.
It would be easier not handle the byte-shuffling in Rails.
If your webserver or the proxy in front of your webserver supports the X-REPROXY-URL HTTP header, your application can set that header and your webserver or proxy will stream the file.
Perlbal is the only proxy server I know of that supports that header out of the box.
An Apache2 module is also available.
I’m hoping to investigate/implement a CDN (initially just via a sub-domain, though moving over to CDN in time) and am having a mare finding resources that talk about handling of versions of files on that sub-domain.
Most places I’ve worked previously have implemented caching of resources (images, javascript, css, etc.) and when wanting to change an image, have gone through the painful process of just changing the filename of the image, and changing the reference to it in the source code (so that customers see the new, not the cached image).
What I want to achieve
what I'd like is:
resources.domain.com
with sub-folders such as:
scripts/
images/
css/
etc.
not a problem, and will help with the yslow/page speed scores (assuming cookieless domain etc.)
But versioning of assets is something I want to resolve.
E.g.
resources.domain.com/images/promo_banner1.jpg
I'd probably have to cache and expire perhaps every 10-15 days.
Assuming we have something key come in as a business request, and we need to change it, I want to be able to override that. From what I understand, I could append a querystring (?1.1) to it to force browsers to see it as a different resource.
I know I can do this in MVC (or indeed ASP.NET) by creating a 'CompanyResource' html helper that will lookup against perhaps a resource file or something similar to see if we have a new version, and if so, append the version number as a querystring element, but there has to be a better way?
So, what has the community come up with?
how best to deal with resources in a sub domain (assume I've read all of the yslow/google backup docs around this)
what have folks come up with to handle versioning of assets (to minimise overall code changes when something updates) - code based helper methods to deliver assets based upon some rules?
Hopefully I haven't waffled too much.
Thanks for any and all help :)
Cheers,
Terry
just noticed this one hadn't been answered - we resovled our problem with Html helpers under ASP.NET MVC using the following.
In web.config we stored the 'resources_url' (in this case resources.mycompany.co.uk), and called the following:
<%= Html.MyCompanyResourceScript("~/scripts/jquery-1.4.2.min.js") %>
Which translated to an Html helper:
public static string MycompanyResourceScript(this HtmlHelper helper, string url)
{
string _out = String.Format(#"<script type=""text/javascript"" src=""{0}""></script>", url.ToMyCompanyUrlAction(helper.ViewContext.HttpContext));
return _out;
}
public static string ToMyCompanyUrlAction(this string url, HttpContextBase context)
{
return String.Format("{0}://{1}{2}",
(context.Request.IsSecureConnection ? "https" : "http"),
WebConfigurationManager.AppSettings["resources_url"],
VirtualPathUtility.ToAbsolute(url, "/"));
}
We created helpers for MyCompanyResourceImage and MyCompanyResourceCss with suitable parameters for alt tags/media types etc. also.
This means we can (from the outset) host via our own resources domain, and if in future we choose to move that over to a CDN, then we can do so with minimal fuss.
Hope that helps someone.
Cheers,
Terry
There is a directory in the standard ASP.NET template "Content" where most people seem to be putting in images and css files etc.
For instance stackoverflow's logo:
(source: stackoverflow.com)
actually is refered to with a server path containing 'content' in the URL (just do View Source for any SO page and you'll see this). So they obviously are storing images in "content/images/...".
src="/Content/Img/stackoverflow-logo-250.png"
Edit: Sometime in the last 10 years they changed the path - but this is what it used to be.
I dont particularly like this. My HTML ends up with /content all over it, and its slightly harder to migrate existing pages that just have /image. Fortunately my stylesheet doesnt end up with content all over it, as long as I save it in content\site.css.
An alternative is to put an images directory in the root, but then you get images at the same level as Controllers and Views which is pretty horrible.
I'd wondered about trying to add a redirection rule like this :
routes.RedirectRoute(
"images rule",
"Images/{*}",
"Content/Images/{1}"); // this is NOT valid code - I made it up
But that code doesnt work because I just made it up. I could use a third party redirection/rewriting plug-in but I want to keep everything 'pure' within the MVC model.
What has anyone else found in this area? Or is everyone just happy with an extra ("/content".length) bytes in their source for every image they serve.
To be honest, I don't think its really something to worry about... "/Content" is going to make a pretty minimal contribution to your page size. If you still want to do it, here are some options:
Option 1, if you are running on your own server, is to check out the IIS URL Rewrite module: http://learn.iis.net/page.aspx/460/using-url-rewrite-module/
Option 2 is to use either RedirectResult, or ContentResult, to achieve the same effect in the MVC framework
First, map a "catchall" route under "Images/" to a controller action, like so
routes.MapRoute("ImageContent",
"Images/{*relativePath}",
new { controller = "Content", action = "Image" })
Make sure it is above your standard "{controller}/{action}/{id}" route. Then, in ContentController (or wherever you decide to put it):
public ActionResult Image() {
string imagePath = RouteData.Values["relativePath"]
// imagePath now contains the relative path to the image!
// i.e. http://mysite.com/Images/Foo/Bar/Baz.png => imagePath = "Foo/Bar/Baz.png"
// Either Redirect, or load the file from Content/Images/{imagePath} and transmit it
}
I did a quick test, and it seemed to work. Let me know in the comments if you have any problems!
It's usually better to put images under a different sub domain. The reason for this is browsers limit the number of connections per URL. So if you use http://static.mysiste.com now the browser can open more concurrent connections due to it being in a different URL.