前言

问题描述

在部署禅道之后,ip访问正常,使用域名https访问会出现点击登录无响应的问题,检查网络请求无错误提示

问题描述

环境

  • nginx 1.21
  • php 7.3

正文

问题排查

在禅道论坛搜索相关问题,有以下相关文章:

https://www.zentao.net/ask/36770.html

https://www.zentao.net/ask/36766.html

https://www.cnblogs.com/fuyuteng/p/15748081.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_FORHTTP_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
最后修改:2024 年 08 月 12 日
如果觉得我的文章对你有用,请随意赞赏