65.9K
CodeProject 正在变化。 阅读更多。
Home

如何进行安全的 AJAX 调用

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (9投票s)

2016年9月3日

CPOL

2分钟阅读

viewsIcon

59711

AJAX 是一种构建 Web 应用程序的现代方式,其中大部分代码在客户端运行,以获得更好的性能和用户体验。那么我们如何进行安全的 AJAX 调用以防止 CSRF(跨站请求伪造)攻击呢?

引言

在 Web 开发的现代时代,开发人员更注重高性能以获得更好的用户体验。AJAX 是构建 Web 应用程序的现代方式之一,其中大部分代码在客户端运行,并且只在服务器上发布所需的数据。现在,脑海中会出现以下几个问题:

1. AJAX post 调用是一种标准做法吗?

2. AJAX 调用安全吗?或者我们如何进行安全的 AJAX 调用?

3. 哪一个更好,HTTP Post(表单提交,一种传统方式)还是 AJAX POST(一种现代方式)?

背景

Post 回发是在 Web 应用程序上进行操作的传统方式,其中整个页面在表单提交时回发。在这种方法中,大部分代码在服务器端运行。AJAX 是一种构建 Web 应用程序的现代方式,其中大部分代码在客户端运行,以获得更好的性能和用户体验。在 AJAX 中,只有所需的数据发布到服务器,而不是发布整个页面。

Post 回发和 Ajax 都会创建 HTTP 请求,因此说一个不如另一个安全是不对的。在两种请求中,攻击者都可以使用 CSRF(跨站请求伪造)注入脚本。当 CORS 被禁用并且 JSONP 请求被阻止时,AJAX 调用本身使用“通用来源策略”来保护 CSRF。

为了进一步防止 CSRF 攻击,我们可以实现类似 MVC 框架的 Anti Forgery 令牌。AJAX 调用可以从 Web 应用程序和 MVC 中调用。

MVC 中,可以在表单加载时调用 @html.antiforgerytoken(),它将一个密钥存储在隐藏字段中,另一个密钥存储在 cookie 中,并使用 ValidateAntiForgeryToken 过滤器,我们可以验证 Anti Forgery 令牌。表单令牌对于 AJAX 请求来说可能是一个问题,因为 AJAX 请求可以发送 JSON 数据,而不是 HTML 表单数据。一种解决方案是在自定义 HTTP 标头中发送令牌。

使用代码

生成 Anti Forgery 令牌的示例服务器端代码。


/// <summary>
/// Get AntiForgery token
/// </summary>
/// <returns>one token into secure & HTTP only cookie & other hidden fields.</returns>
public static string GetAntiXsrfToken()
{
    string cookieToken, formToken;
    Antiery.GetTokens(null, out cookieToken, out formToken);
    var responseCookie = new HttpCookie("__AJAXAntiXsrfToken")
    {
        HttpOnly = true,
        Value = cookieToken
    };
    if(FormsAuthentication.RequireSSL && HttpContext.Current.Request.IsSecureConnecti     on)
    {
        responseCookie.Secure = true;
    }
    HttpContext.Current.Response.Cookies.Set(responseCookie);

    return formToken;
}

验证 Anti Forgery 令牌的示例服务器端代码。

/// <summary>
/// Validate Anti Forgery token coming from secure cookie & request header
/// </summary>
    static void ValidateAntiXsrfToken()
    {
         string tokenHeader, tokenCookie;
         try
         {
            // get header token                    
            tokenHeader = HttpContext.Current.Request.Headers.Get("__RequestVerificationToken");

                    // get cookie token
                    var requestCookie = HttpContext.Current.Request.Cookies["__AntiXsrfToken"];
                    tokenCookie = requestCookie.Value;

                    AntiForgery.Validate(tokenCookie, tokenHeader);
                }
                catch
                {
                    HttpContext.Current.Response.Clear();
                    HttpContext.Current.Response.StatusCode = 403;
                    HttpContext.Current.Response.End();
                }
            }
获取 Anti Forgery 令牌(一部分)并将其保存到隐藏字段中的示例代码。
<input name="__RequestVerificationToken" type="hidden" value="<%= CommonUtils.GetAntiXsrfToken() %>" />

将 Anti Forgery 令牌的一部分传递到请求标头中的示例客户端代码,另一部分将自动从客户端 cookie 中获取,如果请求来自同一来源。

function CallServer(baseUrl, methodName, MethodArgument, callback) {
    $.ajax({
        type: "POST",
        url: baseUrl + methodName,
        data: MethodArgument,
        contentType: "application/json; charset=utf-8",
        async: false,
        dataType: "json",
        headers: {'__RequestVerificationToken': $("input[name='__RequestVerificationToken']").val()
        },
        success: function (data) {
            if (callback != undefined && typeof (callback) === "function") {
                callback(data.d);
            }
        },
        error: function (data) {
            if (data.status == 401 || data.status == 403)
                window.location.href = "../Common/accessdenied";
            else if (data.status == 419) {
                displayUserMessage(commonMessage.RE_SESSIONINFO_NOT_FOUND, true);
                window.location.href = "../Common/logout";
            }
            else
                displayUserMessage(commonMessage.SERVICE_NOT_RESPONDING, true);
        }
    });
}

最后,在服务器端处理每个 AJAX 请求之前,调用 ValidateAntiXsrfToken() 函数。

参考

http://stackoverflow.com/questions/28405795/which-one-is-better-ajax-post-or-page-postcontroller-httppost-when-only-one-f http://www.asp.net/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attack

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet

http://stackoverflow.com/questions/39199129/is-ajax-post-an-acceptable-technique-for-changing-server-state/39205336#39205336

© . All rights reserved.