Cross-Site Request Forgery (CSRF) Attacks In ASP.NET Web API And Anti-Forgery Token
16:25Introduction
In my previous article I show how to use Basic Authentication in Web API and hosting in IIS. This article I will going to show how to protect our site from CSRF (Cross-Site Request Forgery) attacks.What Is CSRF and Anti-Forgery Tokens?
CSRF is Cross-Site Request Forgery. Suppose you login into a website that url is www.webproject.com and its uses Form authentication. The server authenticate your credentials and send a response with authentication cookie. Now you visits a malicious web site. The malicious site HTML like this:<h1>You Are Winner Of $50000!</h1> <form action="http://webproject.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="5000000" /> <input type="submit" value="Click Here"/> </form>So you see that the form action posts to the vulnerable site, not to the malicious site. This is the "cross-site" part of CSRF. When you click the button the request runs on the "www.webproject.com" server with your authentication context and can do anything that you can do after authenticate. Because the browser includes the authentication cookie with the request. This is CSRF(Cross-Site Request Forgery) attacks.
To prevent CSRF(Cross-Site Request Forgery) attacks ASP.NET MVC uses anti-forgery tokens, also called request verification tokens.
How Anti-forgery token works?
Anti-forgery tokens work because the malicious page cannot read the user’s tokens, due to same-origin policies. Same-orgin policies prevent documents hosted on two different sites from accessing each other’s content. So the malicious page can send requests to webproject.com, but it cannot read the response.We Create A Simple API Service Application For Transfer Fund
Now We Create A API Controller "Test API":
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace WebAPICSRFToken.Controllers { public class TestAPIController : ApiController { // GET api/testapi public IEnumerableAfter This We Create A Controller "Test":Get() { return new string[] { "value1", "value2" }; } // GET api/testapi/5 public string Get(int id) { return "value"; } // POST api/testapi public IEnumerable Post([FromBody]string value) { try { return new string[] { "Money transfer successfully" }; } catch (Exception) { return new string[] { "Sorry, You are not authorize person!!!" }; } } // PUT api/testapi/5 public void Put(int id, [FromBody]string value) { } // DELETE api/testapi/5 public void Delete(int id) { } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace WebAPICSRFToken.Controllers { public class TestController : Controller { public ActionResult Index() { return View(); } } }Our Views Is Like This:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Our Site</title> </head> <body style="background-color: #eeeeef"> <div style="margin-left: 40%; margin-top: 100px; background-color:#fff;width:220px;padding:50px;"> <h1>Fund Transfer</h1> <form action="api/TestAPI" method="post"> <button id="postData" name="postData" style="background-color: #e0dcdc; font: 15px; width: 200px; padding: 5px; border: 1px solid #000; cursor: pointer;">Transfer</button> </form> </div> </body> </html>
After This We Create A Malicious Site
We Create A API Controller Like The Previous API Controller (TestAPI):
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace MaliciousSite.Controllers { public class TestAPIController : ApiController { // GET api/testapi public IEnumerableCreate A "Test" ControllerGet() { return new string[] { "value1", "value2" }; } // GET api/testapi/5 public string Get(int id) { return "value"; } // POST api/testapi public void Post([FromBody]string value) { } // PUT api/testapi/5 public void Put(int id, [FromBody]string value) { } // DELETE api/testapi/5 public void Delete(int id) { } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MaliciousSite.Controllers { public class TestController : Controller { public ActionResult Index() { return View(); } } }Now The Views Structure Is Different, Something Like This:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Malicious Site</title> </head> <body style="background-color: #eeeeef"> <div style="margin-left: 40%; margin-top: 100px; background-color:#fff;width:220px;padding:50px;"> <h1>Fund Transfer</h1> <form action="http://localhost:51425/api/TestAPI/" method="post"> <button id="Button1" name="postData" style="background-color: #e0dcdc; font: 15px; width: 200px; padding: 5px; border: 1px solid #000; cursor: pointer;">Transfer</button> </form> </div> </body> </html>Look The Malicious Views Action Url Is "http://localhost:51425/api/TestAPI/".
Now We Run Our Application.
Here If I Click Transfer Button Then Request Hits To Our TestAPI Controller and Send A Message.
Now We Open The Malicious Site. Please Follow The Below Image:
After Click We Show The Malicious View In The Browser.
Now Click The Button Of The Malicious Site. We Show That The Request Hits To Our API Post Method and Show The Same Message.
So To Protect Our Site and Preventing CSRF Hacks We Developed A AntiForgeryToken to Prevent Cross Site Posting.
Now We Need To Change Our API Controller Like This:
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Web.Helpers; using System.Web.Http; namespace WebAPICSRFToken.Controllers { public class TestAPIController : ApiController { // GET api/testapi public IEnumerableAfter This We Need To Update Index Page Like This:Get() { return new string[] { "value1", "value2" }; } // GET api/testapi/5 public string Get(int id) { return "value"; } // POST api/testapi public IEnumerable Post([FromBody]string value) { string message = ""; CookieHeaderValue cookie = Request.Headers.GetCookies(AntiForgeryConfig.CookieName).FirstOrDefault(); if (cookie != null) { Stream stream = Request.Content.ReadAsStreamAsync().Result; stream.Position = 0; NameValueCollection val = Request.Content.ReadAsFormDataAsync().Result; try { AntiForgery.Validate(cookie[AntiForgeryConfig.CookieName].Value, val[AntiForgeryConfig.CookieName]); message = "Money transfer successfully"; } catch (Exception) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } } else { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } return new string[] { message }; } // PUT api/testapi/5 public void Put(int id, [FromBody]string value) { } // DELETE api/testapi/5 public void Delete(int id) { } } }
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Our Site</title> </head> <body style="background-color: #eeeeef"> <div style="margin-left: 40%; margin-top: 100px; background-color:#fff;width:220px;padding:50px;"> <h1>Fund Transfer</h1> <form action="api/TestAPI" method="post"> @Html.AntiForgeryToken() <button id="Button2" name="postData" style="background-color: #e0dcdc; font: 15px; width: 200px; padding: 5px; border: 1px solid #000; cursor: pointer;">Transfer</button> </form> </div> </body> </html>We Add "@Html.AntiForgeryToken()" In The Index Page.
This includes cookie-based authentication protocols, such as forms authentication, as well as protocols such as Basic and Digest authentication.
Now If We Run Application and Go To Malicious Site and Click Transfer Button. We Getting Error That "RequestVerificationToken" Not Found Because The Request Is Comming From Cross Site.
But The Request Is Hits To Our API Method. So To Protect It, We Need To Create A Handler Class In TokenHandlers Folder. The Class Is Following:
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Helpers; using System.Web.Http; namespace WebAPICSRFToken.TokenHandlers { public class AntiForgeryTokenHandler : DelegatingHandler { protected async override TaskNow We Call Our "AntiForgeryTokenHandler.cs" Class In "WebApiConfig.cs". Just Add "config.MessageHandlers.Add(new AntiForgeryTokenHandler());" In It Like This:SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { bool isCsrf = true; CookieHeaderValue cookie = request.Headers.GetCookies(AntiForgeryConfig.CookieName).FirstOrDefault(); if (cookie != null) { Stream requestBufferedStream = request.Content.ReadAsStreamAsync().Result; requestBufferedStream.Position = 0; NameValueCollection myform = request.Content.ReadAsFormDataAsync().Result; requestBufferedStream.Position = 0; try { AntiForgery.Validate(cookie[AntiForgeryConfig.CookieName].Value, myform[AntiForgeryConfig.CookieName]); isCsrf = false; } catch (Exception) { return request.CreateResponse(HttpStatusCode.Forbidden); } } if (isCsrf) { return request.CreateResponse(HttpStatusCode.Forbidden); } return await base.SendAsync(request, cancellationToken); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; using WebAPICSRFToken.TokenHandlers; namespace WebAPICSRFToken { public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.MessageHandlers.Add(new AntiForgeryTokenHandler()); } } }After That Run Application and Go To Malicious Site Click "Transfer" Button. Here Request Will Not Go To Our Code. We Can Show In The Network To Show What That Response Will Come From Our API. See The Following:
So We Able To Protect CSRF Attack By Using Anti Forgary Token.
0 comments