0

I have a web app in .NET Core that serves all files in a certain folder. Now before serving the files, a password is needed. Is this code sufficient or is there a loophole in it?

using DownloadFiles.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace DownloadFiles.Controllers
{
    public class HomeController : Controller
    {
        private readonly Pass _pass;
        private readonly IHostingEnvironment _webHostEnvironment;

        public HomeController(IHostingEnvironment webHostEnvironment, IOptions<Pass> pass)
        {
            _webHostEnvironment = webHostEnvironment;
            _pass = pass.Value;
        }

        [HttpGet]
        public IActionResult Index()
        {
            string Path1 = _webHostEnvironment.WebRootPath + "\\..\\Files\\";
            string[] Files = Directory.GetFiles(Path1);
            List<FilesModel> FilesList = new List<FilesModel>();
            foreach (string item in Files)
            {
                FilesModel file = new FilesModel
                {
                    FileName = Path.GetFileName(item),
                    FilePath = "Home/DownloadFile/"
                };
                file.FilePath += file.FileName;
                FilesList.Add(file);
            }
            return View(FilesList.OrderBy(x => x.FileName));
        }

        [HttpGet]
        public IActionResult DownloadFile(string id)
        {
            string FileName = id;
            if (string.IsNullOrEmpty(FileName))
            {
                return RedirectToAction("Index");
            }
            PwModel PW = new PwModel
            {
                FileName = FileName
            };
            return View(PW);
        }

        [HttpPost]
        public IActionResult DownloadFile(PwModel PassModel)
        {
            string Pass = PassModel.Password;
            if (string.IsNullOrEmpty(PassModel.FileName))
            {
                return RedirectToAction("Index");
            }
            if (!string.Equals(Pass, _pass.Password))
            {
                ModelState.AddModelError("", "Incorrect password!");
                return View();
            }
            string FilePath = _webHostEnvironment.WebRootPath + "\\..\\Files\\" + PassModel.FileName;
            byte[] fileBytes = System.IO.File.ReadAllBytes(FilePath);
            return File(fileBytes, "application/x-msdownload", PassModel.FileName);
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

The above is the controller. Below is the index view:

@model IEnumerable<DownloadFiles.Models.FilesModel>
@{
    ViewData["Title"] = "Home";
}

<style>
    a:hover {
        text-decoration: none;
    }
</style>

@{
    int count1 = 0;
    <table class="table table-responsive table-condensed table-hover table-bordered">
        <thead>
            <tr>
                <th>No.</th>
                <th>File Name</th>
                <th>Download</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model)
            {
                count1++;
                string SerialNum = count1.ToString() + ".";
                string Title1 = "Download " + item.FileName;
                <tr>
                    <td>@SerialNum</td>
                    <td><a href="@item.FilePath" style="color: black">@item.FileName</a></td>
                    <td><a title="@Title1" class="btn btn-success" href="@item.FilePath"><span class="glyphicon glyphicon-download-alt"></span></a></td>
                </tr>
            }
        </tbody>
    </table>
}

I just want to make sure the users can't just write a url in address bar and get the files circumventing the password.

3
  • Well, it look like the files are in a subfolder to wwwroot, so I guess any user could access the file directly via yoursite/Files/<filename>? And you really should avoid taking in filename as a parameter like that, it could open up for exploits if the filename is like "..\..\appsettings.json" or any file on your system. Commented Jan 26, 2021 at 7:24
  • I've edited the code. It is outside of wwwroot folder now: _webHostEnvironment.WebRootPath + "\\..\\Files\\" + PassModel.FileName; Commented Jan 26, 2021 at 7:39
  • But you still open any file-path input, you should't just concat the parameter with the path - you could for example have the file-index as a parameter and get the filename= Directory.GetFiles(Path1)[file-index] instead. Commented Jan 26, 2021 at 8:14

0

Browse other questions tagged or ask your own question.