The best way to store and serve up user profile images - asp.net

My website has public-facing profiles. Users may have 145x145 profile images that are displayed to people. At the moment, I store profile images under a directory on my server: /images/users/ I then use a .ashx file to serve the right image up for each page:
<!-- from the html -->
<img id="imgLogo" runat="server" alt="Company Logo" src="" />
// from the page code behind Page_Load event
imgLogo.Src = "userImage.ashx?id=" + UserId;
// from the .ashx file
String imagePath = context.Server.MapPath("~/images/users/")+context.Request.QueryString["id"]+".jpg";
This code was in place when I joined the project (the guy who wrote it has since left.) It seems to work nicely enough, but I'm new to web development, and unfamiliar with other ways of implementing systems like this.
I now need to add a different kind of image to the site - something like a tall banner ad for a user's profile. As such, these images will be much larger than the 145x145 profile avatars.
How should I implement these for my site? Should I use a similar system to the one currently in place, with images stored in /images/users/largeImage and the src being set to a largeUserImage.ashx file? Or should I store the images in my sql database?
It's likely that I'll want to add more user-defined images in the future - maybe stuff like user photo collections and similar. Is there anything I should consider to make a system that is easily scaleable?

As 3rd party framework like Gravatar doesn't work for your case, you have to do it on your own.
Don't save photos in SQL database, save its paths (image paths of different size) instead. In your userImage.ashx, you can pass 1 more parameter to tell the script which size of image should be fetched, such as:
imgLogo.Src = "userImage.ashx?id=" + UserId + "&size=" + ImageSize;
where ImageSize is your defined variable.

Related

ASP.Net Core static file security (images specifically)

I have an issue that seems like a very common requirement, but I'm unable to find any help. Let's say I have an authenticated user uploading private photos to non browsable folder on my server. Each user has their own folder in a large file store, like...
/FileStore/{UserId}/Photos/my_cute_cat.jpg
The file is uploaded and I save a thumbnail of the photo like...
/FileStore/{UserId}/Photos/Thumbs/my_cute_cat_thumb.jpg
That user wants to download their photo. No problem...
User sends a request to download
I authorize the user and make sure they own that particular photo
I serve the file
I need display the thumbnail in a plain old img tag on the user's dashboard. The /Thumbs/ folder is not set up to serve static images. I don't want the /thumbs/ folder to be able to serve static images because they should only be visible to authorized users. What am I supposed to do?
If its just a small thumb nail, consider using embedded base64 image with more details here:
How to display Base64 images in HTML?
You can pass base64 down to the View by encoding the file into a base 64 format as a string explained here:
http://www.devcurry.com/2009/01/convert-string-to-base64-and-base64-to.html
Using this approach or even using a FileActionResult to serve the file through a controller has the big disadvantage of not being able to use a CDN to deliver the cached content. What you can do to help with this is still serve the images statically but give them obscenely long random names which is unguessable. When someone requests the image from you, then you simply provide them with the unguessable url.
First and foremost, if a static file should not be available to the whole world, then your web server should not serve the directory it is in at all. Nothing else will do on that front. If the directory is served, then the image can leak.
That then presents the problem of how to allow the user to access it. The simple answer there is that you need an authorized action that returns the image(s). Basically, that's going to look something like:
[Authorize]
public async IActionResult Image(string image)
{
var file = $"/FileStore/{User.Identity.GetUserId()}/Photos/{image}";
if (!File.Exists(file))
return NotFound();
return File(file);
}
Now, obviously, I don't know about your "FileStore", so the exact code here may need to change. The general idea is that you simply see if this file exists under the user's directory. If it does, they can have it. If not, they don't own it. You also should probably add in a bit more security, such as restricting the image param to only image types, so someone can't try to pull just any arbitrary file. They'd still have to somehow manage to get some aberrant file there, in the first place, but just in case it's better to not take chances.
The return methodology may need to change as well. Right now, I'm assuming a file on the filesystem of the server, so I just return a FileResult with the path. If you're pulling it from some sort of storage account in Azure, AWS, etc. then you'd use HttpClient to make the request, and then you should stream the response from that directly to a FileStreamResult.
I've not tested on linux, but if you make a folder where you have you pictures, you can actually just create a controller method that returns a file. Now, even if you are using a view served from another method, you can still call this file method from this razor view and display the image.
in controller called App I serve the image from a folder called Documents:
public IActionResult File(string id)
{
if (String.IsNullOrEmpty(id))
{
return PhysicalFile(Path.Combine(_env.ContentRootPath, "Documents", "Wrong.png"), "image/jpg");
}
return PhysicalFile(Path.Combine(_env.ContentRootPath, "Documents", id), "image/jpg");
}
in razor (using a bit bootstrap):
<img src="~/App/File/#profilePicture" class="img-fluid" />
use the location of your method that serves the file.

