【问题标题】:Restassured CSRF protection with X-XSRF-TOKEN (like angularjs) header使用 X-XSRF-TOKEN(如 angularjs)标头确保 CSRF 保护
【发布时间】:2015-09-13 08:05:47
【问题描述】:

我正在尝试编写一个带有放心的 Spring 安全身份验证的 Web 集成测试。该应用程序使用 AngularJS 和 Spring Boot。 由于我使用 AngularJS,CSRF 保护是通过 X-XSRF-TOKEN 标头和 XSRF-TOKEN cookie 完成的(据我所知,它是 angular 的默认值)。

如何配置 reassured 以生成并使用表单身份验证发送此令牌?现在我有这样的东西:

given().auth().form("user", "password", new FormAuthConfig("login", "username", "password").sendCsrfTokenAsHeader()).when().get("/index.html").then().log().all().statusCode(200);

但在日志中,我看到将凭据发布到 /login 时 CSRF 令牌无效。

【问题讨论】:

标签: angularjs spring-security csrf rest-assured


【解决方案1】:

在需要时使用 RestAssured 过滤器检索和注入令牌。

鉴于服务器对GET /login 的响应始终包含令牌cookie。那么保持实际测试代码干净的解决方案可能如下所示:

package com.example;

import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import io.restassured.RestAssured;
import io.restassured.authentication.FormAuthConfig;
import io.restassured.filter.*;
import io.restassured.response.Response;
import io.restassured.specification.*;

@ExtendWith(SpringExtension.class)
@TestInstance(PER_CLASS)
@SpringBootTest(webEnvironment = DEFINED_PORT)
public class CsrfSampleTest {

    @BeforeAll
    public void addCsrfCookieFilter() {
        RestAssured.filters(new Filter() {
            @Override
            public Response filter(FilterableRequestSpecification requestSpec,
                    FilterableResponseSpecification responseSpec, FilterContext ctx) {
                String csrfToken = requestSpec.getCookies().getValue("XSRF-TOKEN");
                if (csrfToken == null) {
                    csrfToken = RestAssured.given().noFilters().get("/login").cookie("XSRF-TOKEN");
                }
                requestSpec.replaceHeader("X-XSRF-TOKEN", csrfToken);
                return ctx.next(requestSpec, responseSpec);
            }
        });
    }

    @Test
    public void test() {
        RestAssured.given()
            .auth().form("user", "password", new FormAuthConfig("login", "username", "password"))
        .when()
            .get("/index.html")
        .then()
            .statusCode(200);
    }
}

【讨论】:

    【解决方案2】:

    您需要在发布前执行 2 GET 才能在您的休息客户端和测试类中使用 Spring Security CSRF 保护。

    1. 发出GET 请求登录。这将返回 JSESSIONID 令牌和 XSRF-TOKEN 令牌。如果你使用返回的XSRF-TOKEN 到POST 会失败,因为我们使用空/假JSESSIONID 得到它。
    2. 使用上一个请求中的JSESSIONID,从第二个GET 中获取有用的XSRF-TOKEN。
    3. 现在您可以将XSRF-TOKEN 用作您的POST。

    示例如何使用 CSRF 保护与X-XSRF-TOKEN 放心:

    //1) get sessionId
    Response response =
            given().auth().preemptive().basic(userName, userPassword).contentType(JSON).
            when().get(PREFIX_URL + "/users/user").
            then().log().all().extract().response();
    String jsessionidId =  response.getSessionId();//or response.cookie("JSESSIONID");
    
    //2) get XSRF-TOKEN using new/real sessionId
    response =
            given().
            sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
            contentType(JSON).
            when().get(PREFIX_URL + "/users/user").
            then().log().all().extract().response();
    
    //3) post data using XSRF-TOKEN
    given().log().all().
            sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
            header("X-XSRF-TOKEN", response.cookie("XSRF-TOKEN")).
            queryParam("pos",pos.getId()).
            queryParam("date",date).
            queryParam("group_id",itemGroup.getId()).
            body(orderItems).
            contentType(JSON).
    when().
            post(PREFIX_URL + "/orders/orderitems").
    then().
        log().all().assertThat().statusCode(200);
    

    【讨论】:

    • 这里它要求表单参数而不是基本自动。在您的示例中,您使用的是基本身份验证,但上述问题需要表单参数。
    【解决方案3】:

    回复有些延迟,但我希望它可以帮助某人

    Response loginResponse = given().contentType(APPLICATION_JSON).
        param(USERNAME, "").
        param(PASSWORD, "").
        when().post(LOGIN_PROCESSING_URL).then().log().all().
        extract().response();
    
    given().contentType(APPLICATION_JSON).
      cookie("XSRF-TOKEN", loginResponse.cookie("XSRF-TOKEN")).
      header("X-XSRF-TOKEN", loginResponse.cookie("XSRF-TOKEN")).
      sessionId(loginResponse.getSessionId()).
      when().post(USER_PATH).
      then().log().all().statusCode(CREATED.value());
    

    【讨论】:

      猜你喜欢
      • 2018-02-18
      • 2020-03-30
      • 2017-07-13
      • 2018-09-06
      • 1970-01-01
      • 2018-02-12
      • 2020-12-23
      相关资源
      最近更新 更多