Các cuộc gọi jQuery Ajax và Html.AntiForgeryToken ()


207

Tôi đã thực hiện trong ứng dụng của mình việc giảm thiểu các cuộc tấn công CSRF sau các thông tin mà tôi đã đọc trên một số bài đăng trên blog trên internet. Đặc biệt những bài đăng này đã là động lực thực hiện của tôi

Về cơ bản những bài viết và khuyến nghị nói rằng để ngăn chặn cuộc tấn công CSRF, bất kỳ ai cũng nên thực hiện mã sau đây:

1) Thêm [ValidateAntiForgeryToken]vào mọi hành động chấp nhận động từ POST http

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

2) Thêm trình <%= Html.AntiForgeryToken() %>trợ giúp bên trong các biểu mẫu gửi dữ liệu đến máy chủ

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

Dù sao, trong một số phần của ứng dụng của tôi, tôi đang thực hiện POST POST với jQuery cho máy chủ mà không có bất kỳ hình thức nào cả. Điều này xảy ra ví dụ khi tôi cho phép người dùng nhấp vào hình ảnh để thực hiện một hành động cụ thể.

Giả sử tôi có một bảng với một danh sách các hoạt động. Tôi có một hình ảnh trên một cột của bảng có nội dung "Đánh dấu hoạt động là đã hoàn thành" và khi người dùng nhấp vào hoạt động đó, tôi đang thực hiện POST POST như trong mẫu sau:

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

Làm thế nào tôi có thể sử dụng <%= Html.AntiForgeryToken() %>trong những trường hợp này? Tôi có nên bao gồm cuộc gọi của người trợ giúp bên trong tham số dữ liệu của cuộc gọi Ajax không?

Xin lỗi vì bài viết dài và cảm ơn rất nhiều vì đã giúp đỡ

CHỈNH SỬA :

Theo câu trả lời của jayrdub, tôi đã sử dụng theo cách sau

$("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) {
            // ....
        }
    });
});

Các liên kết David Hayden tại 404s, dường như anh ấy di cư blog của mình để một CMS mới, nhưng không di chuyển tất cả các nội dung cũ hơn.

Câu trả lời:


252

Tôi sử dụng một chức năng js đơn giản như thế này

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

Vì mọi hình thức trên một trang sẽ có cùng giá trị cho mã thông báo, chỉ cần đặt một cái gì đó như thế này vào trang chính trên cùng của bạn

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

Sau đó, trong cuộc gọi ajax của bạn làm (được chỉnh sửa để phù hợp với ví dụ thứ hai của bạn)

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

6
Rất vui, tôi thích việc đóng gói mã thông báo.
bóng

2
@Lorenzo, hãy đặt dữ liệu tùy chỉnh của bạn vào trong cuộc gọi AddAntiForgeryToken, như vậy:data: AddAntiForgeryToken({ id: parseInt($(this).attr("title")) }),
jball

3
Làm thế nào xấu một ý tưởng sẽ được sử dụng ajaxSendhoặc ghi đè ajaxđể luôn luôn tăng cường datavới mã thông báo chống giả mạo? Có thể thêm một số kiểm tra để đảm bảo rằng số phận urldành cho máy chủ của bạn.
ta.speot.is

1
Hãy cẩn thận nếu bạn sử dụng bộ đệm đầu ra.
Barbaros Alp

1
@SouhaiebBesbes mã xác thực phải giống nhau cho người dùng trên tất cả các trang (nó hoạt động cùng với một cookie được đặt và giữ nguyên). Vì vậy, không có vấn đề gì nếu có nhiều yêu cầu trên mỗi trang, nó sẽ giống nhau nếu trang cơ sở được tải lại.
Jeremy Weir

29

Tôi thích giải pháp được cung cấp bởi 360Airwalk, nhưng nó có thể được cải thiện một chút.

Vấn đề đầu tiên là nếu bạn thực hiện $.post()với dữ liệu trống, jQuery không thêm Content-Typetiêu đề và trong trường hợp này, ASP.NET MVC không thể nhận và kiểm tra mã thông báo. Vì vậy, bạn phải đảm bảo tiêu đề luôn ở đó.

Một cải tiến khác là hỗ trợ tất cả các động từ HTTP có nội dung : POST, PUT, DELETE, v.v. Mặc dù bạn chỉ có thể sử dụng POST trong ứng dụng của mình, tốt hơn là nên có một giải pháp chung và xác minh rằng tất cả dữ liệu bạn nhận được với bất kỳ động từ nào đều có khả năng chống giả mạo mã thông báo.

$(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);
            }
        }
    });
});

1
+1 bạn nói đúng, tôi chưa nghĩ đến vấn đề cuộc gọi bài trống. Cảm ơn các đầu vào. bạn đã đúng về việc chúng tôi chưa sử dụng xóa / đặt trong dự án của chúng tôi.
360Airwalk

2
+1 để cứu tôi khỏi phải thêm chức năng vào tất cả các cuộc gọi jQuery.Ajax
Dragos Durlut

2
+1 Chỉ là một ghi chú cho hậu thế, tài liệu jQuery cho .ajaxSend()các trạng thái "Kể từ jQuery 1.8, phương thức .ajaxSend () chỉ nên được đính kèm vào tài liệu." api.jquery.com/ajaxsend
RJ Cuthbertson

1
@Bronx Nguồn optionsgốc từ đâu, được liệt kê trong iftuyên bố cuối cùng ? Cảm ơn.
hvaughan3

Cẩn thận khi sử dụng điều này nếu bạn có nhiều hình thức trên một trang. Bạn sẽ cần đặt giá trị trong beforeSend bằng lệnh gọi cụ thể hơn thay vì cho tài liệu.
Đàn

22

Tôi biết có rất nhiều câu trả lời khác, nhưng bài viết này rất hay và súc tích và buộc bạn phải kiểm tra tất cả các HTTPPost của mình, không chỉ một số trong số chúng:

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

Nó sử dụng các tiêu đề HTTP thay vì cố gắng sửa đổi bộ sưu tập biểu mẫu.

Người phục vụ

//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);
            }
        }
    }
}

Khách hàng

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
Thuộc tính từ bài viết bạn liên kết quá kết hợp với phản hồi của Bronx là giải pháp DRY cuối cùng cho vấn đề này.
TugboatCaptain

2
Tuyệt vời tìm thấy. Tôi đã chỉnh sửa câu trả lời của bạn để bao gồm các đoạn mã để câu trả lời tự đứng vững, nhưng tôi hy vọng mọi người cũng sẽ đọc phần còn lại của bài viết. Đây dường như là một giải pháp rất sạch sẽ.
Tim Medora

cảm ơn Tim, đó là một ý tưởng tuyệt vời, thật bực bội khi một liên kết bị chết và câu trả lời trở nên vô giá trị. Tôi đã bắt đầu làm điều này trên tất cả các câu trả lời mới của tôi.
viggity

Đây là MVC, WebAPI hay .NetCore? Tôi không thể có được các không gian tên chính xác cho WebAPI 5
Myster

20

Tôi cảm thấy giống như một necromancer tiên tiến ở đây, nhưng đây vẫn là vấn đề 4 năm sau trong MVC5.

Để xử lý các yêu cầu ajax đúng cách, mã thông báo chống giả mạo cần được chuyển đến máy chủ trong các cuộc gọi ajax. Tích hợp nó vào dữ liệu bài đăng và mô hình của bạn là lộn xộn và không cần thiết. Thêm mã thông báo làm tiêu đề tùy chỉnh sẽ sạch và có thể sử dụng lại - và bạn có thể định cấu hình nó để bạn không phải nhớ làm điều đó mỗi lần.

Có một ngoại lệ - ajax không phô trương không cần điều trị đặc biệt cho các cuộc gọi ajax. Mã thông báo được truyền như bình thường trong trường nhập liệu ẩn thông thường. Chính xác giống như một POST thông thường.

_Layout.cshtml

Trong _layout.cshtml tôi có khối JavaScript này. Nó không ghi mã thông báo vào DOM, thay vào đó, nó sử dụng jQuery để trích xuất nó từ nghĩa đen đầu vào ẩn mà Trình trợ giúp MVC tạo ra. Chuỗi Magic là tên tiêu đề được định nghĩa là hằng số trong lớp thuộc tính.

<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>

Lưu ý việc sử dụng các dấu ngoặc đơn trong hàm beforeSend - phần tử đầu vào được hiển thị sử dụng dấu ngoặc kép sẽ phá vỡ chữ JavaScript.

JavaScript khách hàng

Khi điều này thực thi chức năng beforeSend ở trên được gọi và AntiForgeryToken sẽ tự động được thêm vào các tiêu đề yêu cầu.

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

Thư viện máy chủ

Một thuộc tính tùy chỉnh được yêu cầu để xử lý mã thông báo không chuẩn. Điều này được xây dựng dựa trên giải pháp của @ viggity, nhưng xử lý chính xác ajax không phô trương. Mã này có thể được giấu trong thư viện chung của bạn

[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);
            }
        }
    }
}

Máy chủ / Bộ điều khiển

Bây giờ bạn chỉ cần áp dụng thuộc tính cho hành động của bạn. Thậm chí tốt hơn bạn có thể áp dụng thuộc tính cho bộ điều khiển của bạn và tất cả các yêu cầu sẽ được xác thực.

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

Giải pháp hoàn hảo, tập trung hơn nhiều. Cảm ơn
David Freire

Bạn có thể giải thích chi tiết hơn tại sao bạn chỉ muốn thêm tiêu đề cho các URL tương đối không? Điều đó đã đi qua đầu tôi. Giải pháp tuyệt vời!
MattM

tương đối đảm bảo rằng tiêu đề chỉ được đặt cho các yêu cầu quay lại máy chủ của riêng bạn, vì thiết lập ajax bao gồm tất cả các yêu cầu được thực hiện với jquery, chúng tôi không muốn mã thông báo được gửi trên các yêu cầu jsonp hoặc CORS. Điều này có thể đúng với các url tuyệt đối, nhưng tương đối được đảm bảo là cùng một miền.
Sẽ D

1
@WillD Tôi thích giải pháp của bạn, nhưng đã buộc phải sửa đổi nó một chút. Bởi vì bạn chọn $.ajaxSetupđịnh nghĩa một trình xử lý sự kiện chung beforesend, có thể xảy ra việc bạn ghi đè lên nó. Tôi tìm thấy một giải pháp khác trong đó bạn có thể thêm một trình xử lý thứ hai cũng sẽ được gọi. Hoạt động độc đáo và không phá vỡ triển khai của bạn.
Viper

Có ai có phiên bản ASP.net 5 của khách hàng xác thực thuộc tính AntiForgery không? Phiên bản này không biên dịch trong phiên bản mới nhất!
Rob McCabe

19

Đừng sử dụng Html.AntiForgeryToken . Thay vào đó, hãy sử dụng AntiForgery.GetTokensAntiForgery.Validate từ API Web như được mô tả trong Ngăn chặn tấn công giả mạo yêu cầu chéo trang web (CSRF) trong ứng dụng ASP.NET MVC .


Đối với các phương thức hành động của bộ điều khiển mô hình liên kết một kiểu mô hình máy chủ với AJAX JSON đã đăng, cần có loại nội dung là "application / json" để sử dụng chất kết dính mô hình thích hợp. Thật không may, điều này ngăn cấm sử dụng dữ liệu biểu mẫu, được yêu cầu bởi thuộc tính [ValidateAntiForgeryToken], vì vậy phương pháp của bạn là cách duy nhất tôi có thể tìm thấy để làm cho nó hoạt động. Câu hỏi duy nhất của tôi là, nó vẫn hoạt động trong một trang trại web hay nhiều trường hợp vai trò web Azure? Bạn có @Edward, hoặc bất cứ ai khác biết nếu đây là một vấn đề?
Richard B

@Edward Brey Bạn có thể giải thích rõ hơn về lý do tại sao chúng ta không nên sử dụng nó?
Odys

4
@Odys: Không có gì sai với Html.AntiForgeryToken, nhưng nó có nhược điểm: yêu cầu một biểu mẫu, yêu cầu jQuery và giả định các chi tiết triển khai Html.AntiForgeryToken không có giấy tờ. Tuy nhiên, nó vẫn ổn trong nhiều bối cảnh. Tuyên bố của tôi "Đừng sử dụng Html.AntiForgeryToken" có lẽ quá mạnh mẽ. Ý tôi là nó không được sử dụng với API Web, trong khi AntiForgery.GetTokens linh hoạt hơn.
Edward Brey

