如何在静态站点上防止 CSRF?
How to protect against CSRF on a static site?
我有一个静态网站,由 CDN 提供服务,通过 AJAX 与 API 通信。我如何防范 CSRF?
由于我无法控制静态网站的服务方式,因此当有人加载我的静态网站时我无法生成 CSRF 令牌(并将令牌插入表单或与我的 AJAX 请求一起发送) .我可以创建一个 GET
端点来检索令牌,但攻击者似乎可以简单地访问该端点并使用它提供的令牌?
是否有有效的方法来防止此堆栈的 CSRF?
其他详细信息:身份验证在这里是完全独立的。我想要 CSRF 保护的一些 API 请求是经过身份验证的端点,有些是 public POST 请求(但我想确认它们来自我的站点,而不是其他人的)
I could create a GET endpoint to retrieve the token, but it seems like an attacker could simply access that endpoint and use the token it provides?
正确。但 CSRF 令牌并不意味着是秘密的。它们的存在只是为了确认一个操作是按照一个用户期望的顺序执行的(例如,表单 POST 仅在表单的 GET 请求之后)。即使在动态网站上,攻击者也可以向页面提交他们自己的 GET 请求并解析出嵌入表单中的 CSRF 令牌。
来自OWASP:
CSRF is an attack that tricks the victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on the victim's behalf.
在页面加载时发出初始 GET 请求以获取新令牌然后将其与执行操作的请求一起提交是完全有效的。
如果您想确认发出请求的人的身份,您需要进行身份验证,这与 CSRF 不同。
同样有很好的解释,请查看
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
我的解决方法如下
客户端[静态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]);
我有一个静态网站,由 CDN 提供服务,通过 AJAX 与 API 通信。我如何防范 CSRF?
由于我无法控制静态网站的服务方式,因此当有人加载我的静态网站时我无法生成 CSRF 令牌(并将令牌插入表单或与我的 AJAX 请求一起发送) .我可以创建一个 GET
端点来检索令牌,但攻击者似乎可以简单地访问该端点并使用它提供的令牌?
是否有有效的方法来防止此堆栈的 CSRF?
其他详细信息:身份验证在这里是完全独立的。我想要 CSRF 保护的一些 API 请求是经过身份验证的端点,有些是 public POST 请求(但我想确认它们来自我的站点,而不是其他人的)
I could create a GET endpoint to retrieve the token, but it seems like an attacker could simply access that endpoint and use the token it provides?
正确。但 CSRF 令牌并不意味着是秘密的。它们的存在只是为了确认一个操作是按照一个用户期望的顺序执行的(例如,表单 POST 仅在表单的 GET 请求之后)。即使在动态网站上,攻击者也可以向页面提交他们自己的 GET 请求并解析出嵌入表单中的 CSRF 令牌。
来自OWASP:
CSRF is an attack that tricks the victim into submitting a malicious request. It inherits the identity and privileges of the victim to perform an undesired function on the victim's behalf.
在页面加载时发出初始 GET 请求以获取新令牌然后将其与执行操作的请求一起提交是完全有效的。
如果您想确认发出请求的人的身份,您需要进行身份验证,这与 CSRF 不同。
同样有很好的解释,请查看
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
我的解决方法如下
客户端[静态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]);