前言
问题描述
在部署禅道之后,ip访问正常,使用域名https访问会出现点击登录无响应的问题,检查网络请求无错误提示
环境
- nginx 1.21
- php 7.3
正文
问题排查
在禅道论坛搜索相关问题,有以下相关文章:
https://www.zentao.net/ask/36770.html
其中的解决方法都提到了 proxy\_set\_header Host 的相关配置
及 zentao/framework/base/router.class.php 598-600行代码注释下
。
找到该代码文件,是一个关于防范CSRF攻击的方法
/* Change for CSRF. */
if($this->config->framework->filterCSRF)
{
# 获取http类型,$httpType为https或是http
$httpType = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and strtolower((string) $_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') $httpType = 'https';
if(isset($_SERVER['REQUEST_SCHEME']) and strtolower((string) $_SERVER['REQUEST_SCHEME']) == 'https') $httpType = 'https';
# 获取$httpHost,HTTP_HOST通常包含请求的主机名和端口号
$httpHost = zget($_SERVER, 'HTTP_HOST', '');
$apiMode = (defined('RUN_MODE') && RUN_MODE == 'api') || isset($_GET[$this->config->sessionVar]);
# 这是一个嵌套的条件判断语句。如果当前不是API模式,并且$httpHost为空,或者当前页面的HTTP referer(引用页)与$httpType://$httpHost不匹配,那么执行后面的代码块。清空$_FILES和$_POST数组
# 主要目的是让HTTP referer和$httpType://$httpHost匹配
if(!$apiMode && (empty($httpHost) or strncmp((string) $this->server->http_referer, "$httpType://$httpHost", strlen("$httpType://$httpHost")) !== 0)) $_FILES = $_POST = array();
}
为了验证是否是由于该方法引起的问题,我在 zentao/config/my.php
下添加关闭CSRF防范的配置项
$config->framework->filterCSRF = false;
关闭后在https下访问正常,已经成功定位问题就是由于CSRF防御机制引起的无法登录
解决方法
出于安全考虑我们并不能直接强行关闭CSRF防御,既然已经定位到问题,那么继续看一下为什么会导致https下的请求会被认为是CSRF攻击
在debug模式下,经过调试可以看到,不论是我通过https请求还是http请求,禅道的函数都会认为是http请求,但是 http_referer
中的是正确的https,这样会导致无法匹配,从而认为是伪造的请求
接下来,为了解决这个无法准确获取http类型的问题,考虑优化判断逻辑,考虑到反向代理的情况,使用请求头中的 HTTP_X_FORWARDED_FOR
和 HTTP_X_FORWARDED_HOST
中获取准确的http类型
优化后的CSRF防范代码:
/* Change for CSRF. */
if($this->config->framework->filterCSRF)
{
# 获取http状态的逻辑以适应反代服务器下的SSL域名请求
if((!empty( $_SERVER['HTTP_X_FORWARDED_HOST'])) || (!empty( $_SERVER['HTTP_X_FORWARDED_FOR'])) ) {
$_SERVER['HTTPS'] = 'on';
}
$httpType = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and strtolower((string) $_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') $httpType = 'https';
if(isset($_SERVER['REQUEST_SCHEME']) and strtolower((string) $_SERVER['REQUEST_SCHEME']) == 'https') $httpType = 'https';
$httpHost = zget($_SERVER, 'HTTP_HOST', '');
$apiMode = (defined('RUN_MODE') && RUN_MODE == 'api') || isset($_GET[$this->config->sessionVar]);
if(!$apiMode && (empty($httpHost) or strncmp((string) $this->server->http_referer, "$httpType://$httpHost", strlen("$httpType://$httpHost")) !== 0)) $_FILES = $_POST = array();
}
至此问题解决,通过http和https均可正常登录
参考资料
https://blog.csdn.net/qq_43378996/article/details/123910614