cám ơn! Tôi đã phải thay đổi một chút để làm cho nó hoạt động cho bộ điều khiển MVC5, nhưng đây là giải pháp
jao

3
Nó chắc chắn không yêu cầu một hình thức. Bạn chỉ cần phân tích DOM cho nó theo tên. Sử dụng jquery, tôi có thể thêm nó vào bên trong đối tượng dữ liệu của mình thông qua dữ liệu {__RequestVerifyingToken: $ ("input [name = __ RequestVerifyingToken]"). Val ()}
Anthony Mason

16

Tôi chỉ thực hiện vấn đề thực tế này trong dự án hiện tại của tôi. tôi đã làm điều đó cho tất cả các ajax-POST cần một người dùng xác thực.

Trước hết tôi quyết định thực hiện các cuộc gọi ajax jquery của mình để tôi không lặp lại quá thường xuyên. đoạn mã javascript này đảm bảo tất cả các cuộc gọi ajax (bài đăng) sẽ thêm mã thông báo xác thực yêu cầu của tôi vào yêu cầu. Lưu ý: tên __RequestVerifyingToken được sử dụng bởi khung .Net để tôi có thể sử dụng các tính năng Anti-CSRF tiêu chuẩn như được hiển thị bên dưới.

$(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);
            }
        }
    });
});

Trong Chế độ xem của bạn, nơi bạn cần mã thông báo có sẵn cho javascript ở trên, chỉ cần sử dụng Trình trợ giúp HTML phổ biến. Về cơ bản bạn có thể thêm mã này bất cứ nơi nào bạn muốn. Tôi đã đặt nó trong một câu lệnh if (Request.IsAuthenticated):

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

Trong bộ điều khiển của bạn chỉ cần sử dụng cơ chế Anti-CSRF tiêu chuẩn của ASP.Net. Tôi đã làm nó như thế này (mặc dù tôi thực sự sử dụng Salt).

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

Với Fireorms hoặc một công cụ tương tự, bạn có thể dễ dàng thấy các yêu cầu POST của mình hiện có thông số __RequestVerifyingToken được nối thêm như thế nào.


15

Tôi nghĩ rằng tất cả những gì bạn phải làm là đảm bảo rằng đầu vào "__RequestVerifyingToken" được bao gồm trong yêu cầu POST. Nửa thông tin còn lại (tức là mã thông báo trong cookie của người dùng) đã được gửi tự động với yêu cầu AJAX POST.

Ví dụ,

$("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) {
            // ....
        }
    });
});

1
Sau nhiều giờ thử nghiệm với bài viết AJAX của jQuery từ trong trang MVC (Dao cạo), đây là câu trả lời đơn giản nhất trong tất cả những gì hiệu quả với tôi. Chỉ bao gồm các trường dữ liệu của riêng bạn (hoặc viewModel mà tôi cho là) ​​sau mã thông báo dưới dạng một phần dữ liệu mới (nhưng trong đối tượng dữ liệu gốc).
Ralph Bacon

Tôi sẽ thực hiện việc này như thế nào nếu hàm AJAX ở trong trang .html chứ không phải trang Dao cạo?
Bob the Builder

Nếu trang html của bạn không có máy chủ được cung cấp, AntiForgeryTokenthì dù sao đi nữa. Nếu nó (không chắc chắn làm thế nào bạn có được một trong trường hợp đó, nhưng giả sử bạn là như vậy), thì ở trên sẽ hoạt động tốt. Nếu bạn đang cố gắng tạo một trang web đơn giản sẽ gửi yêu cầu đến một máy chủ mong đợi mã thông báo đã nói và máy chủ không tạo trang nói, thì bạn đã hết may mắn. Đó thực chất là quan điểm của AntiForgeryToken ...
jball

6

Bạn cũng có thể làm điều này:

$("a.markAsDone").click(function (event) {
    event.preventDefault();

    $.ajax({
        type: "post",
        dataType: "html",
        url: $(this).attr("rel"),
        data: $('<form>@Html.AntiForgeryToken()</form>').serialize(),
        success: function (response) {
        // ....
        }
    });
});

Điều này đang sử dụng Razor, nhưng nếu bạn đang sử dụng WebFormscú pháp, bạn cũng có thể sử dụng <%= %>các thẻ


4

Hơn nữa với nhận xét của tôi chống lại câu trả lời của @ JBall đã giúp tôi trên đường đi, đây là câu trả lời cuối cùng phù hợp với tôi. Tôi đang sử dụng MVC và Dao cạo và tôi đang gửi một biểu mẫu bằng jQuery AJAX để tôi có thể cập nhật chế độ xem một phần với một số kết quả mới và tôi không muốn thực hiện một bài đăng hoàn chỉnh (và nhấp nháy trang).

Thêm vào @Html.AntiForgeryToken()bên trong mẫu như bình thường.

Mã nút gửi AJAX của tôi (tức là một sự kiện onclick) là:

//User clicks the SUBMIT button
$("#btnSubmit").click(function (event) {

//prevent this button submitting the form as we will do that via AJAX
event.preventDefault();

//Validate the form first
if (!$('#searchForm').validate().form()) {
    alert("Please correct the errors");
    return false;
}

//Get the entire form's data - including the antiforgerytoken
var allFormData = $("#searchForm").serialize();

// The actual POST can now take place with a validated form
$.ajax({
    type: "POST",
    async: false,
    url: "/Home/SearchAjax",
    data: allFormData,
    dataType: "html",
    success: function (data) {
        $('#gridView').html(data);
        $('#TestGrid').jqGrid('setGridParam', { url: '@Url.Action("GetDetails", "Home", Model)', datatype: "json", page: 1 }).trigger('reloadGrid');
    }
});

Tôi đã để lại hành động "thành công" vì nó cho thấy chế độ xem một phần đang được cập nhật có chứa MvcJqGrid và cách nó được làm mới (lưới jqGrid rất mạnh và đây là trình bao bọc MVC tuyệt vời cho nó).

Phương thức điều khiển của tôi trông như thế này:

    //Ajax SUBMIT method
    [ValidateAntiForgeryToken]
    public ActionResult SearchAjax(EstateOutlet_D model) 
    {
        return View("_Grid", model);
    }

Tôi phải thừa nhận rằng mình không phải là người hâm mộ của việc gửi toàn bộ dữ liệu của biểu mẫu dưới dạng Mô hình nhưng nếu bạn cần thực hiện thì đây là một cách hoạt động. MVC chỉ làm cho việc liên kết dữ liệu trở nên quá dễ dàng, thay vì gửi 16 giá trị riêng lẻ (hoặc FormCollection được gõ yếu), điều này là ổn, tôi đoán vậy. Nếu bạn biết rõ hơn xin vui lòng cho tôi biết vì tôi muốn sản xuất mã MVC C # mạnh mẽ.


4

đã tìm thấy ý tưởng rất thông minh này từ https://gist.github.com/scottrippey/3428114 cho mỗi cuộc gọi $ .ajax, nó sửa đổi yêu cầu và thêm mã thông báo.

// Setup CSRF safety for AJAX:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    if (options.type.toUpperCase() === "POST") {
        // We need to add the verificationToken to all POSTs
        var token = $("input[name^=__RequestVerificationToken]").first();
        if (!token.length) return;

        var tokenName = token.attr("name");

        // If the data is JSON, then we need to put the token in the QueryString:
        if (options.contentType.indexOf('application/json') === 0) {
            // Add the token to the URL, because we can't add it to the JSON data:
            options.url += ((options.url.indexOf("?") === -1) ? "?" : "&") + token.serialize();
        } else if (typeof options.data === 'string' && options.data.indexOf(tokenName) === -1) {
            // Append to the data string:
            options.data += (options.data ? "&" : "") + token.serialize();
        }
    }
});

Tôi đã thử một vài lựa chọn thay thế khác ở trên, đây là điều đã giải quyết nó cho tôi.
HostMyBus

