Loading... # 前言 ## 问题描述 在部署禅道之后,ip访问正常,使用域名https访问会出现点击登录无响应的问题,检查网络请求无错误提示 ![问题描述](https://blog.leesong.top/usr/uploads/2024/07/1197471613.png) ## 环境 * 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攻击的方法 ```php /* 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防范的配置项 ```php $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防范代码: ```php /* 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 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