Monday 6 June 2016

Avoid the CSRF attacks by implement the Antiforgery Token in MVC Web API call while using Angular JS

In this post we are going to see what is CSRF attacks and how we can avoid this kind of attacks in WEB API calls using AntiForgery token implementation inside web applications developed using a Angualr JS and Asp.Net MVC.

Cross Site Request Forgery CSRF is an attack where a unknown site sends a request to another site which is already logged in.

Consider an Example
1. Launch a Website
2. Login the Website , now the session id or authcookie is maintained in the client browser.
3. Next launch a another website which have the malicious code like below

<form action="http://facetrack.com/api/account" method="POST">
    <input type="text" name="Id" value="1" />
     <input type="hidden" name="name" value="Rajesh" />
    <input type="submit" value="Submit"  />
</form>


4. Above code call the website which is already login with the auth cookie, now the call is really sent from the another site, i.e Cross site Request Forgery by using the Authcookie or session id from browser.

Now we see how to implement a AntiForgery Token in WebApi calls using Angular js.
Steps
1. Create a Extn class

    public static class RequestHelperExtn
    {
        public static string GetHeaderToken(this HtmlHelper helper) {
            string requestToken = RequestHeaderToken();
            return  string.Format("request-header-token={0}", requestToken);
        }

        public static string RequestHeaderToken()
        {
            string cookie, form;
            AntiForgery.GetTokens(nullout cookie, out form);
            return cookie + "~" + form;
        }

        public static Tuple<stringstring> ParseTokens(string token)
        {
            if (!string.IsNullOrWhiteSpace(token))
            {
                var tokens = token.Split('~');
                if(token.Length > 1)
                return Tuple.Create<stringstring>(tokens[0].Trim(), tokens[1].Trim());
            }                       
            return Tuple.Create<stringstring>("""");
        }


    }

2.  Create a ActionFilterAttribute for check the AntiForgery token before the execution of method.
   
   public class AntiForgeryTokenValidateAttribute:ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            string cookie=string.Empty;
            string form = string.Empty;
            Tuple<stringstring> parsedtoken = 
                  Tuple.Create<stringstring>(string.Empty, string.Empty);

            IEnumerable<string> tokenheaders;
            if (actionContext.Request.Headers.TryGetValues("requestToken"out tokenheaders))             {
                if (tokenheaders.Count() > 0)
                     parsedtoken = RequestHelperExtn.ParseTokens(tokenheaders.First());
                cookie = parsedtoken.Item1;
                form = parsedtoken.Item2;
            }

            AntiForgery.Validate(cookie, form);
            base.OnActionExecuting(actionContext);

        }

    }


3. Create a Angular js directive which is used to add the Antiforgery Token in Headers of Http Call.

app.directive('requestHeaderToken', ['$http',function ($http) {

    return {
        restrict: 'A',
        scope: {
            requestHeaderToken:'@'
        },
        link:function(scope,elem,attrs){
            $http.defaults.headers.common['requestToken'] = scope.requestHeaderToken;
        }

    }


}]);


4. Place the action attribute over the http method in the Web API calls.

    public class ValuesController : ApiController
    {
        // GET api/values
        [AntiForgeryTokenValidate]
        public IEnumerable<string> Get()
        {
            return new string[] { "Rajesh G""Suresh G" };
        }


        [AntiForgeryTokenValidate]
        // GET api/values/5
        public CustomJsonResult Get(int id)
        {
           
            return new CustomJsonResult() {
                Content = new { data = new[] { "India""SriLanka""Japan" }                                }                
            };
        }


        [AntiForgeryTokenValidate]
        //GET api/values
        public HttpResponseMessage Post([FromBody]string result)
        {
            JToken json = JObject.Parse("{'Id':1,'Name':'Rajesh'}");
            return new HttpResponseMessage() {
                Content = new JsonContentResponse(json)
            };
        }

   
        // PUT api/values/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }

    }

5.  First we invoke the client HTML with out place the AntiForgery Token

HTML:
**********
<div id="body" ng-app="app">  
    <section class="featured">
        <div ng-controller="AppController" style="padding-left:100px">
            <div>               
                <br />
                <br />
                <button ng-click="GetValues()"> Get Values with Antiforgery                      
                   Token</button>

                <div style="top:10px">
                    <div style="margin-top:30px"><b>Employees List</b>
                    </div>
                   <ul style="list-style:none">
                        <li ng-repeat="item in collections">{{item}}</li>
                    </ul>
                </div>
            </div>
            <br />
            <div style="width:500px">
                <div style="float:left">
                    <button ng-click="GetValuesByID()"> Get Valuesby Id with Antiforgery
                       Token</button>
                </div>
                <div style="float:right">
                    <button ng-click="PostValues()">Get Json Data</button>
                </div>
            </div>
            <div style="width:500px">
                <div style="height:200px;float:left">
                    <br />                   
                    <div><b>CustomJsonResult</b>
                        {{Data.Content}}
                    </div><br />
                    <b>Countries</b>
                    <br />                   
                    <ul style="list-style:none">
                        <li ng-repeat="item in Data.Content.data">{{item}}</li>
                    </ul>
                    <b></b>
                </div>
                <div style="float:right">
                    <br />
                    <b>JsonContent</b>
                    <table>
                        <tr><td>ID :</td><td>{{empdata.Id}}</td></tr>
                        <tr><td>Name :</td><td>{{empdata.Name}}</td></tr>
                    </table>
                </div>
            </div>
            <br />
            <br />
        </div>
    </section>
</div>


Angular App.Js
********************
var app = angular.module('app', []);


app.factory('DBContext', ['$http'function ($http) {

    return {
        GetValues: GetValues,
        GetValuesByID: GetValuesByID,
        PostValues: PostValues
    };

   
    function GetValues () {
        return $http({
            url: 'api/Values',
            method: 'get'
        });
    };

    function GetValuesByID(){
        return $http({
            url: 'api/Values/2',
            method: 'get'           
        });
    };

    function PostValues() {
        return $http({
            url: 'api/Values',
            method: 'POST',
            data: {
                result: 'sample data'
            }

        });
    };

}]);

app.controller('AppController', ['$scope''DBContext'function ($scope, DBContext) {

    $scope.Value = "Rajesh";
    $scope.collections = [];
    $scope.Data = '';
    $scope.empdata = {};
    $scope.GetValues = function () {

        DBContext.GetValues().then(function (data) {
            var result = data.data || data || [];
            $scope.collections = result;
        }, function () { });

    }

    $scope.GetValuesByID = function () {
        DBContext.GetValuesByID().then(function (data) {
            $scope.Data = data.data;
        }, function (error) {
            console.log(error);
        });
    }

    $scope.PostValues = function () {


        DBContext.PostValues().then(function (result) {
            $scope.empdata = result.data;
        }, function (error) {
            console.log("Error");
            console.log(error);
        })

    }


}]);


Output:
*********
Now invoke any one of button click with out  AntiforgeryToken attribute we will get an error like below in console, because i logged the error in browser console.








6. Now place the AntiForgery Token in the Client Html it is an angular attribute place by an HtmlHelper class 
@Html.GetHeaderToken()

Example to place inside a tag  
<div  @Html.GetHeaderToken()>    

Now we placed that  HtmlHelper inside a div tag which is present after the ng-controller defined.

<div ng-controller="AppController" style="padding-left:100px">
     <div  @Html.GetHeaderToken()>               
       <br /> 
</div>
</div>                     

After placing the Tag

Html View in Client Browser Have a token









Web Api Request Call have the AntiForgery Token in the Header









Output:
*********


From this Post you can understand how to implement a AntiForgery Token in Web API Calls in ASP.Net MVC using Angular JS

2 comments: