使用 Go/Python 使用 CSRF 令牌登录站点

Eri*_*eim 1 python authentication csrf go

我想自动备份需要登录的网站的网页内容。我尝试通过模拟 POST 请求来登录。但我收到错误:

csrf token: CSRF attack detected
Run Code Online (Sandbox Code Playgroud)

以下是我使用的代码的一些摘录:

func postLoginForm(csrfToken string) {
    values := make(url.Values)
    values.Set("signin[username]", "myusername") 
    values.Set("signin[password]", "mypassword") 

    values.Set("signin[_csrf_token]", csrfToken)
    resp, err := http.PostForm("https://spwebservicebm.reaktor.no/admin/nb", values)

    dumpHTTPResponse(resp) // show response to STDOUT
}
Run Code Online (Sandbox Code Playgroud)

我通过获取登录页面并扫描名为 的隐藏输入字段来获取 csrf 令牌signin[_csrf_token]。执行此操作的代码的重要部分如下:

// Finds input field named signin[_csrf_token] and returns value as csrfToken
func handleNode(n *html.Node) (csrfToken string, found bool) {
    if n.Type == html.ElementNode && n.Data == "input" {
        m := make(map[string]string)
        for _, attr := range n.Attr {
            m[attr.Key] = attr.Val
        }
        if m["name"] == "signin[_csrf_token]" {
            return  m["value"], true
        }
    }

    for c := n.FirstChild; c != nil; c = c.NextSibling {
         if csrfToken, found = handleNode(c); found {
             return 
         }       
    }

    return "", false
}
Run Code Online (Sandbox Code Playgroud)

我不需要使用 Go,那只是因为我最熟悉它。使用 python 也可能是一个解决方案,但我对此没有任何运气。

Eri*_*eim 5

问题是 Go 1.2 不会自动为其 HTTP 请求使用 cookie jar。第一个请求是从登录页面获取 CSRF 令牌。第二个请求是使用该 CSRF 令牌发布登录信息。但由于第二次请求的 HTTP 标头中没有附加任何会话 cookie,服务器不知道它是尝试登录的同一个程序。因此,服务器认为这是一次 CSRF 尝试(您从其他地方选择了 CSRF 令牌并尝试重用它)。

因此,为了获取登录页面并提取 CSRF 令牌,我们首先创建自己的客户端对象。否则我们就没有地方可以放置饼干罐了。http.PostForm确实允许访问 cookie jar:

client = &http.Client{}
Run Code Online (Sandbox Code Playgroud)

创建一个 cookie Jar,如来自 golang 的经过身份验证的 http 客户端请求中所述。这比官方的更容易设置和调试: http: //golang.org/pkg/net/http/cookiejar/ Cookie Jar

jar := &myjar{}
jar.jar = make(map[string] []*http.Cookie)
client.Jar = jar    

resp, err := client.Get("https://spwebservicebm.reaktor.no/admin")      
doc, err := html.Parse(resp.Body)
Run Code Online (Sandbox Code Playgroud)

然后,为了登录,我们重用带有附加 cookie jar 的客户端对象:

values := make(url.Values)
values.Set("signin[username]", "myusername")
values.Set("signin[password]", "mypassword")                                       
values.Set("signin[_csrf_token]", csrfToken)

resp, err := client.PostForm("https://spwebservicebm.reaktor.no/admin/login", values)
Run Code Online (Sandbox Code Playgroud)

您会注意到,代码几乎与问题中的代码相同,只是我们使用client.PostForm代替http.PostForm

感谢 dommage 和对来自 golang 的经过身份验证的 http 客户端请求的答复,让我走上了正轨。