2012-07-30 13 views
19

के साथ वेब एपीआई के लिए मान्यकरण एंटीफॉर्गेरी टोकन विशेषता लागू करने में समस्याएं मैं जेएसओएन-आधारित AJAX अनुरोध कर रहा हूं और एमवीसी नियंत्रकों के साथ फिल हैक के Preventing CSRF with AJAX और जोहान ड्रिसेन के Updated Anti-XSRF for MVC 4 RC के लिए बहुत आभारी हैं। लेकिन, जैसा कि मैं वेब एपीआई में एपीआई-केंद्रित नियंत्रकों को संक्रमण करता हूं, मैं उन मुद्दों को मार रहा हूं जहां दोनों दृष्टिकोणों के बीच कार्यक्षमता काफी अलग है और मैं सीएसआरएफ कोड को स्थानांतरित करने में असमर्थ हूं।एमवीसी 4 आरसी

स्कॉट्स ने हाल ही में question उठाया जो डारिन डिमिट्रोव द्वारा answered था। डारिन के समाधान में एक प्राधिकरण फ़िल्टर को कार्यान्वित करना शामिल है जो एंटीफॉर्गेरी। वैलिडेट को कॉल करता है। दुर्भाग्य से, यह कोड मेरे लिए काम नहीं करता है (अगले अनुच्छेद देखें) और - ईमानदारी से - मेरे लिए बहुत उन्नत है।

जैसा कि मैं इसे समझता हूं, फिल का समाधान एमवीसी एंटीफॉर्गेरी के साथ समस्या को खत्म करता है जब एक फॉर्म तत्व की अनुपस्थिति में JSON अनुरोध करता है; फॉर्म तत्व एंटीफॉर्गेरी द्वारा सत्यापित/अपेक्षित है। वैध विधि। मैं पर विश्वास करता हूं कि यही कारण है कि मुझे डारिन के समाधान के साथ भी समस्याएं आ रही हैं। मुझे एक HttpAntiForgeryException प्राप्त होता है "आवश्यक एंटी-फोर्जरी फॉर्म फ़ील्ड '__RequestVerificationToken' मौजूद नहीं है"। मुझे यकीन है कि टोकन पोस्ट किया जा रहा है (हालांकि फिल हेक के समाधान के शीर्षलेख में)। यहाँ ग्राहक की कॉल का एक स्नैपशॉट है:

$token = $('input[name=""__RequestVerificationToken""]').val(); 
$.ajax({ 
    url:/api/states", 
    type: "POST", 
    dataType: "json", 
    contentType: "application/json: charset=utf-8", 
    headers: { __RequestVerificationToken: $token } 
}).done(function (json) { 
    ... 
}); 

मैं एक साथ डैरिन के साथ जोहान समाधान mashing द्वारा हैक करने की कोशिश की और काम बातें प्राप्त करने में सक्षम था, लेकिन HttpContext.Current शुरू कर रहा हूँ, अनिश्चित है कि क्या यह उचित/सुरक्षित है और यही कारण है कि मैं प्रदान किए गए HttpActionContext का उपयोग नहीं कर सकते हैं।

public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) 
{ 
    try 
    { 
     var cookie = HttpContext.Current.Request.Cookies[AntiForgeryConfig.CookieName]; 
     AntiForgery.Validate(cookie != null ? cookie.Value : null, HttpContext.Current.Request.Headers["__RequestVerificationToken"]); 
    } 
    catch 
    { 
     actionContext.Response = new HttpResponseMessage 
     { 
      StatusCode = HttpStatusCode.Forbidden, 
      RequestMessage = actionContext.ControllerContext.Request 
     }; 
     return FromResult(actionContext.Response); 
    } 
    return continuation(); 
} 

मेरे सवाल कर रहे हैं:

  • हूँ यह सोच कर कि डैरिन समाधान हो जाती है में मैं सही

    यहाँ मेरी असजीला मैश-अप .. परिवर्तन कोशिश ब्लॉक में 2 लाइनों है एक फॉर्म तत्व का अस्तित्व?

  • जोहान के एमवीसी 4 आरसी कोड के साथ डारिन के वेब एपीआई फ़िल्टर को मैश-अप करने का एक शानदार तरीका क्या है?

