During this session, we explain the Cross-Site attacks or XSRF/CSRF attacks and will show you how to manage Cross-Site attacks in ASP.NET Core. This type of attack is also known as XSRF/CSRF attacks. Cross-Site or XSRF/CSRF is an attack against web-hosted apps in which a malicious web app can affect how a client browser interacts with a web app that relies on that browser. As various forms of authentication tokens are sent by web browsers automatically with each request to a website, these attacks are made possible. This type of exploit makes use of the user’s previously authenticated session, it is also known as a one-click attack or session riding.
What are the Cross-Site Attacks?
Cross-Site or XSRF/CSRF is an attack against web-hosted apps in which a malicious web app can affect how a client browser interacts with a web app that relies on that browser. This type of exploit makes use of the user’s previously authenticated session.
Let us explain this type of attack with an example. Imagine you receive an email with the below content.
As the content is attractive, some users are tempted to click on the button to see what happens next. Then, there is the place where the Cross-Site attack occurs.
Because if we have already authenticated with our Bank web app with the browser, the below code which is behind the above attractive message, submit a transfer request to our bank account.
1 2 3 4 5 6 7 8 9 10 11 |
<h1>Congratulations! You're a Winner!</h1> <form action="https://yourbank.com/transffer" method="post"> <input type="hidden" name="Transaction" value=“transfer" /> <input type="hidden" name=“Receiver" value=“4154-45445-4444" /> <input type="hidden" name="Amount" value=“999999" /> <input type="submit" value="Click to collect your prize!" /> </form> |
As you can see instead of receiving any funds, we miss the above-mentioned amount of money from our bank account which was submitted by the malicious page. Of course, in real cases, it may not going to be as simple as we explain here, but I make it as simple as possible to convey the message.
Prevent Cross-Site Attacks in ASP.NET Core
There are several methods to prevent Cross-Site attacks (XSRF/CSRF). Also, the method that we provide during this session is not the only adequate way to secure your web app against this type of attack. But, we use Anti Forgery Token in this session to secure our web app against these attacks. The mentioned token is normally sent to the Controller from the form (with the Post method) in the View. It means, the asp-antiforgery Tag helper value is True by default and we don’t need to set this value inside the Form tag.
Next, we need to decorate the respective Action Methods to ValidateAntiForgeryToken attribute to secure the action methods against Cross-Site attacks.
Below code is related to the Cost Controller and we decorate all the action methods with the post method to the mentioned attribute. These modifications secure our action methods against this attack.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
namespace DailyCostWebApplication.Controllers { public class CostController : Controller { private readonly ICostRepository costRepository; private readonly ICategoryRepository categoryRepository; private readonly IWebHostEnvironment webHostEnvironment; public CostController(ICostRepository _costRepository, ICategoryRepository _categoryRepository, IWebHostEnvironment _webHostEnvironment) { costRepository = _costRepository; categoryRepository = _categoryRepository; webHostEnvironment = _webHostEnvironment; } [HttpGet] public IActionResult Create() { LoadDropdownList(); return View(); } [HttpPost] [ValidateAntiForgeryToken] public IActionResult Create(CreateCostViewModel model) { if (ModelState.IsValid) { Cost cost = new() { Amount = model.Amount, Comment = model.Comment, RegisteredDate = model.RegisteredDate, CategoryID = model.CategoryID, PaymentMethod = model.PaymentMethod }; if(model.UploadFile != null) { cost.InvoiceImagePath = UploadFile(model.UploadFile); } costRepository.Create(cost); return RedirectToAction("Index"); } LoadDropdownList(); return View(model); } [HttpGet] public IActionResult Index(string searchby, string searchfor, int? page, string sortby) { var costs = costRepository.GetCostList(searchby, searchfor,sortby).ToPagedList(page ?? 1, 5); return View(costs); } [HttpGet] public IActionResult Detail(int id) { var cost = costRepository.GetCostByID(id); return View(cost); } [HttpGet] public IActionResult Update(int id) { var cost = costRepository.GetCostByID(id); UpdateCostViewModel model = new() { ID = cost.ID, Amount = cost.Amount, RegisteredDate = cost.RegisteredDate, Comment = cost.Comment, CategoryID = cost.CategoryID, PaymentMethod = cost.PaymentMethod, ExsitingFile = cost.InvoiceImagePath }; LoadDropdownList(); return View(model); } [HttpPost] [ValidateAntiForgeryToken] public IActionResult Update(UpdateCostViewModel model) { if (ModelState.IsValid) { Cost UpdatedCost = costRepository.GetCostByID(model.ID); UpdatedCost.Amount = model.Amount; UpdatedCost.RegisteredDate = model.RegisteredDate; UpdatedCost.Comment = model.Comment; UpdatedCost.CategoryID = model.CategoryID; UpdatedCost.PaymentMethod = model.PaymentMethod; if(model.UploadFile != null) { if(UpdatedCost.InvoiceImagePath != null) { string ExitingFile = Path.Combine(webHostEnvironment.WebRootPath, "images", UpdatedCost.InvoiceImagePath); System.IO.File.Delete(ExitingFile); } UpdatedCost.InvoiceImagePath = UploadFile(model.UploadFile); } costRepository.Update(UpdatedCost); return RedirectToAction("Index"); } LoadDropdownList(); return View(model); } [ValidateAntiForgeryToken] [HttpPost] public IActionResult Delete(int id) { Cost cost = costRepository.GetCostByID(id); if (cost.InvoiceImagePath != null) { string ExitingFile = Path.Combine(webHostEnvironment.WebRootPath, "images", cost.InvoiceImagePath); System.IO.File.Delete(ExitingFile); } costRepository.Delete(id); return RedirectToAction("index"); } private void LoadDropdownList() { var Categories = categoryRepository.GetAllCategories(); List<SelectListItem> CatList = new(); CatList.Add(new SelectListItem("Select a Category", "-1")); foreach (var category in Categories) { CatList.Add(new SelectListItem(category.CategoryName, category.ID.ToString())); } List<SelectListItem> PaymentList = new(); PaymentList.Add(new SelectListItem("Select a Payment Method", "")); var PaymentMethod = Enum.GetValues(typeof(PaymentMethods)).Cast<PaymentMethods>().ToList(); for (var i = 0; i < PaymentMethod.Count(); i++) { PaymentList.Add(new SelectListItem(PaymentMethod[i].ToString(), i.ToString())); } ViewBag.Categories = CatList; ViewBag.PaymentMethods = PaymentList; } private string UploadFile(IFormFile formFile) { string UniqueFileName = Guid.NewGuid().ToString() + "-" + formFile.FileName; string TargetPath = Path.Combine(webHostEnvironment.WebRootPath, "images", UniqueFileName); using (var stream = new FileStream(TargetPath, FileMode.Create)) { formFile.CopyTo(stream); } return UniqueFileName; } } } |
If you need more details, watch this session video. Also, to be updated about our coming sessions, follow us on Instagram, Facebook, Telegram, or YouTube. Moreover, you can have access to the list of all sessions HERE and you can download this session source code from our GitHub.
You can download this Session Slides form HERE.