aspx, response.write, image and link referencing

I've spent a while trying to find out whether what I want is possible.
I have 3 websites on different domains. Two are in English, one in French. We have one page in english, one in french which are identical apart from the text. These pages and relevant images (we'll call common content) are stored on a separate domain (reasons beyond my control) and use response-writefile to insert the content into the two english pages.
Got all that working fine. However, the images in these common pages have a path relative to domain on which they are stored, which means when the pages are written into the main pages, the images dont show. I understand why and can get around it by putting in the full path of the image.
I would prefer not to go through every single page changing the image path, is there any way of the server knowing or being told that the image is relative to the common content and not the rendered page?
I wouldn't have thought so, but it would save my day if there was!
Further explanation:
Relative path of image:
abc.png
Path of common content file:
http://domain1.com/CommonContent/123.html
Code in final pages (domain2.com/english.html):
<% Response.WriteFile("/CommonContent/123.html"); %>
Rendered path of image (what I don't want):
http://domain2.com/abd.png
Ideal path of image in rendered page: ie, what I want to happen:
http://domain1.com/CommonContent/abd.png
what you can do is use WebClient class to get the page content
String URI = "http://domain1.com/CommonContent/123.html";
WebClient webClient = new WebClient();
Stream stream = webClient.OpenRead(URI);
String request = reader.ReadToEnd();
then you perform a string replace, here I'm not sure what can match, maybe something like this can match:
request.Replace("<src=", "<src=http://domain1.com/CommonContent/")
then you render this string to the browser.
You can make Application Settings having the path for each domain on each application, and then maybe add a function that will be in charge of writing the full path of the pictures. Also you can make an HTTP Module to address this issue as well as a Generic Handler that will receive all requests for images and load them from different domains/applications.
Good luck!

how to store and retrieve images for user articles

I have a web site in which users post news to site.
so i have two field in my database NewsTitle and NewsBody.
The Newsbody can have images.
What datatype should I use for NewsBody?
I don't want images loaded from image hosting site, I want images save in my database or in my web site's host.
What is the best way to implement this, should I use ajaxcontroltoolkit?
any ideas?
You can set your the datatype as varchar as the filename of your image, since all you need to know when you try to display the image is the filename. (but of course, you must already know the location of this file)
I think u can use ListView and the template field will contain asp:image.you can store the image name in the database and bind the imageUrl through the image directory in your system plus the image name.

How can i hide/secure image path?

How can I hide/secure image path in asp.net? I don't want the user see image path directly.
I have googled with my problem and found the following URL:
http://www.codeproject.com/KB/web-security/ImageObfuscation.aspx
On this page it suggests changing the image path like this:
<img ID='ImageControl'
src='ShowImage.axd?Path=<% EncryptString("C:\Images\img.ext", Page) %>'
But if user copy this image src and paste it into their browser with the domain name then it will show image.
There's absolutely no way to achieve this, so no need to waste your time and efforts. As long as the browser can show an image, the user can also directly fetch it.
It really depends on what you are trying to achieve.
If you're trying to stop people linking to your images from another site, then the best option would be to extend the handler you mentioned in your question to only return an image if the Request.Referrer is your own site.
This means that if they did then try and link to the image via your handler, they'd only see a broken image/no image, they wouldn't be able to request the image directly in their browsers, etc.
You should also probably include some sort of time stamp in the encrypted path, and reject requests that come from too long ago - this will again limit the validity of the links:
<img ID='ImageControl'
src='ShowImage.axd?Path=<% EncryptString("C:\Images\img.ext|" + DateTime.Now.ToString(), Page) %>'
Then in your handler:
Dim pathAndTimeEnc As String = ctx.Request.Params("Path")
Dim pathAndTime As String
Dim path As String
Dim timeStamp As DateTime
pathAndTime = Common.DecryptString(pathAndTimeEnc, ctx)
Dim parts = pathAndTime.Split("|"C)
path = parts(0)
timeStamp = DateTime.Parse(parts(1))
Dim fiveMin As TimeSpan = New TimeStamp(0, 5, 0)
If DateTime.Now.Subtract(timeStamp) < fiveMin Then
' Return image.
End If
If you're trying to stop people downloading your images then you're not really going to stop more than the most basic internet user - after all to display the image on your site, you'll need to send a copy of it to the client browser.
However, a couple of possible options to make it harder:
Ensure that the images expire immediately, this means the browser shouldn't keep them locally for that long - however it does mean that none of the images will be cached, and you'll end up with higher bandwidth useage for repeat viewers; if you are using the handler you can do this in code:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.Now);
Use CSS to place a transparent 1x1px image over the top of the images on your site - this way if a user right-clicks on the image to save it, they will get the path to the transparent image rather than the one they are expecting (Flickr does/used to do this)
At the end of the day, if you put some content online, then it's very hard to stop the most dedicated "thief" from taking it and using it.
You could do some hack that symlinks the real image path to some (one time) temporary location which is sent to the client. Once the client has received the image, the symlink can be removed; although... what a hack!
How comes the image path is a secret?
You can do this, but it's going to be a lot of work, people are going to be able to get around it, and so there needs to be a really good reason for doing it and you need to recognize that it will never be a 100% solution. It will (at best) be a solution to prevent the non-technical from grabbing the images. (And even they can use Alt+PrintScreen.) And it will take time away from whatever you're doing that actually generates value.
But:
Basically, you can use one-time paths tied to an IP address. When the page is requested, log the IP address and generate custom image paths for that page basically in the form of "http://example.com/images/alsdkjflaskdf" (or "http://example.com/images/getimage?alsdkjflaskdf" if you can't do custom URL handlers) where the "alsdkjflaskdf" part is an encrypted/obfuscated, one-time-only path to the image that's only valid from that IP address, and only valid for a given time period. Once the time is up or it's been used, purge that generated path from your database (or whatever you're using to keep track).
The paths would be
Limited to the IP address
Time-limited
One-time-only
As you can see, it's a pain, and I could easily work around it with wget. Your time is almost certainly better spent elsewhere.
Store the path in a database or xml. Store some kind of unique id each path and rewrite the handler to query the path from the datasource. You can use like this:
< img ID='ImageControl' src='ShowImage.axd?ID=1 %>'
And the path reamain secret :)
Ok. Reread the original post. Try to store the session whitch page has been seen. And if there is no one or not the page that contain the picture You show a black screen. Yes the visitor can see if use the link after s/he saw the page, but until the session is alive. And the link won't work if s/he link to somewhere.