अग्रिम धन्यवाद!

+0

FromResult क्या है? –

उत्तर

31

आप हेडर से पढ़ने की कोशिश कर सकते:

var headers = actionContext.Request.Headers; 
var cookie = headers 
    .GetCookies() 
    .Select(c => c[AntiForgeryConfig.CookieName]) 
    .FirstOrDefault(); 
var rvt = headers.GetValues("__RequestVerificationToken").FirstOrDefault(); 
AntiForgery.Validate(cookie != null ? cookie.Value : null, rvt); 

नोट: GetCookies एक विस्तार विधि उस वर्ग HttpRequestHeadersExtensions जो System.Net.Http.Formatting.dll का हिस्सा है में मौजूद है। यह सबसे अधिक संभावना उपलब्ध नहीं होगा C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Net.Http.Formatting.dll

+1

अरे ... 'GetCookies()' विधि के बारे में क्या? हमें इसे सामान्य रूप से कैसे कार्यान्वित करना है? –

+1

@ मेट्रोपॉलिटन उत्तर – Simon

+0

पर अद्यतन नोट देखें [एमवीसी स्रोत कोड] (http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.WebPages/Helpers/AntiXsrf/AntiForgeryTokenStore.cs), मैंने देखा है कि वे GetFormToken विधि में 'httpContext.Request.Form [_config.FormFieldName]' का उपयोग कर रहे हैं। क्या कोई कारण है कि अनुरोध और पैरामीटर दोनों फॉर्म और हेडर मानों तक पहुंचने के लिए क्यों नहीं उपयोग करें? मुझे लगता है कि इसमें कुकी भी शामिल हो सकती है :(। हालांकि फॉर्म उपलब्ध नहीं होने पर वे अनुरोध शीर्षलेख देख सकते हैं ... –

12

में बस को जोड़ने के लिए है कि इस दृष्टिकोण भी मेरे लिए काम किया (एक वेब एपीआई समाप्ति बिंदु को JSON पोस्टिंग .ajax) चाहता था, हालांकि मैं यह ActionFilterAttribute से इनहेरिट और OnActionExecuting विधि अधिभावी द्वारा एक सा सरलीकृत।

public class ValidateJsonAntiForgeryTokenAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     try 
     { 
      var cookieName = AntiForgeryConfig.CookieName; 
      var headers = actionContext.Request.Headers; 
      var cookie = headers 
       .GetCookies() 
       .Select(c => c[AntiForgeryConfig.CookieName]) 
       .FirstOrDefault(); 
      var rvt = headers.GetValues("__RequestVerificationToken").FirstOrDefault(); 
      AntiForgery.Validate(cookie != null ? cookie.Value : null, rvt); 
     } 
     catch 
     {    
      actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Unauthorized request."); 
     } 
    } 
} 
+6

डाउनवोट क्यों? मैं उत्तरदाता नहीं हूं लेकिन डाउनवोट्स एक टिप्पणी वारंट करता है। – DarrellNorton

0

शीर्षलेख की उपस्थिति के लिए चेक के साथ डारिन के उत्तर का उपयोग करके एक्सटेंशन विधि। चेक का अर्थ है कि परिणामस्वरूप त्रुटि संदेश गलत है ("आवश्यक एंटी-फोर्जरी फॉर्म फ़ील्ड" __RequestVerificationToken "मौजूद नहीं है।") बनाम दिया गया हैडर दिया गया था।"

public static bool IsHeaderAntiForgeryTokenValid(this HttpRequestMessage request) 
{ 
    try 
    { 
     HttpRequestHeaders headers = request.Headers; 
     CookieState cookie = headers 
       .GetCookies() 
       .Select(c => c[AntiForgeryConfig.CookieName]) 
       .FirstOrDefault(); 

     var rvt = string.Empty; 
     if (headers.Any(x => x.Key == AntiForgeryConfig.CookieName)) 
      rvt = headers.GetValues(AntiForgeryConfig.CookieName).FirstOrDefault(); 

     AntiForgery.Validate(cookie != null ? cookie.Value : null, rvt); 
    } 
    catch (Exception ex) 
    { 
     LogHelper.LogError(ex); 
     return false; 
    } 

    return true; 
} 

ApiController उपयोग:

public IHttpActionResult Get() 
{ 
    if (Request.IsHeaderAntiForgeryTokenValid()) 
     return Ok(); 
    else 
     return BadRequest(); 
} 
0

कार्यान्वयन AuthorizeAttribute का उपयोग कर:

using System; 
using System.Linq; 
using System.Net.Http; 
using System.Web; 
using System.Web.Helpers; 
using System.Web.Http; 
using System.Web.Http.Controllers; 

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
    public class ApiValidateAntiForgeryToken : AuthorizeAttribute { 
    public const string HeaderName = "X-RequestVerificationToken"; 

    private static string CookieName => AntiForgeryConfig.CookieName; 

    public static string GenerateAntiForgeryTokenForHeader(HttpContext httpContext) { 
     if (httpContext == null) { 
     throw new ArgumentNullException(nameof(httpContext)); 
     } 

     // check that if the cookie is set to require ssl then we must be using it 
     if (AntiForgeryConfig.RequireSsl && !httpContext.Request.IsSecureConnection) { 
     throw new InvalidOperationException("Cannot generate an Anti Forgery Token for a non secure context"); 
     } 

     // try to find the old cookie token 
     string oldCookieToken = null; 
     try { 
     var token = httpContext.Request.Cookies[CookieName]; 
     if (!string.IsNullOrEmpty(token?.Value)) { 
      oldCookieToken = token.Value; 
     } 
     } 
     catch { 
     // do nothing 
     } 

     string cookieToken, formToken; 
     AntiForgery.GetTokens(oldCookieToken, out cookieToken, out formToken); 

     // set the cookie on the response if we got a new one 
     if (cookieToken != null) { 
     var cookie = new HttpCookie(CookieName, cookieToken) { 
      HttpOnly = true, 
     }; 
     // note: don't set it directly since the default value is automatically populated from the <httpCookies> config element 
     if (AntiForgeryConfig.RequireSsl) { 
      cookie.Secure = AntiForgeryConfig.RequireSsl; 
     } 
     httpContext.Response.Cookies.Set(cookie); 
     } 

     return formToken; 
    } 


    protected override bool IsAuthorized(HttpActionContext actionContext) { 
     if (HttpContext.Current == null) { 
     // we need a context to be able to use AntiForgery 
     return false; 
     } 

     var headers = actionContext.Request.Headers; 
     var cookies = headers.GetCookies(); 

     // check that if the cookie is set to require ssl then we must honor it 
     if (AntiForgeryConfig.RequireSsl && !HttpContext.Current.Request.IsSecureConnection) { 
     return false; 
     } 

     try { 
     string cookieToken = cookies.Select(c => c[CookieName]).FirstOrDefault()?.Value?.Trim(); // this throws if the cookie does not exist 
     string formToken = headers.GetValues(HeaderName).FirstOrDefault()?.Trim(); 

     if (string.IsNullOrEmpty(cookieToken) || string.IsNullOrEmpty(formToken)) { 
      return false; 
     } 

     AntiForgery.Validate(cookieToken, formToken); 
     return base.IsAuthorized(actionContext); 
     } 
     catch { 
     return false; 
     } 
    } 
    } 

तो बस अपने नियंत्रक या [ApiValidateAntiForgeryToken]

साथ तरीकों को सजाने और उस्तरा में जोड़ें जावास्क्रिप्ट के लिए अपना टोकन जेनरेट करने के लिए इसे फ़ाइल करें:

<script> 
var antiForgeryToken = '@ApiValidateAntiForgeryToken.GenerateAntiForgeryTokenForHeader(HttpContext.Current)'; 
// your code here that uses such token, basically setting it as a 'X-RequestVerificationToken' header for any AJAX calls 
</script>