source

위조 방지 제품을 제공하려면 어떻게 해야 합니까?$.ajax를 사용하여 JSON 데이터를 게시할 때 토큰?

ittop 2023. 3. 28. 22:34
반응형

위조 방지 제품을 제공하려면 어떻게 해야 합니까?$.ajax를 사용하여 JSON 데이터를 게시할 때 토큰?

이 투고의 코드는 다음과 같습니다.

먼저 어레이 변수에 컨트롤러 액션의 올바른 값을 입력합니다.

아래 코드를 사용하여 JavaScript 코드에 다음 행을 추가하는 것만으로 매우 간단하다고 생각합니다.

data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();

<%= Html.AntiForgeryToken() %>에는 ""가 .[ValidateAntiForgeryToken]

그러나 컨트롤러 동작에 "잘못된 위조 토큰"이 계속 표시됩니다.

내가 여기서 뭘 잘못하고 있는 거지?

코드

data["fiscalyear"] = fiscalyear;
data["subgeography"] = $(list).parent().find('input[name=subGeography]').val();
data["territories"] = new Array();

$(items).each(function() {
    data["territories"].push($(this).find('input[name=territory]').val());
});

    if (url != null) {
        $.ajax(
        {
            dataType: 'JSON',
            contentType: 'application/json; charset=utf-8',
            url: url,
            type: 'POST',
            context: document.body,
            data: JSON.stringify(data),
            success: function() { refresh(); }
        });
    }

MVC 4 이후 ValidationHttpRequestWrapper 솔루션은 필요하지 않습니다. 링크에 따르면

  1. 토큰을 헤더에 넣습니다.
  2. 필터를 만듭니다.
  3. 메서드에 속성을 추가합니다.

저의 솔루션은 다음과 같습니다.

var token = $('input[name="__RequestVerificationToken"]').val();
var headers = {};
headers['__RequestVerificationToken'] = token;
$.ajax({
    type: 'POST',
    url: '/MyTestMethod',
    contentType: 'application/json; charset=utf-8',
    headers: headers,
    data: JSON.stringify({
        Test: 'test'
    }),
    dataType: "json",
    success: function () {},
    error: function (xhr) {}
});


[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class ValidateJsonAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        var httpContext = filterContext.HttpContext;
        var cookie = httpContext.Request.Cookies[AntiForgeryConfig.CookieName];
        AntiForgery.Validate(cookie != null ? cookie.Value : null, httpContext.Request.Headers["__RequestVerificationToken"]);
    }
}


[HttpPost]
[AllowAnonymous]
[ValidateJsonAntiForgeryToken]
public async Task<JsonResult> MyTestMethod(string Test)
{
    return Json(true);
}

가 있는 것은 이할 것으로 이며, 액션에는 「」가 .[ValidateAntiForgeryToken]에서는 「이러다」라고 하는 를 상정하고 있습니다.__RequestVerificationTokenPOST하다

를 사용하고 에, POST 라고 하는 JSON.stringify(data)그러면 폼이 JSON 표현으로 변환되므로 예외가 느려집니다.

여기서 두 가지 해결 방법을 볼 수 있습니다.

1: '1: '을 사용합니다.x-www-form-urlencodedJSON" " " 청청개: 。

data["__RequestVerificationToken"] = $('[name=__RequestVerificationToken]').val();
data["fiscalyear"] = fiscalyear;
// ... other data if necessary

$.ajax({
    url: url,
    type: 'POST',
    context: document.body,
    data: data,
    success: function() { refresh(); }
});

2번: 요청을 다음 두 개의 매개 변수로 구분합니다.

data["fiscalyear"] = fiscalyear;
// ... other data if necessary
var token = $('[name=__RequestVerificationToken]').val();

$.ajax({
    url: url,
    type: 'POST',
    context: document.body,
    data: { __RequestVerificationToken: token, jsonRequest: JSON.stringify(data) },
    success: function() { refresh(); }
});

어떤 에도 POST를 .__RequestVerificationTokendiscloss.discloss 。

저는 지금 하고 있는 프로젝트에서 이 실제 문제를 구현하고 있었습니다.인증된 사용자를 필요로 하는 모든 Ajax POST에 대해 실행했습니다.

우선, 저는 jQuery Ajax에 전화를 걸기로 결심했습니다. 그래서 너무 자주 같은 말을 반복하지 않도록 하기 위해서입니다.이 JavaScript 스니펫을 사용하면 모든 Ajax(포스트) 콜이 내 요청 검증 토큰을 요청에 추가할 수 있습니다.주의: 이름 __RequestVerification토큰은 에 의해 사용됩니다.NET 프레임워크에서는 다음과 같이 표준 Anti-CSRF 기능을 사용할 수 있습니다.

$(document).ready(function () {
    securityToken = $('[name=__RequestVerificationToken]').val();
    $('body').bind('ajaxSend', function (elm, xhr, s) {
        if (s.type == 'POST' && typeof securityToken != 'undefined') {
            if (s.data.length > 0) {
                s.data += "&__RequestVerificationToken=" + encodeURIComponent(securityToken);
            }
            else {
                s.data = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
            }
        }
    });
});

위의 JavaScript 코드에서 토큰을 사용할 수 있어야 하는 보기에서 공통 HTML-Helper를 사용하십시오.기본적으로 이 코드를 원하는 위치에 추가할 수 있습니다.나는 그것을 if(요청) 안에 두었다.IsAuthenticated) 스테이트먼트:

@Html.AntiForgeryToken() // You can provide a string as salt when needed which needs to match the one on the controller

컨트롤러에서는 표준 ASP를 사용하기만 하면 됩니다.NET MVC 안티 CSRF 메커니즘(소금을 썼지만) 이렇게 했다.

[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public JsonResult SomeMethod(string param)
{
    // Do something
    return Json(true);
}

Firebug 등의 툴을 사용하면 POST 요구에 __RequestVerification이 어떻게 설정되어 있는지 쉽게 알 수 있습니다.토큰 파라미터가 추가되었습니다.

$.ajaxs 를 설정할 수 있습니다.traditional을 을 「아트리뷰트true 데이터를 encoded formurl 형식으로 '이렇게'를 설정해 .type:'POST' 서버 header에 대한커스텀 작성)을 사용할 JSON.stringyfy나 서버 측의 변경(예를 들어 snifp 헤더의 커스텀 속성 작성)을 사용할 필요가 없습니다.

ASP에서 사용해 본 적이 있습니다.NET MVC3 및 jquery 1.7 셋업으로 동작하고 있습니다.

코드 스니펫을 다음에 나타냅니다.

var data = { items: [1, 2, 3], someflag: true};

data.__RequestVerificationToken = $(':input[name="__RequestVerificationToken"]').val();

$.ajax({
    url: 'Test/FakeAction'
    type: 'POST',
    data: data
    dataType: 'json',
    traditional: true,
    success: function (data, status, jqxhr) {
        // some code after succes
    },
    error: function () {
        // alert the error
    }
});

이는 다음 시그니처를 가진 MVC 액션과 일치합니다.

[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult FakeAction(int[] items, bool someflag)
{
}

위조 방지 기능을 검증할 필요가 없습니다.게시된 JSON을 수신했을 때의 토큰.

그 이유는 위조방지가CSRF를 방지하기 위해 토큰이 생성되었습니다.AJAX 데이터를 다른 호스트에 게시할 수 없고 HTML 폼이 JSON을 요청 본문으로 제출할 수 없으므로 게시된 JSON에 대해 앱을 보호할 필요가 없습니다.

Request를 사용하여 글로벌하게 해결했습니다.헤더

$.ajaxPrefilter(function (options, originalOptions, jqXhr) {
    if (options.type.toUpperCase() === "POST") {
        // We need to add the verificationToken to all POSTs
        if (requestVerificationTokenVariable.length > 0)
            jqXhr.setRequestHeader("__RequestVerificationToken", requestVerificationTokenVariable);
    }
});

여기서 요청 검증TokenVariable은 토큰 값을 포함하는 변수 문자열입니다.그러면 모든 ajax 콜이 토큰을 서버로 전송하지만 기본 Validate는위조 방지TokenAttribute가 요청을 가져옵니다.폼 값헤더에서 요청으로 토큰을 복사하는 globalFilter를 작성 및 추가했습니다.폼, 디폴트 Validate를 사용할 수 있습니다.위조 방지토큰 속성:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
      filters.Add(new GlobalAntiForgeryTokenAttribute(false));
}


public class GlobalAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
{
    private readonly bool autoValidateAllPost;

    public GlobalAntiForgeryTokenAttribute(bool autoValidateAllPost)
    {
        this.autoValidateAllPost = autoValidateAllPost;
    }

    private const string RequestVerificationTokenKey = "__RequestVerificationToken";
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var req = filterContext.HttpContext.Request;
        if (req.HttpMethod.ToUpperInvariant() == "POST")
        {
            //gestione per ValidateAntiForgeryToken che gestisce solo il recupero da Request.Form (non disponibile per le chiamate ajax json)
            if (req.Form[RequestVerificationTokenKey] == null && req.IsAjaxRequest())
            {
                var token = req.Headers[RequestVerificationTokenKey];
                if (!string.IsNullOrEmpty(token))
                {
                    req.Form.SetReadOnly(false);
                    req.Form[RequestVerificationTokenKey] = token;
                    req.Form.SetReadOnly(true);
                }
            }

            if (autoValidateAllPost)
                AntiForgery.Validate();
        }
    }
}

public static class NameValueCollectionExtensions
{
    private static readonly PropertyInfo NameObjectCollectionBaseIsReadOnly = typeof(NameObjectCollectionBase).GetProperty("IsReadOnly", BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance);

    public static void SetReadOnly(this NameValueCollection source, bool readOnly)
    {
        NameObjectCollectionBaseIsReadOnly.SetValue(source, readOnly);
    }
}

이 작업은 이쪽에서 :)

형식 컨텐츠의 내용을 확인할 수 없습니다.입력: 'application/json; charset=utf-8'을 입력하십시오. 요청의 양식 속성이 아닌 InputStream 속성에 날짜가 업로드되고 이 요청은 수신되지 않습니다.폼["_검증 의뢰토큰"]

이것은 항상 비어 있어 검증에 실패합니다.

토큰을 JSON 오브젝트에 보관하고 있는데 Validate를 수정했습니다.위조 방지게시물이 json일 때 Request 개체의 InputStream을 확인하는 토큰 클래스.블로그에 글을 올렸으니 유용하게 쓰시길 바랍니다.

Dixin's Blog에서 이 작업에 대한 훌륭한 게시물을 확인하십시오.

또한 $.ajax 대신 $.post를 사용하는 것은 어떻습니까?

이 페이지의 jQuery 플러그인과 함께 다음과 같은 간단한 작업을 수행할 수 있습니다.

        data = $.appendAntiForgeryToken(data,null);

        $.post(url, data, function() { refresh(); }, "json");

AntiForgytoken은 AJAX를 통해 Newtonsoft를 지원합니다.SON ★★★★★★★★★★★★★★」
이하의 어프로치가 유효했습니다.
AJAX 게시물은 다음과 같이 유지합니다.

$.ajax({
  dataType: 'JSON',
  url: url,
  type: 'POST',
  context: document.body,
  data: {
    '__RequestVerificationToken': token,
    'model_json': JSON.stringify(data)
  };,
  success: function() {
    refresh();
  }
});

다음으로 MVC 액션에서 다음을 수행합니다.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(FormCollection data) {
 var model = JsonConvert.DeserializeObject < Order > (data["model_json"]);
 return Json(1);
}

이것이 도움이 되기를 바랍니다:)

JSON을 게시할 때 위조방지 토큰을 인증하기 위해 조금 음습해야 했지만 효과가 있었습니다.

//If it's not a GET, and the data they're sending is a string (since we already had a separate solution in place for form-encoded data), then add the verification token to the URL, if it's not already there.
$.ajaxSetup({
    beforeSend: function (xhr, options) {
        if (options.type && options.type.toLowerCase() !== 'get' && typeof (options.data) === 'string' && options.url.indexOf("?__RequestVerificationToken=") < 0 && options.url.indexOf("&__RequestVerificationToken=") < 0) {
            if (options.url.indexOf('?') < 0) {
                options.url += '?';
            }
            else {
                options.url += '&';
            }
            options.url += "__RequestVerificationToken=" + encodeURIComponent($('input[name=__RequestVerificationToken]').val());
        }
    }
});

그러나 이미 언급한 바와 같이 검증에서는 폼만 확인되며 JSON은 확인되지 않으며 쿼리 문자열은 확인되지 않습니다.속성 동작을 오버로드했습니다.모든 검증을 재실장하는 것은 끔찍한 일이었습니다(아마도 안전하지 않을 것입니다).그래서 토큰이 QueryString으로 전달된 경우 Form 속성을 오버로드하여 기본 제공 검증이 Form에 포함되었다고 생각합니다.

이 양식은 읽기 전용이지만 실행이 가능하기 때문에 조금 까다롭습니다.

    if (IsAuth(HttpContext.Current) && !IsGet(HttpContext.Current))
    {
        //if the token is in the params but not the form, we sneak in our own HttpContext/HttpRequest
        if (HttpContext.Current.Request.Params != null && HttpContext.Current.Request.Form != null
            && HttpContext.Current.Request.Params["__RequestVerificationToken"] != null && HttpContext.Current.Request.Form["__RequestVerificationToken"] == null)
        {
            AntiForgery.Validate(new ValidationHttpContextWrapper(HttpContext.Current), null);
        }
        else
        {
            AntiForgery.Validate(new HttpContextWrapper(HttpContext.Current), null);
        }
    }

    //don't validate un-authenticated requests; anyone could do it, anyway
    private static bool IsAuth(HttpContext context)
    {
        return context.User != null && context.User.Identity != null && !string.IsNullOrEmpty(context.User.Identity.Name);
    }

    //only validate posts because that's what CSRF is for
    private static bool IsGet(HttpContext context)
    {
        return context.Request.HttpMethod.ToUpper() == "GET";
    }

...

internal class ValidationHttpContextWrapper : HttpContextBase
{
    private HttpContext _context;
    private ValidationHttpRequestWrapper _request;

    public ValidationHttpContextWrapper(HttpContext context)
        : base()
    {
        _context = context;
        _request = new ValidationHttpRequestWrapper(context.Request);
    }

    public override HttpRequestBase Request { get { return _request; } }

    public override IPrincipal User
    {
        get { return _context.User; }
        set { _context.User = value; }
    }
}

internal class ValidationHttpRequestWrapper : HttpRequestBase
{
    private HttpRequest _request;
    private System.Collections.Specialized.NameValueCollection _form;

    public ValidationHttpRequestWrapper(HttpRequest request)
        : base()
    {
        _request = request;
        _form = new System.Collections.Specialized.NameValueCollection(request.Form);
        _form.Add("__RequestVerificationToken", request.Params["__RequestVerificationToken"]);
    }

    public override System.Collections.Specialized.NameValueCollection Form { get { return _form; } }

    public override string ApplicationPath { get { return _request.ApplicationPath; } }
    public override HttpCookieCollection Cookies { get { return _request.Cookies; } }
}

솔루션과 다른 점이 몇 가지 있습니다(구체적으로는 Http Module을 사용하고 있기 때문에 모든 POST에 속성을 추가할 필요는 없습니다).간단함을 위해 생략했습니다.필요하면 추가할 수 있습니다.

유감스럽게도 다른 응답은 jquery에 의해 처리되는 요구 포맷에 의존하며 payload를 직접 설정할 때는 어느 것도 동작하지 않습니다(솔직히 헤더에 넣는 것은 효과가 있었지만, 그 루트는 가고 싶지 않았습니다).

이 작업을 수행하려면beforeSend다음과 같은 기능이 있습니다. $.params()는 오브젝트를 표준 형식/url-module 형식으로 변환합니다.

토큰을 사용하여 json을 문자열화하는 다양한 방법을 시도했지만 모두 효과가 없었습니다.

$.ajax({
...other params...,
beforeSend: function(jqXHR, settings){

    var token = ''; //get token

    data = {
        '__RequestVerificationToken' : token,
        'otherData': 'value'
     }; 
    settings.data = $.param(data);
    }
});

```

위조 방지 장치를 설치해야 합니다.폼 태그의 토큰:

@using (Html.BeginForm(actionName:"", controllerName:"",routeValues:null, method: FormMethod.Get, htmlAttributes: new { @class="form-validator" }))
{
    @Html.AntiForgeryToken();
}

그런 다음 javascript에서 다음 코드를 수정하여

var DataToSend = [];
DataToSend.push(JSON.stringify(data), $('form.form-validator').serialize());
$.ajax({
  dataType: 'JSON',
  contentType: 'application/json; charset=utf-8',
  url: url,
  type: 'POST',
  context: document.body,
  data: DataToSend,
  success: function() {
    refresh();
  }
});

그러면 ActionResult 주석을 사용하여 요청을 검증할 수 있습니다.

[ValidateAntiForgeryToken]
        [HttpPost]

이게 도움이 됐으면 좋겠어요.

언급URL : https://stackoverflow.com/questions/2906754/how-can-i-supply-an-antiforgerytoken-when-posting-json-data-using-ajax

반응형