Prevent access to file(s) to secure path based downloads - asp.net

It is fairly common to allow users to download a file via having some path modifier in the URL
//MVC Action to download the correct file From our Content directory
public ActionResult GetFile(string name) {
string path = this.Server.MapPath("~/Content/" + name);
byte[] file = System.IO.File.ReadAllBytes(path);
return this.File(file, "html/text");
}
quoted from http://hugoware.net/blog/dude-for-real-encrypt-your-web-config
An application I'm working with has liberal path downloads ( directory based ) sprinkled throughout the application, hence it is super vulnerable to requests like "http://localhost:1100/Home/GetFile?name=../web.config" or ( ..%2fweb.config )
Is there an easy way to restrict access to the config file - do I need to provide a custom Server.MapPath with whitelisted directories - or is there a better way.
How do you secure your file downloads - are path based downloads inherently insecure?

A simple option, assuming that all files in the ~/Content directory are safe to download would be to verify that the path is actually under (or in) the ~/Content directory and not up from it, as ~/Content/../web.config would be. I might do something like this:
// MVC Action to download the correct file From our Content directory
public ActionResult GetFile(string name) {
// Safe path
var safePath = this.Server.MapPath("~/Content");
// Requested path
string path = this.Server.MapPath("~/Content/" + name);
// Make sure requested path is safe
if (!path.StartsWith(safePath))
// NOT SAFE! Do something here, like show an error message
// Read file and return it
byte[] file = System.IO.File.ReadAllBytes(path);
return this.File(file, "html/text");
}

Related

Xamarin.Forms - How to access /data/user/0/com.companyname.notes/files/.local/share/

I was following a small tutorial of Microsoft.
Which basically saves your text input onto the internal memory of your device.
String _filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Notes.txt");
Results in: /data/user/0/com.companyname.notes/files/.local/share/Notes.txt for me.
Now, while everything works, I would like to see this Notes.txt file in the folder.
I have searched far and wide, but can't seem to find a way to locate this file on my device.
I can go to Android/data/com.companyname.notes/files but then I only see a ._override_ folder with the app project files in it, but without the Notes.txt
Any ideas?
Thanks
From your path:/data/user/0/com.companyname.notes/files/.local/share/Notes.txt, we can know that you want to access internal storage, but Internal storage refers to the non-volatile memory that Android allocates to the operating system, APKs, and for individual apps. This space is not accessible except by the operating system or apps. So you can not find this text file from internal storage.
If you want to see file, you can save this file in external storage
/storage/emulated/0/Android/data/com.companyname.app/files
More detailed info about internal storage, see:
https://learn.microsoft.com/en-us/xamarin/android/platform/files/
Update
If you want to save text file, you should declare one of the two permissions for external storage in the AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Then the primary location for private external files is found by calling the method Android.Content.Context.GetExternalFilesDir(string type). This method will return a Java.IO.File object that represents the private external storage directory for the app. Passing null to this method will return the path to the user's storage directory for the application. As an example, for an application with the package name com.companyname.app, the "root" directory of the private external files would be:
/storage/emulated/0/Android/data/com.companyname.app/files/
In the Forms, you need to create new interface:
public interface IFileSystem
{
string GetExternalStorage();
}
Implement this interface in Android:
[assembly: Dependency(typeof(FileSystemImplementation))]
namespace FileApp.Droid
{
public class FileSystemImplementation : IFileSystem
{
public string GetExternalStorage()
{
Context context = Android.App.Application.Context;
var filePath = context.GetExternalFilesDir("");
return filePath.Path;
}
}
}
Now you can create text file and save text in this file:
private async void Btn1_Clicked(object sender, EventArgs e)
{
var folderPath = DependencyService.Get<IFileSystem>().GetExternalStorage();
var file = Path.Combine(folderPath, "count.txt");
using (var writer = File.CreateText(file))
{
await writer.WriteLineAsync("123456789000000000000000000000000000000000000");
}
}
I have made a sample:
https://github.com/CherryBu/FileApp
The exact path to the private external storage directory can vary from device to device and between versions of Android.
Open your File Manager App
Go to Android/data
Find the .com folder, in your case, com.companyname.notes
Follow the path until you find the file

