【问题标题】:How to protect against CSRF on a static site?如何在静态站点上防止 CSRF?
【发布时间】:2017-12-02 04:33:06
【问题描述】:

我有一个静态网站,由 CDN 提供服务,通过 AJAX 与 API 通信。如何防范 CSRF?

由于我无法控制静态网站的服务方式,因此当有人加载我的静态网站时我无法生成 CSRF 令牌(并将令牌插入表单或与我的 AJAX 请求一起发送)。我可以创建一个GET 端点来检索令牌,但似乎攻击者可以简单地访问该端点并使用它提供的令牌?

有没有一种有效的方法可以用这个堆栈来防止 CSRF?


其他细节:这里的身份验证是完全独立的。我想要 CSRF 保护的一些 API 请求是经过身份验证的端点,还有一些是公共 POST 请求(但我想确认它们来自我的站点,而不是其他人的)

【问题讨论】:

  • “通过 AJAX 与 API 通信...不涉及服务器”。有一个服务器,用于 API。不是你自己的服务器/API吗?
  • 是的——我会在问题中澄清这一点。我假设 CDN 也有服务器?但我控制不了他们。我确实可以完全控制 API 服务器。

标签: ajax security csrf csrf-protection


【解决方案1】:

我可以创建一个 GET 端点来检索令牌,但似乎攻击者可以简单地访问该端点并使用它提供的令牌?

正确。但 CSRF 代币并不意味着保密。它们仅用于确认按一个用户期望的顺序执行操作(例如,表单 POST 仅在表单的 GET 请求之后)。即使在动态网站上,攻击者也可以向页面提交他们自己的 GET 请求并解析出嵌入在表单中的 CSRF 令牌。

来自OWASP

CSRF 是一种欺骗受害者提交恶意请求的攻击。它继承受害者的身份和特权,代表受害者执行不受欢迎的功能。

在页面加载时发出初始 GET 请求以获取新令牌然后在请求执行操作时提交它是完全有效的。

如果您想确认提出请求的人的身份,您需要进行身份验证,这与 CSRF 不同。

【讨论】:

  • 好点。这听起来像是一个 CSRF 令牌,即使在页面加载时嵌入到 HTML 页面中,也不能完全防止跨站点请求伪造,因为攻击者可以将其解析出来并在 POST 请求中使用它。提供令牌的专用GET 端点对于攻击者来说绝对更容易获取/使用,但它比根本不强制执行 CSRF 令牌提供更多的安全性。那正确吗?谢谢。
  • 是的,一个专门的 GET 请求至少会确认用户没有被诱骗发送 POST 请求。
  • 谢谢。这很有帮助。
  • 快速跟进 - 我很好奇为什么这篇文章说不要创建 GET 端点来获取令牌。你的逻辑对我来说很有意义;我不明白为什么他们说不要在这里做:github.com/pillarjs/…
  • 他们似乎将身份验证与 CSRF 混合在一起。他们假设每个用户都经过身份验证。但无论哪种方式,由于您的网站是静态的,因此您只需在页面加载后使用 AJAX 妥协以获取令牌。如果每个 AJAX 请求也都经过身份验证,则没有第 3 方可以获取令牌。由于您的一些 POST 是公开的,因此您必须允许对 CSRF 的公开 GET 请求。攻击者可以获取令牌,然后 POST 到您的 API,但如果 API 是公开的,您就无能为力了。
【解决方案2】:

我的解决方法如下

客户端 [静态 html]

<script>
// Call script to GET Token and add to the form
fetch('https:/mysite/csrf.php')
.then(resp => resp.json())
.then(resp => {
    if (resp.token) {
        const csrf = document.createElement('input');
        csrf.name = "csrf";
        csrf.type = "hidden";
        csrf.value = resp.token;
        document.forms[0].appendChild(csrf);
    }
});
</script>

可以修改以上内容以针对预先存在的 csrf 字段。我用它来添加到带有表单的页面。该脚本假定页面上的第一个表单是目标,因此如果需要也需要更改。

