[asp.net-mvc] jQuery Ajax 호출과 Html.AntiForgeryToken ()

내 앱에서 CSRF 공격에 대한 완화를 구현했습니다.인터넷에서 블로그 게시물에서 읽은 정보에 따라 . 특히이 게시물은 내 구현의 동인이었습니다.

기본적으로 이러한 기사와 권장 사항에 따르면 CSRF 공격을 방지하려면 다음 코드를 구현해야합니다.

1) [ValidateAntiForgeryToken]POST Http 동사를 허용하는 모든 작업을 추가하십시오 .

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SomeAction( SomeModel model ) {
}

2) <%= Html.AntiForgeryToken() %>서버에 데이터를 제출하는 양식 안에 도우미를 추가하십시오.

<div style="text-align:right; padding: 8px;">
    <%= Html.AntiForgeryToken() %>
    <input type="submit" id="btnSave" value="Save" />
</div>

어쨌든 내 응용 프로그램의 일부 부분에서는 jQuery와 함께 Ajax POST를 서버에 전혀 양식을 작성하지 않고 서버에 수행하고 있습니다. 예를 들어 사용자가 이미지를 클릭하여 특정 작업을 수행 할 수있게합니다.

활동 목록이있는 테이블이 있다고 가정하십시오. 표의 열에 “활동 완료로 표시”라는 이미지가 있으며 사용자가 해당 활동을 클릭하면 다음 샘플과 같이 Ajax POST를 수행합니다.

$("a.markAsDone").click(function (event) {
    event.preventDefault();
    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: {},
        success: function (response) {
            // ....
        }
    });
});

<%= Html.AntiForgeryToken() %>이 경우 어떻게 사용할 수 있습니까? Ajax 호출의 data 매개 변수 안에 도우미 호출을 포함시켜야합니까?

긴 게시물에 대해 죄송합니다. 도와 주셔서 대단히 감사합니다.

편집 :

당으로 jayrdub 대답 나는 다음과 같은 방법으로 사용했다

$("a.markAsDone").click(function (event) {
    event.preventDefault();
    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: {
            AddAntiForgeryToken({}),
            id: parseInt($(this).attr("title"))
        },
        success: function (response) {
            // ....
        }
    });
});



답변

나는 이와 같은 간단한 js 함수를 사용한다

AddAntiForgeryToken = function(data) {
    data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
    return data;
};

페이지의 모든 양식은 토큰에 대해 동일한 값을 가지므로 최상위 마스터 페이지에 이와 같은 것을 넣으십시오.

<%-- used for ajax in AddAntiForgeryToken() --%>
<form id="__AjaxAntiForgeryForm" action="#" method="post"><%= Html.AntiForgeryToken()%></form>  

그런 다음 아약스 호출에서 (두 번째 예제와 일치하도록 편집)

$.ajax({
    type: "post",
    dataType: "html",
    url: $(this).attr("rel"),
    data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
    success: function (response) {
        // ....
    }
});


답변

360Airwalk에서 제공하는 솔루션이 마음에 들지만 조금 개선 될 수 있습니다.

첫 번째 문제는 $.post()빈 데이터로 만들면 jQuery가 Content-Type헤더를 추가하지 않으며이 경우 ASP.NET MVC가 토큰을 수신 및 확인하지 못한다는 것입니다. 따라서 헤더가 항상 있어야합니다.

또 다른 개선 사항은 POST, PUT, DELETE 등 컨텐츠 가 포함 된 모든 HTTP 동사 지원하는 것입니다 . 애플리케이션에서 POST 만 사용할 수 있지만 일반적인 솔루션을 사용하고 동사로 수신 한 모든 데이터에 위조 방지 기능이 있는지 확인하는 것이 좋습니다. 토큰.

$(document).ready(function () {
    var securityToken = $('[name=__RequestVerificationToken]').val();
    $(document).ajaxSend(function (event, request, opt) {
        if (opt.hasContent && securityToken) {   // handle all verbs with content
            var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
            opt.data = opt.data ? [opt.data, tokenParam].join("&") : tokenParam;
            // ensure Content-Type header is present!
            if (opt.contentType !== false || event.contentType) {
                request.setRequestHeader( "Content-Type", opt.contentType);
            }
        }
    });
});


답변

다른 답변이 많이 있다는 것을 알고 있지만이 기사는 훌륭하고 간결하며 일부 HttpPosts를 확인하지 않아도됩니다.

http://richiban.wordpress.com/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/

양식 모음을 수정하지 않고 HTTP 헤더를 사용합니다.

섬기는 사람

//make sure to add this to your global action filters
[AttributeUsage(AttributeTargets.Class)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
    public override void OnAuthorization( AuthorizationContext filterContext )
    {
        var request = filterContext.HttpContext.Request;

        //  Only validate POSTs
        if (request.HttpMethod == WebRequestMethods.Http.Post)
        {
            //  Ajax POSTs and normal form posts have to be treated differently when it comes
            //  to validating the AntiForgeryToken
            if (request.IsAjaxRequest())
            {
                var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];

                var cookieValue = antiForgeryCookie != null
                    ? antiForgeryCookie.Value
                    : null;

                AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
            }
            else
            {
                new ValidateAntiForgeryTokenAttribute()
                    .OnAuthorization(filterContext);
            }
        }
    }
}

고객

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

$.ajax({
    type: 'POST',
    url: '/Home/Ajax',
    cache: false,
    headers: headers,
    contentType: 'application/json; charset=utf-8',
    data: { title: "This is my title", contents: "These are my contents" },
    success: function () {
        ...
    },
    error: function () {
        ...
    }
});


답변

고급 네크로맨서 인 것 같지만 4 년 후에도 MVC5에서 여전히 문제가됩니다.

Ajax 요청을 올바르게 처리하려면 Ajax 호출시 위조 방지 토큰을 서버로 전달해야합니다. 이를 포스트 데이터 및 모델에 통합하는 것은 복잡하고 불필요합니다. 토큰을 사용자 정의 헤더로 추가하는 것은 깨끗하고 재사용이 가능하며 매번 기억할 필요가 없도록 구성 할 수 있습니다.

예외가 있습니다-눈에 거슬리지 않는 아약스는 아약스 호출에 대한 특별한 치료가 필요하지 않습니다. 토큰은 일반적인 숨겨진 입력 필드에서 평소와 같이 전달됩니다. 일반 POST와 정확히 동일합니다.

_Layout.cshtml

_layout.cshtml에는이 JavaScript 블록이 있습니다. DOM에 토큰을 쓰지 않고 MVC Helper가 생성하는 숨겨진 입력 리터럴에서 jQuery를 사용하여 토큰을 추출합니다. 헤더 이름 인 Magic 문자열은 속성 클래스에서 상수로 정의됩니다.

<script type="text/javascript">
    $(document).ready(function () {
        var isAbsoluteURI = new RegExp('^(?:[a-z]+:)?//', 'i');
        //http://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative

        $.ajaxSetup({
            beforeSend: function (xhr) {
                if (!isAbsoluteURI.test(this.url)) {
                    //only add header to relative URLs
                    xhr.setRequestHeader(
                       '@.ValidateAntiForgeryTokenOnAllPosts.HTTP_HEADER_NAME',
                       $('@Html.AntiForgeryToken()').val()
                    );
                }
            }
        });
    });
</script>

beforeSend 함수에서 작은 따옴표를 사용하십시오. 렌더링되는 입력 요소는 큰 따옴표를 사용하여 JavaScript 리터럴을 손상시킵니다.

클라이언트 JavaScript

이것이 실행되면 위의 beforeSend 함수가 호출되고 AntiForgeryToken이 요청 헤더에 자동으로 추가됩니다.

$.ajax({
  type: "POST",
  url: "CSRFProtectedMethod",
  dataType: "json",
  contentType: "application/json; charset=utf-8",
  success: function (data) {
    //victory
  }
});

서버 라이브러리

비표준 토큰을 처리하려면 사용자 정의 속성이 필요합니다. 이것은 @viggity의 솔루션을 기반으로하지만 눈에 띄지 않는 아약스를 올바르게 처리합니다. 이 코드는 공용 라이브러리에 넣을 수 있습니다

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ValidateAntiForgeryTokenOnAllPosts : AuthorizeAttribute
{
    public const string HTTP_HEADER_NAME = "x-RequestVerificationToken";

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var request = filterContext.HttpContext.Request;

        //  Only validate POSTs
        if (request.HttpMethod == WebRequestMethods.Http.Post)
        {

            var headerTokenValue = request.Headers[HTTP_HEADER_NAME];

            // Ajax POSTs using jquery have a header set that defines the token.
            // However using unobtrusive ajax the token is still submitted normally in the form.
            // if the header is present then use it, else fall back to processing the form like normal
            if (headerTokenValue != null)
            {
                var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];

                var cookieValue = antiForgeryCookie != null
                    ? antiForgeryCookie.Value
                    : null;

                AntiForgery.Validate(cookieValue, headerTokenValue);
            }
            else
            {
                new ValidateAntiForgeryTokenAttribute()
                    .OnAuthorization(filterContext);
            }
        }
    }
}

서버 / 컨트롤러

이제 액션에 속성을 적용하기 만하면됩니다. 또한 컨트롤러에 속성을 적용하면 모든 요청이 확인됩니다.

[HttpPost]
[ValidateAntiForgeryTokenOnAllPosts]
public virtual ActionResult CSRFProtectedMethod()
{
  return Json(true, JsonRequestBehavior.DenyGet);
}


답변

Html.AntiForgeryToken을 사용하지 마십시오 . 대신 ASP.NET MVC 응용 프로그램에서 CSRF (Cross-Site Request Forgery) 공격 방지에 설명 된대로 웹 API에서 AntiForgery.GetTokensAntiForgery.Validate 를 사용 하십시오 .


답변

나는 현재 프로젝트 에서이 실제 문제를 구현하고있었습니다. 인증 된 사용자가 필요한 모든 ajax-POST에 대해 수행했습니다.

먼저 jquery ajax 호출을 연결하여 너무 자주 반복하지 않기로 결정했습니다. 이 자바 스크립트 스 니펫은 모든 아약스 (포스트) 호출이 요청에 내 요청 유효성 검사 토큰을 추가하도록합니다. 참고 : __RequestVerificationToken이라는 이름은 .Net 프레임 워크에서 사용되므로 아래 표시된 것처럼 표준 Anti-CSRF 기능을 활용할 수 있습니다.

$(document).ready(function () {
    var 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);
            }
        }
    });
});

위의 자바 스크립트에서 토큰을 사용할 수 있어야하는 뷰에서 일반적인 HTML 도우미를 사용하십시오. 기본적으로이 코드를 원하는 곳에 추가 할 수 있습니다. if (Request.IsAuthenticated) 문 내에 배치했습니다.

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

컨트롤러에서 단순히 표준 ASP.Net MVC Anti-CSRF 메커니즘을 사용하십시오. 나는 이것을 실제로했다 (실제로 소금을 사용했지만).

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

Firebug 또는 이와 유사한 도구를 사용하면 POST 요청에 __RequestVerificationToken 매개 변수가 추가 된 방법을 쉽게 확인할 수 있습니다.


답변

“__RequestVerificationToken”입력이 POST 요청에 포함되어 있는지 확인하기 만하면됩니다. 정보의 나머지 절반 (즉, 사용자 쿠키의 토큰)은 이미 AJAX POST 요청과 함께 자동으로 전송됩니다.

예 :

$("a.markAsDone").click(function (event) {
    event.preventDefault();
    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: {
            "__RequestVerificationToken":
            $("input[name=__RequestVerificationToken]").val()
        },
        success: function (response) {
            // ....
        }
    });
});