Tôi đã làm tuy nhiên phải thêm if (options.contentType != false && options.contentType.indexOf('application/json') === 0) { để bắt các cuộc gọi Ajax chưa chỉ định loại nội dung
HostMyBus

3

1. Xác định chức năng để nhận mã thông báo từ máy chủ

@function
{

        public string TokenHeaderValue()
        {
            string cookieToken, formToken;
            AntiForgery.GetTokens(null, out cookieToken, out formToken);
            return cookieToken + ":" + formToken;                
        }
}

2.Nhận mã thông báo và đặt tiêu đề trước khi gửi đến máy chủ

var token = '@TokenHeaderValue()';    

       $http({
           method: "POST",
           url: './MainBackend/MessageDelete',
           data: dataSend,
           headers: {
               'RequestVerificationToken': token
           }
       }).success(function (data) {
           alert(data)
       });

3. Xác thực Onserver trên HttpRequestBase trên phương thức bạn xử lý Post / get

        string cookieToken = "";
        string formToken = "";
        string[] tokens = Request.Headers["RequestVerificationToken"].Split(':');
            if (tokens.Length == 2)
            {
                cookieToken = tokens[0].Trim();
                formToken = tokens[1].Trim();
            }
        AntiForgery.Validate(cookieToken, formToken);

1

Tôi biết rằng đã được một thời gian kể từ khi câu hỏi này được đăng, nhưng tôi đã tìm thấy tài nguyên thực sự hữu ích, thảo luận về việc sử dụng AntiForgeryToken và làm cho nó ít rắc rối hơn khi sử dụng. Nó cũng cung cấp plugin jquery để dễ dàng bao gồm mã thông báo chống phân tách trong các cuộc gọi AJAX:

Bí quyết chống giả mạo cho ASP.NET MVC và AJAX

Tôi không đóng góp nhiều, nhưng có lẽ ai đó sẽ thấy nó hữu ích.


Bài viết đó giống như một dặm dài! Tôi chắc chắn điều đó thật tuyệt nhưng tl; dr
Anh

1
Quá tệ, bởi vì nó độc đáo bao gồm chủ đề. Nó không chỉ cho bạn biết cách sử dụng tính năng, mà còn giải thích vấn đề mà nó khắc phục và cung cấp cho bạn ngữ cảnh để hiểu cách sử dụng nó một cách chính xác. Khi nói đến bảo mật tôi nghĩ rằng sự hiểu biết sâu sắc là quan trọng.
slawek

2
Nếu nó quan trọng thì nó nên được viết theo cách khuyến khích mọi người đọc nó;)
Anh

1

lần đầu tiên sử dụng @ Html.AntiForgeryToken () trong html

 $.ajax({
        url: "@Url.Action("SomeMethod", "SomeController")",
        type: 'POST',
        data: JSON.stringify(jsonObject),
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        async: false,
        beforeSend: function (request) {
            request.setRequestHeader("RequestVerificationToken", $("[name='__RequestVerificationToken']").val());
        },
        success: function (msg) {
            alert(msg);
        }

1

Đây là cách dễ nhất tôi từng thấy. Lưu ý: Đảm bảo bạn có "@ Html.AntiForgeryToken ()" trong Chế độ xem của bạn

  $("a.markAsDone").click(function (event) {
        event.preventDefault();
        var sToken = document.getElementsByName("__RequestVerificationToken")[0].value;
        $.ajax({
            url: $(this).attr("rel"),
            type: "POST",
            contentType: "application/x-www-form-urlencoded",
            data: { '__RequestVerificationToken': sToken, 'id': parseInt($(this).attr("title")) }
        })
        .done(function (data) {
            //Process MVC Data here
        })
        .fail(function (jqXHR, textStatus, errorThrown) {
            //Process Failure here
        });
    });

0

Cải thiện nhẹ cho giải pháp 360Airwalk. Điều này nhúng mã thông báo chống giả mạo trong chức năng javascript, vì vậy @ Html.AntiForgeryToken () không còn cần phải được đưa vào mỗi chế độ xem.

$(document).ready(function () {
    var securityToken = $('@Html.AntiForgeryToken()').attr('value');
    $('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);
            }
        }
    });
});

0
function DeletePersonel(id) {

    var data = new FormData();
    data.append("__RequestVerificationToken", "@HtmlHelper.GetAntiForgeryToken()");

    $.ajax({
        type: 'POST',
        url: '/Personel/Delete/' + id,
        data: data,
        cache: false,
        processData: false,
        contentType: false,
        success: function (result) {
        }
    });
}