在服务器上生成 CSRF(使用 PHP:假设 > 7)

[CSRFTOKEN 在配置文件中定义。例子]

define('CSRFTOKEN','__csrftoken');

服务器:

$root_domain = $_SERVER['HTTP_HOST'] ?? false;
$referrer = $_SERVER['HTTP_REFERER'] ?? false;

// Check that script was called by page from same origin
// and generate token if valid. Save token in SESSION and
// return to client
$token = false;
if ($root_domain && 
    $referrer && 
    parse_url($referrer, PHP_URL_HOST) == $root_domain) {
  $token = bin2hex(random_bytes(16));
  $_SESSION[CSRFTOKEN] = $token;
}

header('Content-Type: application/json');
die(json_encode(['token' => $token]));

最后在处理表单的代码中

session_start();

// Included for clarity - this would typically be in a config
define('CSRFTOKEN', '__csrftoken');

$root_domain = $_SERVER['HTTP_HOST'] ?? false;
$referrer = parse_url($_SERVER['HTTP_REFERER'] ?? '', PHP_URL_HOST);

// Check submission was from same origin
if ($root_domain !== $referrer) {
    // Invalid attempt
    die();
}

// Extract and validate token
$token = $_POST[CSRFTOKEN] ?? false;
$sessionToken = $_SESSION[CSRFTOKEN] ?? false;
if (!empty($token) && $token === $sessionToken) {
  // Request is valid so process it
}

// Invalidate the token  
$_SESSION[CSRFTOKEN] = false;
unset($_SESSION[CSRFTOKEN]);

【讨论】:

    【解决方案3】:

    同样有很好的解释,请查看
    https://cloudunder.io/blog/csrf-token/

    据我了解,如果我们添加了 X-Requested-With 标志,由于 CORS 限制,静态站点似乎不会遇到 CSRF 的任何问题。
    我还想在这里强调一个问题,

    如何保护从移动应用程序和静态站点调用的 api?
    由于 api 是公开的,您要确保只有允许的用户才能调用它。
    我们可以在我们的 API 服务层添加一些检查以进行相同的检查

    1) 对于 AJAX 请求(来自静态站点)检查请求域,因此只有允许的站点才能访问它
    2) 对于移动请求使用 HMAC 令牌,请在此处阅读更多信息
    http://googleweblight.com/i?u=http://www.9bitstudios.com/2013/07/hmac-rest-api-security/&hl=en-IN

    【讨论】:

    • -1 CORS 限制不会保护 CSRF。 CORS 会阻止您读取请求的结果,但不会阻止您发出请求。这是不可能的,因为 CORS 是在响应标头中发送的,因此必须在浏览器读取 CORS 数据之前生成响应。因此,恶意站点可以对尽管 CORS 改变状态的端点进行 CSRF 攻击(例如bank.com/pay?amount=1000&amp;to=Alice)。他们无法读取结果,但可以通过其他方式观察状态变化——即 Alice 的账户中是否有 1000 美元。
    • @Jansky 使用自定义请求标头是 OWASP 推荐的方法之一。即使发出请求,服务器也会检查自定义请求标头。您只能使用 XHR 添加标头,而 CORS 会阻止对方使用自定义请求标头发出请求。
    • 我不确定我是否理解;你会在这个自定义请求标头中放什么?没有什么可以阻止恶意代理将特定的自定义标头添加到来自其域的客户端请求中,除非您使用从您的域生成的 Web 令牌。从您的域生成网络令牌,用户只有在通过 CORS 时才能读取它,他们使用令牌发出后续请求,然后您就知道他们是合法的,因为如果他们不在您的域上就无法获得有效的请求令牌由于 CORS。
    猜你喜欢
    • 1970-01-01
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-21
    • 2017-09-24
    • 1970-01-01
    • 2011-04-06
    相关资源
    最近更新 更多