Gorilla CSRF与AngularJS

huangapple go评论90阅读模式
英文:

Gorilla CSRF with AngularJS

问题

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

这是我的代码:

package main

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

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

    r.HandleFunc("/", Home).Methods("GET")
    // 其他路由处理在这里

    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))
}
// 更多路由

请注意,这只是一个示例代码,你需要根据你的实际需求进行适当的修改。

英文:

I'm trying to get AngularJS to work with Gorilla CSRF for my web applciation, but there aren't many documentation around that I can find, so I'm not sure where exactly to start. Should I set a X-CSRF-Tokenfor every GET request or should I just do it when the user visits the home page like I'm doing now? Also, how do I make AngularJS CSRF protection work with Gorilla CSRF? Do I need to do some sort of comparisons? Any example codes would be appreciated.

Here is my code:

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

得分: 5

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

在双重提交模式中,服务器应该设置一个带有随机值(如GUID)的cookie,该cookie必须标记为安全。你也可以将其设置为httponly,但这将需要你在前端资源上做更多的工作。在客户端上,处理这个问题的最简单方法是实现一些JavaScript代码,读取cookie的值,并在任何POST请求之前将其添加为头部。通常不需要保护GET请求。你可以这样做,但如果你的GET请求在服务器端执行构造性/破坏性操作,那么你就误用了HTTP动词,我建议将这些请求改为POST请求,而不是试图保护每个请求。

在服务器端,最好在一个公共位置进行CSRF检查,处理所有请求。当收到POST请求时,服务器应该读取cookie的值,检查头部的值并进行比较。如果它们相等,那么请求应该被允许通过,如果它们不相等,那么你应该返回403或其他错误。在这样做之后,服务器应该重新生成cookie的值(最好是一次性的)。

你的客户端脚本可以像下面的代码一样,只需确保资源在每个页面加载时都存在,并且不使用表单提交,这样就可以覆盖所有情况。如果你提交表单,你将需要一些其他的代码来处理。有些方法喜欢在服务器端将值写入DOM。例如,在.NET中,CSRF库会将值设置为HTTPOnly和Secure,并期望开发人员在项目的每个cshtml文件中的每个表单中放置一个占位符令牌...我个人认为这非常愚蠢和低效。无论你如何做,你可能都需要做一些定制工作。Angular不会为gorilla的CSRF库实现前端,gorilla也不会为你的客户端提供JavaScript,因为它是一个API库。无论如何,这是一个基本的JavaScript示例:

// 用于在客户端启用CSRF保护的三个函数。在发送任何HTTP POST之前,将nonce头部设置为cookie的值。
function addXMLRequestCallback(callback) {
    var oldSend;
    if (!XMLHttpRequest.sendcallback) {
        XMLHttpRequest.sendcallback = callback;
        oldSend = XMLHttpRequest.prototype.send;

        // 覆盖原生的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;
                };
            }

            // 调用原生的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中,这样你就不必担心会话在服务器之间移动等问题(如果你不使用双重提交模式,这将需要一个共享数据存储服务器端)。在当前硬件限制下,几乎没有机会通过暴力破解这个值。浏览器的同源策略防止攻击者读取你设置的cookie值(只有来自你域名的脚本才能访问它,如果设置为安全)。唯一的利用方式是如果用户之前已经受到了XSS的攻击,这也就是说,做CSRF的目的有点失去了意义,因为攻击者已经有了更多的控制权/能力来进行恶意操作。

英文:

You're question might be a bit broad but overall you're misusing the tools so I'm just going try and explain the basic ideas. The application you're using uses a 'double submit' pattern for CSRF protection. This requires changes in both the client and server code bases. The server should not be setting the X-CSRF-Token header, that is the role of the client. I've actually implemented a couple from scratch anti-CSRF solutions recently and they're pretty simple (both double submit pattern). I also used a few packages from vendors like MSTF and Apache (had to implement CSRF across like 20 years of applications on all kinds of stacks).

In the double submit pattern the server should be setting a cookie with a random value (like a guid), the cookie must be marked as secure. You can make it httponly as well, however it will require you to do a lot more work on your front end resources. On the client side, the simplest way to deal with this is to implement some JavaScript that reads the cookie value and adds it as a header before any POST request. You don't need to protect GET's typically. You could, but if your GET's are doing constructive/destructive things server side, then you're misusing the HTTP verb and I would correct that by making those requests POSTS rather than trying to protect every single request.

On the server side, it's best to do the CSRF check up front, in a common place where all requests come in. When a POST comes in, the server should read the cookie value, check for the header value and compare them. If they're equal then the request should be allowed to pass through, if they're not then you should boot them out with a 403 or something. After doing so the server should rewrite the cookie value (best to make it one use only).

Your client side script can have something like the code below, just make sure the resource is on every page load and you don't use form submits and this will cover everything. If you submit forms you'll need some other code like this to handle that. Some approaches prefer to write the value in the DOM server side. For example in .NET the CSRF library makes the value HTTPOnly and Secure and expects the devs to put a place holder token in every single form in every single cshtml file in their project... I personally think that is very stupid and inefficient. No matter how you do this, you're probably gonna have to do some custom work. Angular isn't going to implement the front end for gorillas CSRF library. gorilla probably isn't going to come with JavaScript for your client since it's an API library. Anyway, basic JavaScript example;

// 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(&#39;X-CSRF-Token&#39;, getCookie(&#39;X-CSRF-Cookie&#39;));
});
function getCookie(cname) {
var name = cname + &quot;=&quot;;
var ca = document.cookie.split(&#39;;&#39;);
for (var i = 0; i &lt; ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == &#39; &#39;) c = c.substring(1);
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
}
return &quot;&quot;;
}

Now, if you can narrow your question a bit I can provide some more specific guidance but this is just a guess (maybe I'll read their docs when I have a minute). Gorilla is automatically going to set your cookie and do your server side check for you if you use csrf.Protect. The code you have setting the header in Go, that is what you need the JavaScript above for. If you set the header on the server side, you've provided no security at all. That needs to happen in the browser. If you send the value along with all your requests, Gorilla will most likely cover the rest for you.

Some other random thoughts about the problem space. As a rule of thumb, if an attacker can't replay a request, they probably can't CSRF you. This is why this simple method is so effective. Every incoming request has exactly one random GUID value it requires to pass through. You can store that value in the cookie so you don't have to worry about session moving across servers ect (that would require a shared data store server side if you're not using the double submit pattern; this cookie-header value compare business). There's no real chance of this value being brute forced with current hardware limitations. The single origin policy in browsers prevents attackers from reading the cookie value you set (only scripts from your domain will be able to access it if it's set as secure). The only way to exploit that is if the user has previously been exploited by XSS which I mean, kind of defeats the purpose of doing CSRF since the attacker would already have more control/ability to do malicious things with XSS.

huangapple
  • 本文由 发表于 2015年11月13日 02:06:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/33678311.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定