public static class HtmlHelper {
    public static string GetAntiForgeryToken() {
        System.Text.RegularExpressions.Match value = 
                System.Text.RegularExpressions.Regex.Match(System.Web.Helpers.AntiForgery.GetHtml().ToString(), 
                        "(?:value=\")(.*)(?:\")");
        if (value.Success) {
            return value.Groups[1].Value;
        }
        return "";
    }
}

0

Tôi đang sử dụng một bài đăng ajax để chạy một phương thức xóa (tình cờ là từ dòng thời gian visjs nhưng đó không phải là tương đối). Đây là những gì tôi sis:

Đây là Index.cshtml của tôi

@Scripts.Render("~/bundles/schedule")
@Styles.Render("~/bundles/visjs")
@Html.AntiForgeryToken()

<!-- div to attach schedule to -->
<div id='schedule'></div>

<!-- div to attach popups to -->
<div id='dialog-popup'></div>

Tất cả tôi đã thêm ở đây là @Html.AntiForgeryToken() để làm cho mã thông báo xuất hiện trong trang

Sau đó, trong bài ajax của tôi, tôi đã sử dụng:

$.ajax(
    {
        type: 'POST',
        url: '/ScheduleWorks/Delete/' + item.id,
        data: {
            '__RequestVerificationToken': 
            $("input[name='__RequestVerificationToken']").val()
              }
     }
);

Mà thêm giá trị mã thông báo, loại bỏ trang, vào các trường được đăng

Trước đó, tôi đã thử đặt giá trị vào các tiêu đề nhưng tôi đã gặp lỗi tương tự

Hãy đăng bài cải tiến. Đây chắc chắn là một cách tiếp cận đơn giản mà tôi có thể hiểu


0

Được rồi rất nhiều bài viết ở đây, không ai trong số họ giúp tôi, ngày và ngày của google, và tôi vẫn không hiểu thêm về việc viết bài cho toàn bộ ứng dụng từ đầu, và sau đó tôi nhận thấy cái nugget nhỏ này trong Web.confg của tôi

 <httpCookies requireSSL="false" domain="*.localLookup.net"/>

Bây giờ tôi không biết tại sao tôi lại thêm nó tuy nhiên tôi đã nhận thấy, nó bị bỏ qua trong chế độ gỡ lỗi và không ở chế độ sản xuất (IE được cài đặt vào IIS ở đâu đó)

Đối với tôi, giải pháp là một trong 2 lựa chọn, vì tôi không nhớ tại sao tôi lại thêm nó. Tôi không thể chắc chắn những thứ khác không phụ thuộc vào nó, và thứ hai tên miền phải là chữ thường và TLD không giống như tôi đã làm trong * .localLookup.net

Có lẽ nó giúp có thể nó không. Tôi hy vọng nó sẽ giúp được ai đó


0

Giải pháp tôi tìm thấy không phải dành cho ASPX mà là cho dao cạo, nhưng vấn đề khá phức tạp.

Tôi đã giải quyết nó bằng cách thêm AntiForgery vào yêu cầu. Trình trợ giúp HTML không tạo id HTML bằng lệnh gọi

@Html.AntiForgeryToken()

Để thêm mã thông báo vào postrequest, tôi chỉ cần thêm id AntiForgery vào trường ẩn với jquery:

$("input[name*='__RequestVerificationToken']").attr('id', '__AjaxAntiForgeryForm');

Điều này khiến bộ điều khiển chấp nhận yêu cầu với thuộc tính [ValidateAntiForgeryToken]


-3

AntiforgeryToken vẫn còn là một nỗi đau, không có ví dụ nào ở trên làm việc từng chữ cho tôi. Quá nhiều cho đó. Vì vậy, tôi kết hợp tất cả chúng. Cần một @ Html.AntiforgeryToken ở dạng treo xung quanh iirc

Giải quyết như vậy:

function Forgizzle(eggs) {
    eggs.__RequestVerificationToken =  $($("input[name=__RequestVerificationToken]")[0]).val();
    return eggs;
}

$.ajax({
            url: url,
            type: 'post',
            data: Forgizzle({ id: id, sweets: milkway }),
});

Khi nghi ngờ, hãy thêm dấu $

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.