为什么我的请求在没有请求新的XSRF令牌的情况下工作?

时间:2016-05-21 13:46:27

标签: java gwt csrf

是否足以从服务器请求一个 XSRF令牌并在整个会话中重复使用我是否应该请求保存,编辑或保存等每项保护措施首先删除 XSRF令牌然后然后执行实际请求?

出现这个问题是因为我不明白为什么我的XSRF保护请求正在运行,即使我没有请求新请求:

public void saveName(Long shopId, Long languageId, String name, OnSuccessCallback<String> success, OnFailureCallback failure) {

    Request.<String> doRequest(this.shopService,

            asyncCallback -> {
                this.shopService.saveName(shopId, languageId, name, asyncCallback);
            }, 

            (String result) -> {
                // ..
                success.onSuccess(result);
            }, failure);
}

此处Request#doRequest()只会执行请求,但不会首先要求新的XSRF令牌。我必须将其更改为XsrfRequest#doRequest(),它基本上会做同样的事情,但会首先请求 XSRF令牌

事情是saveName()应该受到保护:

@XsrfProtect
@RemoteServiceRelativePath("shop")
public interface ShopServlet extends RemoteService {
    // ..
    String saveName(Long shopId, Long languageId, String name);
}

请注意:saveName()被调用之前,还有其他一些请求,其中一些已经获得了XSRF令牌。但是因为我可以在不请求新名称的情况下保存新名称,所以我觉得以前请求的令牌在这里被重用了。这样可以吗?

我注意到的另一件事是我将@NoXsrfProtect添加到saveName()

@NoXsrfProtect
String saveName(Long restaurantId, Long languageId, String name);

请求仍将包含XSRF令牌信息:

7|2|9|http://localhost:8080/app/|424F33664CAA93E2F8A4A94C57DA5827|com.google.gwt.user.client.rpc.XsrfToken/4254043109|..ShopServlet|saveName|java.lang..

为什么令牌被发送到这里,即使该方法被声明为@NoXsrfProtect

有人可以向我澄清一下吗?我不想犯任何错误 - 特别是在与安全有关的事情上......

1 个答案:

答案 0 :(得分:0)

  

是否足以从服务器请求一个XSRF令牌并在整个会话中重复使用它,或者我是否应该首先请求保存,编辑或删除新的XSRF令牌等每个有保护的操作,然后执行实际请求?< / p>

让我们暂时忽略GWT RPC的内置XSRF保护并查看标题问题和这句话:什么是XSRF以及我们如何防范它?

什么是XSRF

XSRF代表跨站点请求伪造 - 这个想法是恶意站点可能以某种方式伪造请求,并强迫我们的一个用户正确地将其发送到我们的应用程序,就像他们打算自己做的那样。例如,如果将资金从一个银行帐户转移到另一个银行帐户所需的全部是

GET /transfer?from=me&to=attacker&amount=10000USD
然后,攻击者可以非常简单地从他们自己的站点向我们的服务器发出请求,作为图像,css或js文件:

<img src="https://securebank.com/transfer?from=me&to=attacker&amount=10000USD" />

撇开其他细节(“好吧,这适用于GET,他们是如何设法将POST发送到我的GWT RPC服务的?”),让我们看看XSRF“令牌”防止这种攻击的想法:什么是一个友好的客户知道或可以做,攻击者无法知道或做什么?

缓解

这个想法是客户端应该有一种方法向服务器指示它是可信的 - 它知道只有有效的客户端可以知道的东西,这表明用户愿意做出指定的动作。一种选择是要求用户执行验证码,使得攻击站点无法编写操作脚本,并且用户必须有意识地执行。另一种选择是将一些数据提供给真实/可信客户端,例如cookie(只能从加载在同一域中的页面读取),或者作为HTML页面加载时的一部分或其他一些请求(也许可以通过其他方式发送,但结果无法读取。)

OWASP将后一段数据称为"Synchronizer Token"

  

同步器令牌模式需要生成与用户当前会话关联的随机“质询”令牌。 [...]当用户希望调用这些敏感操作时,HTTP请求应包含此质询令牌。然后,服务器应用程序负责验证此令牌的存在性和正确性。

因此,在这种情况下,我们可以为cookie写一些值,以便只有客户端可以看到它,然后客户端可以使用它来生成令牌。然后,应该在每个必须验证的请求上将该令牌传递给服务器。但是从这个描述中,我们发现一次只有一个有效令牌并不一定很重要。

但是如果攻击者可以获得XSS,他们可以只读取令牌并再次强制执行请求!或者MitM!

这是真的,但如果他们有一个XSS,那么你自己的JS可以做出的任何请求,攻击也可以。你迷路了,打包店,回家的时间。同样,如果他们拥有用户和应用程序之间的连接,并可以随意读写。 XSRF保护不是解决所有问题的魔杖,它是一种特定的攻击,只需要自己解决:如果窗口可能被破坏,家中的锁定不会被视为错误。

好的,回到GWT

那么GWT如何做到这一点?如您所述,@XsrfProtect注释将服务类型标记为需要在服务器上进行检查。然后,客户端必须请求令牌,然后确保客户端的服务知道该令牌以用于将来的请求。

那么服务器如何生成令牌?服务器上的XsrfTokenServiceServlet RPC服务生成令牌,作为每次调用服务器的一部分,如您所见,XsrfProtectedServiceServlet.validateXsrfToken然后验证这是正确的。如果你想要自定义行为,你必须修改该系统的每一面,并且可以构建它以使每个令牌一旦使用就无效,但这不是默认的(也不是,根据OWASP,鼓励它。)

  

我注意到的另一件事是我将@NoXsrfProtect添加到saveName()......

请注意validateXsrfToken只在GWT的一个地方调用,来自AbstractXsrfProtectedServiceServlet

@Override
protected void onAfterRequestDeserialized(RPCRequest rpcRequest) {
  if (shouldValidateXsrfToken(rpcRequest.getMethod())) {
    validateXsrfToken(rpcRequest.getRpcToken(), rpcRequest.getMethod());
  }
}

方法shouldValidateXsrfToken然后检查方法是否明确禁用了保护。如果是这样,它将返回false,并且不会执行检查。但是在构建RPCRequest对象时,RPC.decodeRequest总是会附加令牌,即使该令牌恰好是null:

  RpcToken rpcToken = null;
  if (streamReader.hasFlags(AbstractSerializationStream.FLAG_RPC_TOKEN_INCLUDED)) {
    // Read the RPC token
    rpcToken = (RpcToken) streamReader.deserializeValue(RpcToken.class);
  }

因此,如果客户端配置为发送令牌,它将始终发送,但服务器可能会忽略它。

更多阅读: