Makale Özeti

Bu yazımızda yeni nesil web tekniklerinde ortaya çıkan bir risk CSRF (Cross Side Request Forgery) saldırılarına karşı nasıl bir önlem alınabileceğini ASP.NET WebApi ve MVC4 üzerinde uygulamalı olarak değindik.

Makale

CSRF (Cross Side Request Forgery) aynı zamanda XSRF olarak da kısaltılmıştır. Türkçeye "çapraz site sahteciliği" olarak çevrilmiştir. Şu şekilde kısa ifade edebiliriz, bir saldırganın siteniz üzerinde verilere erişip değiştirebilmesi, silebilmesi veyahut veri yazabilmesi. Bunun önüne geçilebilmesi için ajax methodları için yazdığımız servisleri yetkilendirmeye tabi tutacağız, bunu da sitemizi barındırdığımız IIS üzerinde bir machine key oluşturup, sitemiz dahilinde bu servisi kullanan sayfada bu key ile kriptolanmış bir token oluşturup istemcinin makinasına cookie ile atacağız, daha sonra gelen her istekte bu tokenı okuyarak karşılaştıracağız ve açık ajax methodları (webapi restful servislerimiz) güvenli hale getirilmiş olacak. Tüm bu token oluşturma, cookie oluşturma vs. işlerini her sayfada biz değil de bizim yerimize Microsoftun getirmiş olduğu Antifoırgery token sistemi yapacak. Şimdi uygulamalı örneğimizle daha iyi açıklayalım;

Örneğimizde otel listesi dönen bir view var ve bu view webapi controllerdan ajax request ile veri çekiyor, webapi servisinin sadece bizim sitemizden gelen requestlere cevap vermesini istiyoruz ve diğer tüm requestlere exception fırlatmasını bekliyoruz, böylece GET methodunda uygulandığında veri çalınmasının önüne geçmek, hem de POST methodunda uygulandığında istenmeyen sahte form verileri üretilmesinin engellenmesini amaçlıyoruz.

Otel listesi dönen view;

HotelList.cshtml

@{
    ViewBag.Title = "Hotel List";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<form id="__AjaxAntiForgeryForm" action="#">@Html.AntiForgeryToken()</form>
  <input type="button" class="search-button" value="Ara" onclick="GetHotelList(); return false;">
  <!-- Hotel List -->
            <div class="hotel-list">
                <!-- loading BEGIN -->
                  <div id="siteLoader" style="width:300px;margin:auto;display:none; z-index:3;">
                    <div id="loadImg">
                    <img src="~/Content/images/loading.gif" border="0">
                    </div>
                  </div>
                <!-- loading END -->
                <div id="divHotelResult"></div>

            </div>
   <!-- Hotel List -->
 viewımız her ne kadar GET methodu kullansada bir form ve tokenı değerini üretip sayfaya bir hidden field ile basacak olan
@Html.AntiForgeryToken() fonksiyonunu ekledik. sayfa render edildiğinde sayfa kaynağında aşağıdaki gibi bir token eklendiğini görürüz,
<form id="__AjaxAntiForgeryForm" action="#"><input name="__RequestVerificationToken" type="hidden" value="AcKivtXxcbUSPqyNM9FC1N2Olunc3IWK6tZPxr3rTeOTikbFOPb3NCJiL5pwZEW9xk9E7JmuP3KDdTaUCxc3MYWbUycnbbWWIr9Gi-TFaFbgVt9xIRCiAs9LHG3-hwSgclziMQ2" /></form>
Şimdi webapi controller ımıza antiforgery token validate ettirecek atrribute ekleyelim,


[ValidateHttpAntiForgeryToken]
public class TApi1Controller : ApiController
{
// GET api/tapi1
public IEnumerable<HotelConsept> GetHotelList()
{
var hotels = BusinessLayer.DataProvider.GetHotelList("", "", "", 1, 0);
return hotels;
}

Projemize Filters adında yeni bir klasör yaratalım, ve antiforgery validate edecek olan attribute classımızı ekleyelim,

public class ValidateHttpAntiForgeryTokenAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
HttpRequestMessage request = actionContext.ControllerContext.Request;
try
{
var cookie = request.Headers
.GetCookies()
.Select(c => c[AntiForgeryConfig.CookieName])
.FirstOrDefault();
AntiForgery.Validate(cookie != null ? cookie.Value : null, request.Headers.GetValues("__RequestVerificationToken").FirstOrDefault());
}
catch (HttpAntiForgeryException e)
{
actionContext.Response = request.CreateErrorResponse(HttpStatusCode.Forbidden, e);
}
}
Ve son olarak ajax methodumuzu çağıran javascript dosyamıza gerekli düzenlemeyi yapıyoruz,

var token = $('[name=__RequestVerificationToken]').val();
var headers = {};
headers["__RequestVerificationToken"] = token;

function GetHotelSearchList() {
jQuery.support.cors = true;
$.ajax({
url: 'http://mydomain/GetHotelList',
type: 'GET',
contentType: "application/json;charset=utf-8",
cache: false,
headers: headers,
success: function (data) {
WriteResponseHotelList(data);
},

scriptte görüldüğü üzere, token değerini header olarak ajax requestimize ekleyip sunucuya gönderiyor ve validate edilmesini sağlıyoruz, bunu yapmazsak istenen responseu vermeyecek ve header error dönecektir.

Ürettiğimiz token, session bazlı cookie oluşturur, token değerini web.config system.web altında de belirteceğimiz machineKey tag ile belirteceğiz,

 <machineKey validationKey="C155B0F32216020427A11841A17E9792B1FD7AAB7B0916EDF334788BCDFD1FB96093D1FD2AA94D270C6FFD47E55178549143623E435773732D67C16F78974059" decryptionKey="AD4A4DB683704373A30CF9BA109EFB13C0DA5E2345823905F61BBBD56BD3F12B" validation="SHA1" decryption="AES" />
</system.web>

machineKey üretmek için şu adresi kullanabilirsiniz,

http://aspnetresources.com/tools/machineKey

Artık CSRF attack larına karşı güvendeyiz, sitemizi test ettikten sonra saldırıya karşı nasıl cevap verdiğini görmek için test amaçlı olarak şu kısmı kapatıp sabit bir değer vermeyi deneyin aşağıdaki resimdeki gibi hata verecektir ,

<!--<form id="__AjaxAntiForgeryForm" action="#">@Html.AntiForgeryToken()</form>-->
<form id="__AjaxAntiForgeryForm" action="#"><input name="__RequestVerificationToken" type="hidden" value="fBoEFIJVpx8kdsgD0l9jWQI0sTKr2aZkeFyLlt143IOwfMDBKhGFFzNUFD7sdsuxGkVVd51-jhB8ZFIc7hkfj2Apuik_bAaQ94xp7hwOb_CCPrRvhAzMwK04-ZWEHVsS0hStRQ2" /></form>
 

Önlemin çalıştığından emin olduktan sonra tekrar eski haline çevirip kaydetmeyi unutmayalım,
<form id="__AjaxAntiForgeryForm" action="#">@Html.AntiForgeryToken()</form>

Herkese iyi çalışmalar dilerim.

Mücahid Uslu