ASP.NET MVC: How to use static HTML pages in MVC applications?

In the app I am working on, I want to allow the user to upload static HTML pages to replace the default "user profile" MVC View page. Is this possible? That is, the user uploaded html pages will totally run out of MVC, and it can include its own CSS links, etc.
Ideas? Suggestions?
Obviously the .net MVC framework handles static content already for images / css / js etc. It would just be a matter of extending that (routing?) to pass .html files through straight to IIS. That coupled with a dash of rewriting to make prettier urls should do the trick.
However, I would be very, very wary of allowing User Generated Content in the form of raw HTML uploads as you're leaving a very very wide door open. At best, you're going to wind up with people's pages full of spam/porn/adverts. At the worst, you'll be providing a gateway for people to upload cross-site scripting hacks and potentially uploading malicious content to damage your site. The could easily take an existing form on your site, hardcode a load of junk into it, and exectute it from their homepage and break a whole heap of things.
At the very least you should be parsing the uploaded content to reduce it down to just a block of content, and then wrapping that in your own etc. I would personally be much more inclined to just provide users with a nice WYSIWYG editor to edit a single block of content - any editor worth it's salt should provide you with sanitisation as to what elements it includes / excludes. Then store this content fragment in your database / on disc and have the request for a homepage go through a standard MVC controller route and load up that content.
Edit - for you request for examples
You should be able to add an Ignore rule to your routing - there will probably already be examples of these already - crack open your Global.asax file - you will want to put in a call to the routes.IgnoreRoute method :
routes.IgnoreRoute("UserPages/{*path}");
Should let IIS handle all requests for yourwebsite.com/UserPages/aUser/homepage.html - you can also play about a bit more with the wild card fragments / constraints for prettier solutions
I suggest you to make your custom ViewEngine that will allow to use static html markup with custom tags in it, that will be replaced by user info.
So, your view engine may accept something like that:
<html>
<body>
<b><user:FirstName /></b>
<b><user:LastName /></b>
</body>
</html>
and produce
<html>
<body>
<b>First Name</b>
<b>Last Name</b>
</body>
</html>
This custom markup you can store in database, for example.
You can take a look at custom ViewEngine implementations at MVC Contrib project.

Resources