Where to store uploaded images in Linux server using Spring MVC

I have written a code to upload the images(profile picture of an student) in the server running in linux environment.The code is shown below
#RequestMapping(value = "/updatePhoto",method = RequestMethod.POST)
public String handleFormUpload(#RequestParam("id") String id,
#RequestParam("file") MultipartFile file,
HttpServletRequest request,
Model model) throws IOException {
if(!file.isEmpty())
{
try
{
String relativePath="/resources";
String absolutePath=request.getServletContext().getRealPath(relativePath);
System.out.print(absolutePath);
byte[] bytes=file.getBytes();
File dir=new File(absolutePath);
if(!dir.exists())
{
dir.mkdir();
}
File uploadFile=new File(dir.getAbsolutePath()+File.separator+id+".jpg");
BufferedOutputStream outputStream=new BufferedOutputStream(new FileOutputStream(uploadFile));
outputStream.write(bytes);
outputStream.close();
model.addAttribute("uploadMessage","image uploaded for id"+id);
}
catch (Exception e)
{
System.out.print(e);
}
}
return "successFileUpload";
}
i have stored in "/resources" folder.but the problem is, whenever i generate the war file of whole application and deploy in server, it flushes the "/resources" folder and deletes the old uploaded images.Is there any way or the path ,i could upload the images.
The way I do is:
Create a directory in the server. For example: /myImages
Then grant full permissions for tomcat user
You are good to go now. I have read somewhere that you shouldn't save your stuff on /resources folder because it makes your app independent from container you are using: with tomcat you could use catalina.home but what if you shift to another container
I store the images inside my Tomcat home location as it will be outside of my project folder(war) and inside the tomcat.
String rootPath = System.getProperty("catalina.home");
File dir = new File(rootPath + File.separator + "images");
The above lines of code will create a folder in tomcat base directory with name 'images'.
This is the one of the best ways to store images.
Here's simple way
System.out.println(System.getProperty("user.dir"));

Can't get the names of the files that exist in a specific directory using File or InputStream [duplicate]

I have a resources folder/package in the root of my project, I "don't" want to load a certain File. If I wanted to load a certain File, I would use class.getResourceAsStream and I would be fine!! What I actually want to do is to load a "Folder" within the resources folder, loop on the Files inside that Folder and get a Stream to each file and read in the content... Assume that the File names are not determined before runtime... What should I do? Is there a way to get a list of the files inside a Folder in your jar File?
Notice that the Jar file with the resources is the same jar file from which the code is being run...
Finally, I found the solution:
final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
if(jarFile.isFile()) { // Run with JAR file
final JarFile jar = new JarFile(jarFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
while(entries.hasMoreElements()) {
final String name = entries.nextElement().getName();
if (name.startsWith(path + "/")) { //filter according to the path
System.out.println(name);
}
}
jar.close();
} else { // Run with IDE
final URL url = Launcher.class.getResource("/" + path);
if (url != null) {
try {
final File apps = new File(url.toURI());
for (File app : apps.listFiles()) {
System.out.println(app);
}
} catch (URISyntaxException ex) {
// never happens
}
}
}
The second block just work when you run the application on IDE (not with jar file), You can remove it if you don't like that.
Try the following.
Make the resource path "<PathRelativeToThisClassFile>/<ResourceDirectory>" E.g. if your class path is com.abc.package.MyClass and your resoure files are within src/com/abc/package/resources/:
URL url = MyClass.class.getResource("resources/");
if (url == null) {
// error - missing folder
} else {
File dir = new File(url.toURI());
for (File nextFile : dir.listFiles()) {
// Do something with nextFile
}
}
You can also use
URL url = MyClass.class.getResource("/com/abc/package/resources/");
The following code returns the wanted "folder" as Path regardless of if it is inside a jar or not.
private Path getFolderPath() throws URISyntaxException, IOException {
URI uri = getClass().getClassLoader().getResource("folder").toURI();
if ("jar".equals(uri.getScheme())) {
FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
return fileSystem.getPath("path/to/folder/inside/jar");
} else {
return Paths.get(uri);
}
}
Requires java 7+.
I know this is many years ago . But just for other people come across this topic.
What you could do is to use getResourceAsStream() method with the directory path, and the input Stream will have all the files name from that dir. After that you can concat the dir path with each file name and call getResourceAsStream for each file in a loop.
I had the same problem at hands while i was attempting to load some hadoop configurations from resources packed in the jar... on both the IDE and on jar (release version).
I found java.nio.file.DirectoryStream to work the best to iterate over directory contents over both local filesystem and jar.
String fooFolder = "/foo/folder";
....
ClassLoader classLoader = foofClass.class.getClassLoader();
try {
uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
throw new FooException(e.getMessage());
} catch (NullPointerException e){
throw new FooException(e.getMessage());
}
if(uri == null){
throw new FooException("something is wrong directory or files missing");
}
/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
/** jar case */
try{
URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
//jar.toString() begins with file:
//i want to trim it out...
Path jarFile = Paths.get(jar.toString().substring("file:".length()));
FileSystem fs = FileSystems.newFileSystem(jarFile, null);
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
for(Path p: directoryStream){
InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
performFooOverInputStream(is);
/** your logic here **/
}
}catch(IOException e) {
throw new FooException(e.getMessage());
}
}
else{
/** IDE case */
Path path = Paths.get(uri);
try {
DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
for(Path p : directoryStream){
InputStream is = new FileInputStream(p.toFile());
performFooOverInputStream(is);
}
} catch (IOException _e) {
throw new FooException(_e.getMessage());
}
}
Another solution, you can do it using ResourceLoader like this:
import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;
#Autowire
private ResourceLoader resourceLoader;
...
Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
load(fi.next())
}
If you are using Spring you can use org.springframework.core.io.support.PathMatchingResourcePatternResolver and deal with Resource objects rather than files. This works when running inside and outside of a Jar file.
PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver();
Resource[] resources = r.getResources("/myfolder/*");
Then you can access the data using getInputStream and the filename from getFilename.
Note that it will still fail if you try to use the getFile while running from a Jar.
As the other answers point out, once the resources are inside a jar file, things get really ugly. In our case, this solution:
https://stackoverflow.com/a/13227570/516188
works very well in the tests (since when the tests are run the code is not packed in a jar file), but doesn't work when the app actually runs normally. So what I've done is... I hardcode the list of the files in the app, but I have a test which reads the actual list from disk (can do it since that works in tests) and fails if the actual list doesn't match with the list the app returns.
That way I have simple code in my app (no tricks), and I'm sure I didn't forget to add a new entry in the list thanks to the test.
Below code gets .yaml files from a custom resource directory.
ClassLoader classLoader = this.getClass().getClassLoader();
URI uri = classLoader.getResource(directoryPath).toURI();
if("jar".equalsIgnoreCase(uri.getScheme())){
Pattern pattern = Pattern.compile("^.+" +"/classes/" + directoryPath + "/.+.yaml$");
log.debug("pattern {} ", pattern.pattern());
ApplicationHome home = new ApplicationHome(SomeApplication.class);
JarFile file = new JarFile(home.getSource());
Enumeration<JarEntry> jarEntries = file.entries() ;
while(jarEntries.hasMoreElements()){
JarEntry entry = jarEntries.nextElement();
Matcher matcher = pattern.matcher(entry.getName());
if(matcher.find()){
InputStream in =
file.getInputStream(entry);
//work on the stream
}
}
}else{
//When Spring boot application executed through Non-Jar strategy like through IDE or as a War.
String path = uri.getPath();
File[] files = new File(path).listFiles();
for(File file: files){
if(file != null){
try {
InputStream is = new FileInputStream(file);
//work on stream
} catch (Exception e) {
log.error("Exception while parsing file yaml file {} : {} " , file.getAbsolutePath(), e.getMessage());
}
}else{
log.warn("File Object is null while parsing yaml file");
}
}
}
Took me 2-3 days to get this working, in order to have the same url that work for both Jar or in local, the url (or path) needs to be a relative path from the repository root.
..meaning, the location of your file or folder from your src folder.
could be "/main/resources/your-folder/" or "/client/notes/somefile.md"
Whatever it is, in order for your JAR file to find it, the url must be a relative path from the repository root.
it must be "src/main/resources/your-folder/" or "src/client/notes/somefile.md"
Now you get the drill, and luckily for Intellij Idea users, you can get the correct path with a right-click on the folder or file -> copy Path/Reference.. -> Path From Repository Root (this is it)
Last, paste it and do your thing.
Simple ... use OSGi. In OSGi you can iterate over your Bundle's entries with findEntries and findPaths.
Inside my jar file I had a folder called Upload, this folder had three other text files inside it and I needed to have an exactly the same folder and files outside of the jar file, I used the code below:
URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);
URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);
URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);

How to determine if a relative path is outside of a virtual path

I'm using the following function to convert paths into a valid Virtual Path:
public string GetFullPath(string path)
{
Ensure.Argument.NotNullOrEmpty(path, "path");
if (path[0] == '~') // a virtual path e.g. ~/assets/style.less
{
return path;
}
if (VirtualPathUtility.IsAbsolute(path)) // an absolute path e.g. /assets/style.less
{
return VirtualPathUtility.ToAppRelative(path,
HostingEnvironment.IsHosted ? HostingEnvironment.ApplicationVirtualPath : "/");
}
// otherwise, assume relative e.g. style.less or ../../variables.less
return VirtualPathUtility.Combine(VirtualPathUtility.AppendTrailingSlash(currentFileDirectory), path);
}
This passes all my tests other than for when the input path is a relative path, above the website directory.
For example given a currentFileDirectory of ~/foo/bar and a relative path of ../../../ I want to detect this and attempt to fix the path.
Server.MapPath is an easy way to validate either virtual or dos style paths. Validation is restricted for security reasons from validating any physical paths that are outside the web site. See my comment above.
To elaborate on Mike's answer we can validate whether a relative path attempts to go outside of the physical web site directory by:
Getting the combined path of the current path and relative path using Path.GetFullPath
Getting the file system path of the attempted path using Server.MapPath
Getting the file system path of the root of the application
Validating that the attempted path exists within the root path
Example:
var currentFileDirectory = "~/foo/bar";
var relativePath = "../../../";
var attemptedPath = Path.GetFullPath(Path.Combine(Server.MapPath(currentFileDirectory), relativePath)); // 1 + 2
var rootPath = Server.MapPath("~/"); // 3
if (attemptedPath.IndexOf(rootPath, StringComparison.InvariantCultureIgnoreCase) == -1) // 4
{
throw new Exception(string.Format("Path {0} is outside path {1}",
rootPath, HostingEnvironment.ApplicationPhysicalPath));
}

ASP.NET MVC 3, pass the file from server to client

Actually, this looks like the download feature, allowing a user to determine the local path where the file should be stored.
The whole thing is: the background program will generate a data file in the server, after that, I want to pass the data file from server to client.
I used FileStreamResult and FileContentResult, but it doesn't work.
A *.csv file was generated, then the file needs to transfer to the client. in controller , the code is very simple , like return new FilePathResult(filePath,"text/csv"); and I set the break point , the code execute without any exception , but I didn't see any web diaglog letting the user to select the path to save the csv file.
Try using return File()
public FileResult GetFile()
{
byte[] test = { 0 };
return File(test, "text/csv","TempFile.csv");
}
and calling it with an actionlink.
#Html.ActionLink("Download File","GetFile","Home")
Thank you guys, I changed the designe--place the csv file under the web root directory, (e.g. \File\Date\testing.csv) in the Controller method will return a json which point to the csv location (e.g. /File/Date/testing.csv) , the js should get the url and redirect to the /File/Date/testing.csv .
public FastJsonResult Download()
{
//generate the csv file under root path
//return the url point to the file
return JsonView(path);
}

Resources