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.