使用AngularJS的Gorilla CSRF

时间:2015-11-12 18:06:53

标签: angularjs go gorilla

我试图让AngularJS与Gorilla CSRF一起使用我的网络应用程序,但是我找不到很多文档,所以我不确定从哪里开始。我应该为每个X-CSRF-Token请求设置GET,还是应该在用户访问主页时执行此操作,就像我现在正在做的那样?另外,如何使用Gorilla CSRF使AngularJS CSRF保护工作?我需要进行某种比较吗?任何示例代码将不胜感激。

这是我的代码:

package main

import (
    "github.com/gorilla/csrf"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()


    r.HandleFunc("/", Home).Methods("GET")
    // Other routes handling goes here

    http.ListenAndServe(":8000",
        csrf.Protect([]byte("32-byte-long-auth-key"))(r))
}

func Home(w http.ResponseWriter, r *http.Request) {

    w.Header().Set("X-CSRF-Token", csrf.Token(r))
}
// More routes

1 个答案:

答案 0 :(得分:5)

你的问题可能有点广泛,但总的来说你误用了这些工具,所以我只是试着解释基本的想法。您正在使用的应用程序使用“双提交”模式进行CSRF保护。这需要更改客户端和服务器代码库。服务器不应该设置X-CSRF-Token标头,即客户端的角色。我最近实际上实现了几个从头开始反CSRF解决方案,它们非常简单(都是双提交模式)。我还使用了一些来自MSTF和Apache等供应商的软件包(必须在各种堆栈上应用20年的CSRF)。

在双提交模式中,服务器应该设置一个具有随机值的cookie(如guid),cookie必须标记为安全。您也可以将其设为httponly,但是它需要您在前端资源上做更多的工作。在客户端,处理此问题的最简单方法是实现一些读取cookie值的JavaScript,并在任何POST请求之前将其添加为标头。您通常不需要保护GET。你可以,但如果你的GET正在服务器方面做建设性/破坏性的事情,那么你就是在滥用HTTP动词,我会通过提出这些请求来纠正这个问题,而不是试图保护每一个请求。

在服务器端,最好在所有请求进入的公共位置进行CSRF检查。当POST进来时,服务器应该读取cookie值,检查标头值并比较它们。如果它们相等则应该允许请求通过,如果它们不是那么你应该用403或者其他东西启动它们。执行此操作后,服务器应重写cookie值(最好只使用一次)。

您的客户端脚本可能具有类似下面的代码,只需确保每个页面加载资源,并且您不使用表单提交,这将涵盖所有内容。如果您提交表单,则需要一些其他代码才能处理。有些方法更喜欢在DOM服务器端编写值。例如在.NET中,CSRF库使值为HTTPOnly和Secure,并期望开发人员在其项目中的每个单独的cshtml文件中的每个表单中放置一个占位符令牌......我个人认为这是非常愚蠢和低效的。不管你怎么做,你可能要做一些定制工作。 Angular不会为gorillas CSRF库实现前端。大猩猩可能不会为您的客户端提供JavaScript,因为它是一个API库。无论如何,基本的JavaScript示例;

// three functions to enable CSRF protection in the client. Sets the nonce header with value from cookie
// prior to firing any HTTP POST.
function addXMLRequestCallback(callback) {
    var oldSend;
    if (!XMLHttpRequest.sendcallback) {
        XMLHttpRequest.sendcallback = callback;
        oldSend = XMLHttpRequest.prototype.send;

        // override the native send()
        XMLHttpRequest.prototype.send = function () {
            XMLHttpRequest.sendcallback(this);

            if (!Function.prototype.apply) {
                Function.prototype.apply = function (self, oArguments) {
                    if (!oArguments) {
                        oArguments = [];
                    }
                    self.__func = this;
                    self.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
                    delete self.__func;
                };
            }

            // call the native send()
            oldSend.apply(this, arguments);
        }
    }
}

addXMLRequestCallback(function (xhr) {
    xhr.setRequestHeader('X-CSRF-Token', getCookie('X-CSRF-Cookie'));
});

function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1);
        if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
    }
    return "";
}

现在,如果你可以稍微缩小你的问题,我可以提供一些更具体的指导,但这只是一个猜测(也许我会在我有一分钟时阅读他们的文档)。如果您使用csrf.Protect,Gorilla会自动设置您的Cookie并为您进行服务器端检查。你在Go中设置标题的代码,就是你需要上面的JavaScript。如果在服务器端设置标头,则根本没有提供安全性。这需要在浏览器中进行。如果您发送价值以及所有请求,Gorilla很可能会为您提供其余的服务。

关于问题空间的其他一些随机想法。根据经验,如果攻击者无法重播请求,他们可能无法对您进行CSRF。这就是为什么这种简单方法如此有效。每个传入的请求都只有一个随机的GUID值。您可以将该值存储在cookie中,这样您就不必担心会话跨服务器移动ect(如果您不使用双提交模式,则需要共享数据存储服务器端;此cookie-header值比较业务)。在目前的硬件限制下,这个值很难被强行逼迫。浏览器中的单一来源策略可防止攻击者读取您设置的cookie值(如果设置为安全,则只有来自您域的脚本才能访问它)。利用这种方法的唯一方法是,如果用户之前已被XSS利用,我的意思是,这种做法违背了进行CSRF的目的,因为攻击者已经拥有更多控制/能力来使用XSS进行恶意攻击。