Leave a comment (4) 作者:adwin

最近打算搞一个论坛,选来选去还是选了DZ,但是由于种种因素,必须要做一层反向代理,本来没想太多,只是一层反向代理而已嘛,但是真正搞起来的时候,着实遇到了不少麻烦,要想逐一解决这些问题还真的需要下点功夫,在此我把我遇到的问题以及解决方案给大家分享出来,也算是给大家做一个参考吧,如果以后大家有类似的需求或者问题的话,可以来参考这篇文章。

另,本文较长,请确定有耐心看完。文章较长,写的时候避免不了脑袋混乱,若文中有错误之处还望各位小伙伴指出,以免对其他读者造成误导。

  Author:adwin
  From:Silic Security [blackbap.org]
  Contract me:
  QQ:562474299
  Email:root@okadwin.com
  Blog:http://www.okadwin.com

好了,那咱们现在开始正文吧。可能你会说,不就是反代吗,至于搞的那么牛逼的样子吗,一个7ghost不就全都搞定了,如果你有这个想法的话,你可以不要继续往下看,自己先去用7ghost搞一个反代自己试一下就知道怎么回事了。

我这里是用apache做的反向代理,至于真正的服务端用什么我觉得这个就不是很重要了吧。好吧,那么我们现在就从头说起吧,先说说apache怎么实现反向代理。

其实apache实现反向代理很简单,再简单不过了,只要找到httpd.conf,ctrl+f找到LoadModule proxy_module modules/mod_proxy.so和LoadModule proxy_http_module modules/mod_proxy_http.so这两行,把这两行前边的sharp注释去掉,然后就是在主机的地方添加一个代理的主机,如下:

<VirtualHost 本机IP>  
   ServerName www.okadwin.com
   ProxyRequests Off
   <Proxy *>
   Order deny,allow
   Allow from all
   </Proxy>
   ProxyPass / http://test.okadwin.com:9999/
   ProxyPassReverse / http://test.okadwin.com:9999/
</VirtualHost>

好了,现在你要做的就是ctrl+s然后重启apache,这样一个反向代理就已经ok了。

注:以上例子实现的是,用户访问www.okadwin.com,实际的WEB地址是http://test.okadwin.com:9999/

 

好了,现在我们在真正的web地址http://test.okadwin.com:9999/上搭建一个DZ论坛,步骤略,搭建好之后,这时我们访问一下http://www.okadwin.com会发现反向代理已经成功了,我们通过www.okadwin.com就可以访问test.okadwin.com:9999上的DZ论坛了。但是这个时候你会发现一个问题,那就是似乎DZ全站使用的都是绝对路径?页面上的每一个链接都是http://test.okadwin.com:9999/index.php?action=1234...这样的,所以我们点击页面上的任意一个链接就会跳回test.okadwin.com:9999上面去,这可怎么办呀。

别着急,右键查看源文件,咦?DZ用的是相对路径呀,比如下图:

点击查看原图

我们从所有的a标签或者img标签当中都可以看得出来,DZ明显使用的是相对路径嘛,所以按尿性的话不可能出现刚才我们的说的那种情况的啊,但是这是为什么呢?仔细看了看源代码,发现一个很坑的问题,因为我在head标签里面找到了base标签..发现如下的代码:

<base href="http://test.okadwin.com/" />

看见了没,这才是原因所在啊,就是他搞的鬼了(不了解<base>标签的请出门左转百度即可)。找到原因,那么处理起来就简单的多了(我一开始没找到问题出在哪,导致我解决问题走错了方向,纠结了一下午给apache编译新模块的问题...),那既然这个base标签是出现在head标签里边的,那么我们就把他注释掉或者干脆删掉就可以了。如果你现在用的模板目录里边有common/header_common.htm这个文件的话就编辑这个,如果现在的模板目录里没有这个文件的话就到默认的模板目录找到这个文件,ctrl+f找到【<base href="{$_G['siteurl']}" />】,注释掉或者干脆删掉这句,就可以了。

ctrl+s保存以后我以为就万事大吉了,现在似乎已经没有任何问题了,于是迫不及待的开始进行各种各样的测试,但是我发现似乎我高兴的有点太早了,好像来了一个更大的麻烦:几乎任何一个需要post传值的地方(登陆、发帖、回复、删帖、后台各项设置等)都无法提交了,提交的时候会提示【您的请求来路不正确或表单验证串不符,无法提交】,这特么的是啥节奏?按尿性这个问题完全不应该啊,完全没有理由的,对不对。

好吧,有问题就要想办法解决呀,再次仔细观察了一下页面源文件,发现还是有很多采用绝对路径的标签存在的,虽然一般不会体现在页面上,但比如head中调用js啊,css啊什么的,基本都还是绝对路径,包括页面上的设为首页也都是原来的地址,而不是我们代理后的。那这些绝对路径是从哪来的呢?我们来慢慢的寻找源头吧,当然首先就是找一下模板文件看一下是如何调用的,找到相应的模板发现,http://test.okadwin.com:9999这个地址是程序调用了$_G['siteurl']这个全局变量所产生的,那么是不是DZ在做表单验证的时候也是调用了这个$_G['siteurl']而导致的来路和提交的地址不符呢,目前来看,基本就是这个节奏了。OK,既然知道是从$_G['siteurl']这里来的,那么我们就完全可以把模板中的所有$_G['siteurl']全部都改成我们自己想要的地址,但是对于我这么一个严格要求自己的人来说(咳、此处应该有掌声)这么做是绝对不行的,我们要从根本上解决问题,也就是说要从一开始给$_G['siteurl']赋值的时候我们就要给他赋上我们想要的值,再说还有表单验证那里也许也是调用了$_G['siteurl']呢。我在整个DZ目录搜索了一下$_G['siteurl'],尼玛啊,出来的结果还真多啊。。。这该从哪下手呢,凭着感觉找到了几个看起来挺有好感的文件,这几个地方都曾有过给$_G['siteurl']赋值的操作(其实这个$_G['siteurl']在整个程序中对他的赋值语句多了去了...只是这几处比较明显),那我们就拿这几处看起来比较明显的赋值先试着修改一下试试吧,于是发现这三个文件(api\trade\notify_credit.php、notify_invite.php、notify_trade.php)中对$_G['siteurl']赋值时都采用了一样的语句,代码如下:

$_G['siteurl'] = dhtmlspecialchars('http://'.$_SERVER['HTTP_HOST'].preg_replace("/\/+(api\/trade)?\/*$/i", '', substr($PHP_SELF, 0, strrpos($PHP_SELF, '/'))).'/');
$notifydata = trade_notifycheck('credit');

这看起来好像没有任何问题啊,不就是获取$_SERVER['HTTP_HOST']嘛,看到这里我就有点想不太明白了,这不科学啊,如此赋值根本没有任何问题嘛,难道问题出现在$_SERVER['HTTP_HOST']上?反向代理的时候获取$_SERVER['HTTP_HOST']会获取到什么呢?于是一番百度之后终于知道真相的我眼泪流下来。

在php中,我们一般通过$_SERVER['HTTP_HOST']来获得URL中网站的域名或者ip地址,一般来说,这样子不会遇到什么问题,在一些常见的php框架中,如PFC3和FLEA也是基于该预定义变量,但是,此时我们做了一层反向代理,我们访问网站的时候实际上是客户端->反向代理服务器->WEB服务器,而$_SERVER['HTTP_HOST']并不能识别出是否使用了反向代理,所以$_SERVER['HTTP_HOST']获取到的是反向代理服务器访问WEB服务器这个过程中的host值,这个值当然肯定是9999端口的那个地址了,那么我们该怎么办才能让WEB服务端获取到客户端的进行访问时的host值呢?其实很简单,我们只要用$_SERVER['HTTP_X_FORWARDED_HOST']) 来获取就可以了,这个是由代理加入的,标记原始访问者的地址,可以通过他来取出通过代理服务器访问过来的用户的真实地址,但如果代理服务器没有提供此信息,则获取不到此地址。那这样就好办多了,我先在WEB服务器上写了一个php脚本来测试能否获取到$_SERVER['HTTP_X_FORWARDED_HOST']),事实证明,perfect!那既然解决问题的方向找到了,那就剩下实施了。

于是我找到上面说到的那三个文件,把赋值的$_SERVER['HTTP_HOST']改成:

$_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST']) : ($_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : "")

这样就保证了在能获取到$_SERVER['HTTP_X_FORWARDED_HOST']) 的时候优先使用$_SERVER['HTTP_X_FORWARDED_HOST']) 的值,当获取不到$_SERVER['HTTP_X_FORWARDED_HOST']) 的时候再使用$_SERVER['HTTP_HOST']的值,当然也可以直接把$_SERVER['HTTP_HOST']替换成我们想要的字符串,比如www.okadwin.com这样。

当然不出所料,我把上面那三个文件中相应的位置替换后还是没能解决问题,因为给$_G['siteurl']语句太多了,根本不能确定哪个是入口位置。好吧,那既然这样,也只能是从入口文件index.php来一点一点的跟踪了。

从入口跟踪了有十分钟不到,实在是看不下去了,于是在百度胡乱搜索了一番,还真找到了两篇关于DZ核心代码的一些分析,本来也没想会对这个问题有什么帮助,却饶有兴趣的看了一下,不过我却发现,我似乎找到我想要的东西了!下面贴一下DZ核心分析,不光是对于这个问题有很大帮助,以后如果遇到其他问题的话,这个东西也会有很大的帮助:

 <?php
/**
 *      [Discuz!] (C)2001-2099 Comsenz Inc.
 *      This is NOT a freeware, use is subject to license terms
 *
 *      $Id: class_core.php 21271 2011-03-22 02:44:58Z congyushuai $
 */
define('IN_DISCUZ', true);
error_reporting(0);
class discuz_core {
 var $db = null;
 var $mem = null;
 var $session = null;
 var $config = array();
 var $var = array();
 var $cachelist = array();
 var $init_setting = true;
 var $init_user = true;
 var $init_session = true;
 var $init_cron = true;
 var $init_misc = true;
 var $init_memory = true;
 var $init_mobile = true;
 var $initated = false;  //初始化工作未完成标志
 var $superglobal = array(
  'GLOBALS' => 1,
  '_GET' => 1,
  '_POST' => 1,
  '_REQUEST' => 1,
  '_COOKIE' => 1,
  '_SERVER' => 1,
  '_ENV' => 1,
  '_FILES' => 1,
 );
 function &instance() {
  static $object;
  if(empty($object)) {
   $object = new discuz_core();
  }
  return $object;
 }
 function discuz_core() {
  $this->_init_env();  //初始化环境变量
  $this->_init_config();  //初始化配置变量
  $this->_init_input();  //初始化输入
  $this->_init_output();  //初始化输出
 }
 function init() {
  if(!$this->initated) {
   $this->_init_db();  //初始化数据库
   $this->_init_memory();  //初始化memcache
   $this->_init_user();  //用户信息初始化
   $this->_init_session();  //session操作初始化
   $this->_init_setting();  //系统设置初始化
   $this->_init_mobile();   //手机功能初始化
   $this->_init_cron();   //计划任务初始化
   $this->_init_misc();   //其他功能初始化
  }
  $this->initated = true;   //设置完成标志
 }
 function _init_env() {  //环境变量初始化方法
  error_reporting(E_ERROR);  //定义错误报告等级
  if(phpversion() < '5.3.0') {
   set_magic_quotes_runtime(0);  //设置set_magic_quotes_runtime
  }
  define('DISCUZ_ROOT', substr(dirname(__FILE__), 0, -12));  //定义根目录常量
  define('MAGIC_QUOTES_GPC', function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc());  //定义MAGIC_QUOTES_GPC
  define('ICONV_ENABLE', function_exists('iconv'));  //定义转码函数常量,如果 iconv函数支持,则为TRUE
  define('MB_ENABLE', function_exists('mb_convert_encoding')); //转码函数是否支持
  define('EXT_OBGZIP', function_exists('ob_gzhandler'));  //缓存输出句柄函数
  define('TIMESTAMP', time());  //定义当前时间戳常量
  $this->timezone_set();
  if(!defined('DISCUZ_CORE_FUNCTION') && !@include(DISCUZ_ROOT.'./source/function/function_core.php')) {
   exit('function_core.php is missing');  //如果系统核心函数库文件未include,则include,如果核心函数库文件缺失,则退出,因为function_core.php是系统本身的函数库,必须加载
  }
  if(function_exists('ini_get')) {
   $memorylimit = @ini_get('memory_limit');
   if($memorylimit && return_bytes($memorylimit) < 33554432 && function_exists('ini_set')) {
    ini_set('memory_limit', '128m');
   }
  }
//以上6行代码的作用是设置内存使用限制,如果小于32M,则增加为128M,因为memory_limit小于32M,可能会不够用,毕竟X系统比较大,可能内存会使用的多点,如果不支持ini_set函数,就得去php.ini中修改了。
  define('IS_ROBOT', checkrobot());  //检测机器人
  foreach ($GLOBALS as $key => $value) {
   if (!isset($this->superglobal[$key])) {
    $GLOBALS[$key] = null; unset($GLOBALS[$key]);
   }
  }
//以上几行的意思是,注销所有的超级变量。
  global $_G;  //$_G大数组是Discuz中自定义的超级变量
  $_G = array(
   'uid' => 0,  //uid
   'username' => '', //用户名
   'adminid' => 0,  //adminid标识
   'groupid' => 1,  //用户组ID
   'sid' => '',  // sessionID
   'formhash' => '',  //表单验证认证
   'timestamp' => TIMESTAMP,  //时间戳
   'starttime' => dmicrotime(),  //开始时间
   'clientip' => $this->_get_client_ip(),  //客户端IP
   'referer' => '',  //referer地址
   'charset' => '',  //字符串编码
   'gzipcompress' => '',  //gzip
   'authkey' => '',  //authkey  认证码
   'timenow' => array(),  //当前时间
   'PHP_SELF' => '', //PHP_SELF
   'siteurl' => '', //网站地址
   'siteroot' => '',  //网站根目录
   'config' => array(),  //配置变量数组
   'setting' => array(),  //设置变量数组
   'member' => array(),  //用户信息数组
   'group' => array(),  //用户组数组
   'cookie' => array(),  //cookie数组
   'style' => array(),  //风格数组
   'cache' => array(),  //缓存列表数组
   'session' => array(), //session变量数组
   'lang' => array(),  //语言包数组
   'my_app' => array(),  //我的应用数组
   'my_userapp' => array(),  //用户应用数组
   'fid' => 0, 
   'tid' => 0,
   'forum' => array(),  //论坛板块数组
   'thread' => array(),  //论坛相关帖子数组
   'rssauth' => '',  //RSS订阅认证
   'home' => array(),  //home功能相关数组
   'space' => array(),  //space功能相关数组
   'block' => array(),  //块信息数组
   'article' => array(),
   'action' => array(
    'action' => APPTYPEID,
    'fid' => 0,
    'tid' => 0,
   ),
   'mobile' => '',  //手机信息
  );
  //以上定义了$_G超级变量的部分内容。都是系统以后要用到的。写在一个大数组中,方便使用
  $_G['PHP_SELF'] = htmlspecialchars($_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['PHP_SELF']);
  //当前脚本地址写入$_G超级变量中
  $_G['basescript'] = CURSCRIPT; //当前脚本
  $_G['basefilename'] = basename($_G['PHP_SELF']);  //当前脚本名称
  $_G['siteurl'] = htmlspecialchars('http://'.$_SERVER['HTTP_HOST'].preg_replace("/\/+(api)?\/*$/i", '', substr($_G['PHP_SELF'], 0, strrpos($_G['PHP_SELF'], '/'))).'/');//网站地址
  $_G['siteroot'] = substr($_G['PHP_SELF'], 0, -strlen($_G['basefilename']));//网站根地址
 
  if(defined('SUB_DIR')) {  //二级目录设置
   $_G['siteurl'] = str_replace(SUB_DIR, '/', $_G['siteurl']);
   $_G['siteroot'] = str_replace(SUB_DIR, '/', $_G['siteroot']);
  }
  $this->var = & $_G;
 }
 function _init_input() {   //输入初始化方法
  if (isset($_GET['GLOBALS']) ||isset($_POST['GLOBALS']) ||  isset($_COOKIE['GLOBALS']) || isset($_FILES['GLOBALS'])) {
   system_error('request_tainting');
  }
  if(!MAGIC_QUOTES_GPC) {
   $_GET = daddslashes($_GET);
   $_POST = daddslashes($_POST);
   $_COOKIE = daddslashes($_COOKIE);
   $_FILES = daddslashes($_FILES);
  }
//以上代码未GPC安全机制,进行转义
  $prelength = strlen($this->config['cookie']['cookiepre']);
  foreach($_COOKIE as $key => $val) {
   if(substr($key, 0, $prelength) == $this->config['cookie']['cookiepre']) {
    $this->var['cookie'][substr($key, $prelength)] = $val;
   }
  }
//以上代码意思是,如果cookie的键值等于定义的键值,那么截取cookiepre
  $_GET['diy'] = empty($_GET['diy']) ? '' : $_GET['diy'];
  if($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST)) {
   $_GET = array_merge($_GET, $_POST);
  }
//合并$_POST,$_GET
  foreach($_GET as $k => $v) {
   $this->var['gp_'.$k] = $v;
  }
//然后把$_POST和$_GET的值都赋予gp变量中,方便使用
  $this->var['mod'] = empty($this->var['gp_mod']) ? '' : htmlspecialchars($this->var['gp_mod']);
 //获得$mod变量 ?mod=xxx,则$this->var['mod']为xxx
  $this->var['inajax'] = empty($this->var['gp_inajax']) ? 0 : (empty($this->var['config']['output']['ajaxvalidate']) ? 1 : ($_SERVER['REQUEST_METHOD'] == 'GET' && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || $_SERVER['REQUEST_METHOD'] == 'POST' ? 1 : 0));
  //是否需要ajax方式
  $this->var['page'] = empty($this->var['gp_page']) ? 1 : max(1, intval($this->var['gp_page']));
  //页面获取,最小为1
  $this->var['sid'] = $this->var['cookie']['sid'] = isset($this->var['cookie']['sid']) ? htmlspecialchars($this->var['cookie']['sid']) : '';
 //sid获取
 }
 function _init_config() {  //获得配置文件
  $_config = array();
  @include DISCUZ_ROOT.'./config/config_global.php';//加载全局配置文件
  if(empty($_config)) {
   if(!file_exists(DISCUZ_ROOT.'./data/install.lock')) {
    header('location: install');
    exit;
   } else {
    system_error('config_notfound');
   }
  }
//以上代码为:如果config不存在,要么没安装,要么文件不存在,系统报错
  if(empty($_config['security']['authkey'])) {
   $_config['security']['authkey'] = md5($_config['cookie']['cookiepre'].$_config['db'][1]['dbname']);
  }
//设置安全验证的authkey
  if(empty($_config['debug']) || !file_exists(libfile('function/debug'))) {
   define('DISCUZ_DEBUG', false);
  } elseif($_config['debug'] === 1 || $_config['debug'] === 2 || !empty($_REQUEST['debug']) && $_REQUEST['debug'] === $_config['debug']) {
   define('DISCUZ_DEBUG', true);
   if($_config['debug'] == 2) {
    error_reporting(E_ALL);
   }
  } else {
   define('DISCUZ_DEBUG', false);
  }
//以上代码为是否调试模式,
  define('STATICURL', !empty($_config['output']['staticurl']) ? $_config['output']['staticurl'] : 'static/');
  //定义静态文件常量,如:css,img等,如果$_config['output']['staticurl']为空,则是默认的static目录,就是默认目录
  $this->var['staticurl'] = STATICURL;
 
  $this->config = & $_config;
  $this->var['config'] = & $_config;
  //引用$_config变量,改变$this->config,则$_config改变,保持统一
  if(substr($_config['cookie']['cookiepath'], 0, 1) != '/') {
   $this->var['config']['cookie']['cookiepath'] = '/'.$this->var['config']['cookie']['cookiepath'];
  }
  //设置cookie域,一般是设置目录域。/不存在则加上/,安全性更高
  $this->var['config']['cookie']['cookiepre'] = $this->var['config']['cookie']['cookiepre'].substr(md5($this->var['config']['cookie']['cookiepath'].'|'.$this->var['config']['cookie']['cookiedomain']), 0, 4).'_';
  //定义cookie前缀,如定义为xxx_;则为$cookie['xxx_uid]
  $this->var['authkey'] = md5($_config['security']['authkey'].$_SERVER['HTTP_USER_AGENT']);
 //得到authkey
 }
 function _init_output() {  //输出初始化方法
  if($this->config['security']['urlxssdefend'] && $_SERVER['REQUEST_METHOD'] == 'GET' && !empty($_SERVER['REQUEST_URI'])) {
   $this->_xss_check();
  }
  if($this->config['security']['attackevasive'] && (!defined('CURSCRIPT') || !in_array($this->var['mod'], array('seccode', 'secqaa', 'swfupload')))) {
   require_once libfile('misc/security', 'include');
  }
//验证码设置,加载include/misc/misc_security.php文件,验证功能;
  if(!empty($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false) {
   $this->config['output']['gzip'] = false;
  }
//是否开启gzip,如果不支持gzip,则定义为false
  $allowgzip = $this->config['output']['gzip'] && empty($this->var['inajax']) && $this->var['mod'] != 'attachment' && EXT_OBGZIP;
  setglobal('gzipcompress', $allowgzip);
  //把$allowgzip写入全局变量中
  ob_start($allowgzip ? 'ob_gzhandler' : null);
//定义输出缓存
  setglobal('charset', $this->config['output']['charset']);
  //把配置文件中的字符集赋予全局变量中
  define('CHARSET', $this->config['output']['charset']);
  if($this->config['output']['forceheader']) {
   @header('Content-Type: text/html; charset='.CHARSET);//设置网页编码,强制输出
  }
 }
 function reject_robot() {  //拒绝机器人访问,设置为403错误
  if(IS_ROBOT) {
   exit(header("HTTP/1.1 403 Forbidden"));
  }
 }
 function _xss_check() {  //检测xss漏洞,UBB
  $temp = strtoupper(urldecode(urldecode($_SERVER['REQUEST_URI'])));
  if(strpos($temp, '<') !== false || strpos($temp, '"') !== false || strpos($tmp,'CONTENT-TRANSFER-ENCODING')!==false) {
   system_error('request_tainting');
  }
  return true;
 }
 function _get_client_ip() {  //得到客户端IP
  $ip = $_SERVER['REMOTE_ADDR'];
  if (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
   $ip = $_SERVER['HTTP_CLIENT_IP'];
  } elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
   foreach ($matches[0] AS $xip) {
    if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
     $ip = $xip;
     break;
    }
   }
  }
  return $ip;
 }
 function _init_db() {  //初始化数据库
  $class = 'db_mysql';
  if(count(getglobal('config/db/slave'))) {  //是否存在从数据连接,存在则初始化
   require_once libfile('class/mysql_slave');
   $class = 'db_mysql_slave';
  }
  $this->db = & DB::object($class);
  $this->db->set_config($this->config['db']);
  $this->db->connect(); //建立数据库连接
 }
 function _init_session() { //session初始化方法
  $this->session = new discuz_session();   //new discuz_session类
  if($this->init_session)
  {
   $this->session->init($this->var['cookie']['sid'], $this->var['clientip'], $this->var['uid']);
   $this->var['sid'] = $this->session->sid;  //设置sid
   $this->var['session'] = $this->session->var;  //设置session
   if($this->var['sid'] != $this->var['cookie']['sid']) {
    dsetcookie('sid', $this->var['sid'], 86400);  //如果sid不为cookie中的sid,则重写sid到cookie
   }
   if($this->session->isnew) {
    if(ipbanned($this->var['clientip'])) {
     $this->session->set('groupid', 6);  //如果发现IP在禁止范围里,则设置该客户端用户组为6,则:禁止IP用户组
    }
   }
   if($this->session->get('groupid') == 6) {
    $this->var['member']['groupid'] = 6;
    sysmessage('user_banned');  //提示IP禁止
   }

   if($this->var['uid'] && ($this->session->isnew || ($this->session->get('lastactivity') + 600) < TIMESTAMP)) {
    $this->session->set('lastactivity', TIMESTAMP);
    //最近活动检测,600秒
    if($this->session->isnew) {
     DB::update('common_member_status', array('lastip' => $this->var['clientip'], 'lastvisit' => TIMESTAMP), "uid='".$this->var['uid']."'");
    } //如果用户在600秒里不活动,则设置最后访问时间点
   }
  }
 }
 function _init_user() {  //用户初始化方法
  if($this->init_user) {
   if($auth = getglobal('auth', 'cookie')) {   //得到auth,username\tuid的加密信息
    $auth = daddslashes(explode("\t", authcode($auth, 'DECODE')));  //进行解密
   }
   list($discuz_pw, $discuz_uid) = empty($auth) || count($auth) < 2 ? array('', '') : $auth;
   //得到用户名和用户密码,如果auth为空,或者缺失uid和username中的一个,则为空
   if($discuz_uid) {
    $user = getuserbyuid($discuz_uid);  //如果uid存在,则得到该用户信息
   }
   if(!empty($user) && $user['password'] == $discuz_pw) {
    $this->var['member'] = $user;  //如果用户存在,且密码正确,则用户信息写进全局变量中
   } else {
    $user = array();  //user定义为空数组
    $this->_init_guest();  //否则为游客,游客初始化方法
   }
   if($user && $user['groupexpiry'] > 0 && $user['groupexpiry'] < TIMESTAMP && getgpc('mod') != 'spacecp' && getgpc('do') != 'expiry' && CURSCRIPT != 'home') {
    dheader('location: home.php?mod=spacecp&ac=usergroup&do=expiry');
   }
    //用户组过期检测
   $this->cachelist[] = 'usergroup_'.$this->var['member']['groupid'];
   //用户组数据缓存
   if($user && $user['adminid'] > 0 && $user['groupid'] != $user['adminid']) {
    $this->cachelist[] = 'admingroup_'.$this->var['member']['adminid'];  //管理员用户组缓存
   }
  } else {
   $this->_init_guest(); //游客
  }
  if(empty($this->var['cookie']['lastvisit'])) {
   $this->var['member']['lastvisit'] = TIMESTAMP - 3600;
   dsetcookie('lastvisit', TIMESTAMP - 3600, 86400 * 30); //cookie中如果为记录最后访问时间,则写入
  } else {
   $this->var['member']['lastvisit'] = $this->var['cookie']['lastvisit'];//否则,写入全局变量
  }
  setglobal('uid', getglobal('uid', 'member')); 
  setglobal('username', addslashes(getglobal('username', 'member')));
  setglobal('adminid', getglobal('adminid', 'member'));
  setglobal('groupid', getglobal('groupid', 'member'));
  //以上四行是把用户的uid,用户名,管理组id,用户组写入全局变量中
 }
 function _init_guest() {  //游客初始化方法
  setglobal('member', array( 'uid' => 0, 'username' => '', 'adminid' => 0, 'groupid' => 7, 'credits' => 0, 'timeoffset' => 9999));
 }
 function _init_cron() {  //计划任务初始化
  if($this->init_cron && $this->init_setting) {
   if($this->var['cache']['cronnextrun'] <= TIMESTAMP) {
    require_once libfile('class/cron');   //加载source/class/class_cron.php文件
    discuz_cron::run();  //运行
   }
  }
 }
 function _init_misc() { 
  if(!$this->init_misc) {
   return false;
  }
  lang('core');  //加载core语言包
  if($this->init_setting && $this->init_user) {
   if(!isset($this->var['member']['timeoffset']) || $this->var['member']['timeoffset'] == 9999 || $this->var['member']['timeoffset'] === '') {
    $this->var['member']['timeoffset'] = $this->var['setting']['timeoffset'];
   }
  }
//设置用户时区
  $timeoffset = $this->init_setting ? $this->var['member']['timeoffset'] : $this->var['setting']['timeoffset'];
  $this->var['timenow'] = array(
   'time' => dgmdate(TIMESTAMP),
   'offset' => $timeoffset >= 0 ? ($timeoffset == 0 ? '' : '+'.$timeoffset) : $timeoffset
  );
  $this->timezone_set($timeoffset);
  $this->var['formhash'] = formhash();   //得到FORMHASH
  define('FORMHASH', $this->var['formhash']);  //定义为常量
  if($this->init_user) {
   if($this->var['group'] && isset($this->var['group']['allowvisit']) && !$this->var['group']['allowvisit']) {
    if($this->var['uid']) {
     sysmessage('user_banned', null);  //检测是否为禁止访问
    } elseif((!defined('ALLOWGUEST') || !ALLOWGUEST) && !in_array(CURSCRIPT, array('member', 'api')) && !$this->var['inajax']) {
     dheader('location: member.php?mod=logging&action=login&referer='.rawurlencode($_SERVER['REQUEST_URI']));
    }
   }
   if($this->var['member']['status'] == -1) {
    sysmessage('user_banned', null);  //如果用户状态为-1,则提示禁止访问
   }
  }
  if($this->var['setting']['ipaccess'] && !ipaccess($this->var['clientip'], $this->var['setting']['ipaccess'])) {
   sysmessage('user_banned', null);  //ip权限检测
  }
  if($this->var['setting']['bbclosed']) {
   if($this->var['uid'] && ($this->var['group']['allowvisit'] == 2 || $this->var['groupid'] == 1)) {
   } elseif(in_array(CURSCRIPT, array('admin', 'member', 'api')) || defined('ALLOWGUEST') && ALLOWGUEST) {
   } else {
    $closedreason = DB::result_first("SELECT svalue FROM ".DB::table('common_setting')." WHERE skey='closedreason'");
    $closedreason = str_replace(':', ':', $closedreason);
    showmessage($closedreason ? $closedreason : 'board_closed', NULL, array(), array('login' => 1));
   }
  }
//以上是论坛如果为关闭,只有管理员可以访问,其他则提示关闭原因
  if(CURSCRIPT != 'admin' && !(in_array($this->var['mod'], array('logging', 'seccode')))) {
   periodscheck('visitbanperiods');  //私密板块访问设置
  }
  if(defined('IN_MOBILE')) {
   $this->var['tpp'] = $this->var['setting']['mobile']['mobiletopicperpage'] ? intval($this->var['setting']['mobile']['mobiletopicperpage']) : 20;
   $this->var['ppp'] = $this->var['setting']['mobile']['mobilepostperpage'] ? intval($this->var['setting']['mobile']['mobilepostperpage']) : 5;
  } else {
   $this->var['tpp'] = $this->var['setting']['topicperpage'] ? intval($this->var['setting']['topicperpage']) : 20;
   $this->var['ppp'] = $this->var['setting']['postperpage'] ? intval($this->var['setting']['postperpage']) : 10;
  }
//wap访问设置
  if($this->var['setting']['nocacheheaders']) {
   @header("Expires: -1");
   @header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE);
   @header("Pragma: no-cache");
  }
//以上五行作用是header cache状态设置
  if($this->session->isnew && $this->var['uid']) {
   updatecreditbyaction('daylogin', $this->var['uid']);  //每日登陆增加积分设置
   include_once libfile('function/stat');
   updatestat('login', 1);
   if(defined('IN_MOBILE')) {
    updatestat('mobilelogin', 1); //MOBILE
   }
   if($this->var['setting']['connect']['allow'] && $this->var['member']['conisbind']) {
    updatestat('connectlogin', 1);
   }
  }
  if($this->var['member']['conisbind'] && $this->var['setting']['connect']['newbiespan'] !== '') {
   $this->var['setting']['newbiespan'] = $this->var['setting']['connect']['newbiespan'];
  }
  $lastact = TIMESTAMP."\t".htmlspecialchars(basename($this->var['PHP_SELF']))."\t".htmlspecialchars($this->var['mod']);
  dsetcookie('lastact', $lastact, 86400);
  setglobal('currenturl_encode', base64_encode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']));
//设置最后动作
  if($this->var['setting']['magicstatus'] && !$this->var['group']['allowmagics']) {
   $this->var['setting']['magicstatus'] = false;
   $this->var['setting']['magics'] = array();
   unset($this->var['setting']['spacenavs']['magic']);
  }
  if((!empty($this->var['gp_fromuid']) || !empty($this->var['gp_fromuser'])) && ($this->var['setting']['creditspolicy']['promotion_visit'] || $this->var['setting']['creditspolicy']['promotion_register'])) {
   require_once libfile('misc/promotion', 'include');
  }
  $this->var['seokeywords'] = !empty($this->var['setting']['seokeywords'][CURSCRIPT]) ? $this->var['setting']['seokeywords'][CURSCRIPT] : '';  //SEO keywords
  $this->var['seodescription'] = !empty($this->var['setting']['seodescription'][CURSCRIPT]) ? $this->var['setting']['seodescription'][CURSCRIPT] : '';  //SEO 网站描述
 }
 function _init_setting() {
  if($this->init_setting) {
   if(empty($this->var['setting'])) {
    $this->cachelist[] = 'setting';  //缓存设置文件
   }
   if(empty($this->var['style'])) {
    $this->cachelist[] = 'style_default';  //风格缓存设置
   }
   if(!isset($this->var['cache']['cronnextrun'])) {
    $this->cachelist[] = 'cronnextrun';  //缓存计划任务
   }
  }
  !empty($this->cachelist) && loadcache($this->cachelist);
  if(!is_array($this->var['setting'])) {
   $this->var['setting'] = array();
  }
  if($this->var['member'] && $this->var['group']['radminid'] == 0 && $this->var['member']['adminid'] > 0 && $this->var['member']['groupid'] != $this->var['member']['adminid'] && !empty($this->var['cache']['admingroup_'.$this->var['member']['adminid']])) {
   $this->var['group'] = array_merge($this->var['group'], $this->var['cache']['admingroup_'.$this->var['member']['adminid']]);
  }
 }
 function _init_style() {  //模板初始化方法
  $styleid = !empty($this->var['cookie']['styleid']) ? $this->var['cookie']['styleid'] : 0;
  if(intval(!empty($this->var['forum']['styleid']))) {
   $this->var['cache']['style_default']['styleid'] = $styleid = $this->var['forum']['styleid'];
  }
  if(intval(!empty($this->var['category']['styleid']))) {
   $this->var['cache']['style_default']['styleid'] = $styleid = $this->var['category']['styleid'];
  }
  if($styleid && $styleid != $this->var['setting']['styleid']) {
   loadcache('style_'.$styleid);
   if($this->var['cache']['style_'.$styleid]) {
    $this->var['style'] = $this->var['cache']['style_'.$styleid];
   }
  }
  if(is_array($this->var['style'])) {
   foreach ($this->var['style'] as $key => $val) {
    $key = strtoupper($key);
    if(!defined($key) && !is_array($val)) {
     define($key, $val);
    }
   }
  }
 }
 function _init_memory() {  //缓存设置
  $this->mem = new discuz_memory();
  if($this->init_memory) {
   $this->mem->init($this->config['memory']);
  }
  $this->var['memory'] = $this->mem->type;
 }
 function _init_mobile() {  //手机访问设置
  if(!$this->var['setting'] || !$this->init_mobile || !$this->var['setting']['mobile']['allowmobile'] || !is_array($this->var['setting']['mobile']) || IS_ROBOT) {
   $nomobile = true;  //允许手机访问
  }
  if($_GET['mobile'] === 'no') {
   dsetcookie('mobile', 'no', 3600);
   $nomobile = true;
  } elseif($this->var['cookie']['mobile'] == 'no' && $_GET['mobile'] === 'yes') {
   dsetcookie('mobile', '');
  } elseif($this->var['cookie']['mobile'] == 'no') {
   $nomobile = true;
  }
  if(!checkmobile()) {  //检测是否为手机访问
   $nomobile = true;
  }
  if((!$this->var['setting']['mobile']['mobileforward'] && $_GET['mobile'] !== 'yes') || $nomobile) {
   if($_SERVER['HTTP_HOST'] == $this->var['setting']['domain']['app']['mobile'] && $this->var['setting']['domain']['app']['default']) {
    dheader("Location:http://".$this->var['setting']['domain']['app']['default'].$_SERVER['REQUEST_URI']);
   } else {
    return;
   }
  }
  if(strpos($this->var['setting']['domain']['defaultindex'], CURSCRIPT) === false && CURSCRIPT != 'forum' && !$_GET['mod']) {
   if($this->var['setting']['domain']['app']['mobile']) {
    $mobileurl = 'http://'.$this->var['setting']['domain']['app']['mobile'];
   } else {
    if($this->var['setting']['domain']['app']['forum']) {
     $mobileurl = 'http://'.$this->var['setting']['domain']['app']['forum'].'?mobile=yes';
    } else {
     $mobileurl = $this->var['siteurl'].'forum.php?mobile=yes';
    }
   }
   dheader("location:$mobileurl");
  }
  define('IN_MOBILE', true);
  setglobal('gzipcompress', 0);
  $query_sting_tmp = preg_replace(array('/&simpletype=\w+/', '/simpletype=\w+/', '/&mobile=yes/', '/mobile=yes/'), array(), $_SERVER['QUERY_STRING']);
  $this->var['setting']['mobile']['nomobileurl'] = ($this->var['setting']['domain']['app']['forum'] ? 'http://'.$this->var['setting']['domain']['app']['forum'].'/' : $this->var['siteurl']).$this->var['basefilename'].($query_sting_tmp ? '?'.$query_sting_tmp.'&' : '?').'mobile=no';
  $this->var['setting']['lazyload'] = 0;
  if('utf-8' != CHARSET) {
   if(strtolower($_SERVER['REQUEST_METHOD']) === 'post') {
    foreach($_POST AS $pk => $pv) {
     if(!is_numeric($pv)) {
      $this->var['gp_'.$pk] = $_GET[$pk] = $_POST[$pk] = $this->mobile_iconv_recurrence($pv);
     }
    }
   }
  }
  if($_GET['simpletype']) {
   if($_GET['simpletype'] == 'yes') {
    $this->var['setting']['mobile']['mobilesimpletype'] = 1;
    dsetcookie('simpletype', 1, 86400);
   } else {
    $this->var['setting']['mobile']['mobilesimpletype'] = 0;
    dsetcookie('simpletype', 0, 86400);
   }
  } elseif($this->var['cookie']['simpletype']) {
   $this->var['setting']['mobile']['mobilesimpletype'] = $this->var['cookie']['simpletype'] == 1 ? 1 : 0 ;
  }
  if(!$this->var['setting']['mobile']['mobilesimpletype']) {
   $this->var['setting']['imagemaxwidth'] = 224;
  }
  $this->var['setting']['regstatus'] = $this->var['setting']['mobile']['mobileregister'] ? $this->var['setting']['regstatus'] : 0 ;
  if(!$this->var['setting']['mobile']['mobileseccode']) {
   $this->var['setting']['seccodestatus'] = 0;
  }
  $this->var['setting']['seccodedata']['type'] = 99;
  $this->var['setting']['thumbquality'] = 50;

  $this->var['setting']['mobile']['simpletypeurl'] = array();
  $this->var['setting']['mobile']['simpletypeurl'][0] = $this->var['siteurl'].$this->var['basefilename'].($query_sting_tmp ? '?'.$query_sting_tmp.'&' : '?').'simpletype=no&mobile=yes' ;
  $this->var['setting']['mobile']['simpletypeurl'][1] =  $this->var['siteurl'].$this->var['basefilename'].($query_sting_tmp ? '?'.$query_sting_tmp.'&' : '?').'simpletype=yes&mobile=yes';
  unset($query_sting_tmp);
  ob_start();
 }
 function timezone_set($timeoffset = 0) {  //时区设置
  if(function_exists('date_default_timezone_set')) {
   @date_default_timezone_set('Etc/GMT'.($timeoffset > 0 ? '-' : '+').(abs($timeoffset)));
  }
 }
 function mobile_iconv_recurrence($value) {  //手机访问再次转码
  if(is_array($value)) {
   foreach($value AS $key => $val) {
    $value[$key] = $this->mobile_iconv_recurrence($val);
   }
  } else {
   $value = daddslashes(diconv(stripslashes($value), 'utf-8', CHARSET));
  }
  return $value;
 }
}
class db_mysql   //数据库类
{
 var $tablepre;
 var $version = '';
 var $querynum = 0;
 var $slaveid = 0;
 var $curlink;
 var $link = array();
 var $config = array();
 var $sqldebug = array();
 var $map = array();
 function db_mysql($config = array()) {
  if(!empty($config)) {
   $this->set_config($config);
  }
 }
 function set_config($config) {
  $this->config = &$config;
  $this->tablepre = $config['1']['tablepre'];
  if(!empty($this->config['map'])) {
   $this->map = $this->config['map'];
  }
 }
 function connect($serverid = 1) {
  if(empty($this->config) || empty($this->config[$serverid])) {
   $this->halt('config_db_not_found');
  }
  $this->link[$serverid] = $this->_dbconnect(
   $this->config[$serverid]['dbhost'],
   $this->config[$serverid]['dbuser'],
   $this->config[$serverid]['dbpw'],
   $this->config[$serverid]['dbcharset'],
   $this->config[$serverid]['dbname'],
   $this->config[$serverid]['pconnect']
   );
  $this->curlink = $this->link[$serverid];
 }
 function _dbconnect($dbhost, $dbuser, $dbpw, $dbcharset, $dbname, $pconnect) {
  $link = null;
  $func = empty($pconnect) ? 'mysql_connect' : 'mysql_pconnect';
  if(!$link = @$func($dbhost, $dbuser, $dbpw, 1)) {
   $this->halt('notconnect');
  } else {
   $this->curlink = $link;
   if($this->version() > '4.1') {
    $dbcharset = $dbcharset ? $dbcharset : $this->config[1]['dbcharset'];
    $serverset = $dbcharset ? 'character_set_connection='.$dbcharset.', character_set_results='.$dbcharset.', character_set_client=binary' : '';
    $serverset .= $this->version() > '5.0.1' ? ((empty($serverset) ? '' : ',').'sql_mode=\'\'') : '';
    $serverset && mysql_query("SET $serverset", $link);
   }
   $dbname && @mysql_select_db($dbname, $link);
  }
  return $link;
 }
 function table_name($tablename) {
  if(!empty($this->map) && !empty($this->map[$tablename])) {
   $id = $this->map[$tablename];
   if(!$this->link[$id]) {
    $this->connect($id);
   }
   $this->curlink = $this->link[$id];
  } else {
   $this->curlink = $this->link[1];
  }
  return $this->tablepre.$tablename;
 }
 function select_db($dbname) {
  return mysql_select_db($dbname, $this->curlink);
 }
 function fetch_array($query, $result_type = MYSQL_ASSOC) {
  return mysql_fetch_array($query, $result_type);
 }
 function fetch_first($sql) {
  return $this->fetch_array($this->query($sql));
 }
 function result_first($sql) {
  return $this->result($this->query($sql), 0);
 }
 function query($sql, $type = '') {
  if(defined('DISCUZ_DEBUG') && DISCUZ_DEBUG) {
   $starttime = dmicrotime();
  }
  $func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?
  'mysql_unbuffered_query' : 'mysql_query';
  if(!($query = $func($sql, $this->curlink))) {
   if(in_array($this->errno(), array(2006, 2013)) && substr($type, 0, 5) != 'RETRY') {
    $this->connect();
    return $this->query($sql, 'RETRY'.$type);
   }
   if($type != 'SILENT' && substr($type, 5) != 'SILENT') {
    $this->halt('query_error', $sql);
   }
  }
  if(defined('DISCUZ_DEBUG') && DISCUZ_DEBUG) {
   $this->sqldebug[] = array($sql, number_format((dmicrotime() - $starttime), 6), debug_backtrace());
  }
  $this->querynum++;
  return $query;
 }
 function affected_rows() {
  return mysql_affected_rows($this->curlink);
 }
 function error() {
  return (($this->curlink) ? mysql_error($this->curlink) : mysql_error());
 }
 function errno() {
  return intval(($this->curlink) ? mysql_errno($this->curlink) : mysql_errno());
 }
 function result($query, $row = 0) {
  $query = @mysql_result($query, $row);
  return $query;
 }
 function num_rows($query) {
  $query = mysql_num_rows($query);
  return $query;
 }
 function num_fields($query) {
  return mysql_num_fields($query);
 }
 function free_result($query) {
  return mysql_free_result($query);
 }
 function insert_id() {
  return ($id = mysql_insert_id($this->curlink)) >= 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0);
 }
 function fetch_row($query) {
  $query = mysql_fetch_row($query);
  return $query;
 }
 function fetch_fields($query) {
  return mysql_fetch_field($query);
 }
 function version() {
  if(empty($this->version)) {
   $this->version = mysql_get_server_info($this->curlink);
  }
  return $this->version;
 }
 function close() {
  return mysql_close($this->curlink);
 }
 function halt($message = '', $sql = '') {
  require_once libfile('class/error');
  discuz_error::db_error($message, $sql);
 }
}
class DB   //数据库操作
class discuz_session   //session类
class discuz_memory   //缓存初始化 支持eaccelerator/xcach/memcache

?>

 

 

注意上面代码中加粗的部分,这里正是初始化全局变量$_G['siteurl']!好吧,既然找到了初始化的地方,那么我们就让他在初始化的地方就初始化成我们想要的值(如何改成我们想要的值就不说了吧,上边已经说过了。),代码位置在/source/class/discuz/discuz_application.php第188行左右。按尿性现在应该已经没有任何问题了,改完之后更新了一下缓存F5之,右键查看源文件,嘿,你别说,9999端口的地址还真就变成我们想要的了,head中引用的css啊,js啊啥的,都变成了我们想要的。ok,赶紧发个帖子试一下先。

现实总是很残酷,还是一样的错误,不仅如此,我在后台还发现验证码也不能够正确显示。我了个去,看来DZ在验证表单的时候并没有调用$_G['siteurl'] 这个全局变量,好吧,现在我们回到问题的源头上来。发帖的时候提示的是【您的请求来路不正确或表单验证串不符,无法提交】,那么也就是说有两种可能,第一是来路的问题,第二就是表单验证字符的问题。很显然第二种可能近乎为0,我百度查找了一下相关的资料,把可能造成第二种可能的几种问题都检查了一遍,最终确定不可能是表单验证字符不符,基本就是因为来路的问题了,那么DZ是如何获取来路的呢?我想一定是referer,显然我们不可能要求用户每次访问页面都伪造一个referer,所以还是要从验证上下手,那么此时问题就很明朗了,DZ一定是把referer和论坛的地址进行对比来进行验证的,而DZ获取论坛地址的方法也一定还是采用$_SERVER['HTTP_HOST']来进行获取的,这样就完全可以解释的通为什么不能通过表单验证了。好吧,既然已经找到原因了,那么我们就来找一下DZ进行表单验证的代码吧,走起!

既然基本可以肯定是使用了$_SERVER['HTTP_HOST'],那么就在DZ的项目目录里查找关键词$_SERVER['HTTP_HOST'],可是这尼玛出现的地方也太多了!如果挨个去改的话一定没有任何问题,但是...显然我不想这样做...不过从搜索到的结果来看,我似乎找到了应该如何解决验证码的问题:

解决验证码无法显示的问题:ource\module\misc\misc_seccode.php,查找第94行 

if($_G['setting']['seccodedata']['type'] < 2 && ($refererhost['host'] != $_SERVER['HTTP_HOST']) || $_G['setting']['seccodedata']['type'] == 2 && !extension_loaded('ming') && $_POST['fromFlash'] != 1 || $_G['setting']['seccodedata']['type'] == 3 && $_GET['fromFlash'] != 1) {

改成:if($_G['setting']['seccodedata']['type'] < 2 && ($refererhost['host'] != ($_SERVER['HTTP_X_FORWARDED_HOST'] ?

$_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST'])) || $_G['setting']['seccodedata']['type'] == 2 && !

extension_loaded('ming') && $_POST['fromFlash'] != 1 || $_G['setting']['seccodedata']['type'] == 3 && $_GET['fromFlash'] !=

1) {

 

ok,验证码的问题已经解决,但是这么多地方出现了$_SERVER['HTTP_HOST']),总不能挨个全都改掉(虽然全都改掉会完全杜绝日后可能由$_SERVER['HTTP_HOST']产生的其他问题,但是显然我并不很关心这些),所以现在我们只要把表单验证的问题解决就可以了,至于以后可能还会发现由$_SERVER['HTTP_HOST']产生的其他问题如何解决,就不用我说了吧(起码我在代码中发现有好几处$_SERVER['HTTP_HOST']的使用会对部分功能产生影响)。于是我想到了DZ给我们的提示文字,我想从DZ的提示文字入手,这样可以更快更精准的定位到相关代码,于是乎...

点击查看原图

在DZ的目录添加到项目,然后在整个项目中搜索提示文字,很快就找到了submit_invalid这个参数,好吧,现在我们再搜索一下看那个show了submit_invalid这个提示,那么相关的处理代码应该就在附近了。

点击查看原图

很显然ID是3的搜索记录就是上面那幅图中的语言配置。按一共有10条搜索记录,去除ID是6 7 8 9 10的插件目录文件和ID是3的语言配置文件,也就只剩下ID是1 2 4 5这四处了,经过查看ID是1的搜索结果附近的代码,发现丫跟表单验证一丁点关系都没有,于是看下面的/source/class/helper/helper_form.php这个文件,经过查看搜索结果附近的代码发现,没错,就是这货了!

既然找到出现问题的地方了,问题就变得简单多了。定位到21行,原来的代码是介样的:

 public static function submitcheck($var, $allowget = 0, $seccodecheck = 0, $secqaacheck = 0) {
  if(!getgpc($var)) {
   return FALSE;
  } else {
   global $_G;
   if($allowget || ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_GET['formhash']) && $_GET['formhash'] == formhash() && empty($_SERVER['HTTP_X_FLASH_VERSION']) && (empty($_SERVER['HTTP_REFERER']) ||
   preg_replace("/https?:\/\/([^\:\/]+).*/i", "\\1", $_SERVER['HTTP_REFERER']) == preg_replace("/([^\:]+).*/", "\\1", $_SERVER['HTTP_HOST'])))) {
    if(checkperm('seccode')) {
     if($secqaacheck && !check_secqaa($_GET['secanswer'], $_GET['secqaahash'])) {
      showmessage('submit_secqaa_invalid');
     }
     if($seccodecheck && !check_seccode($_GET['seccodeverify'], $_GET['seccodehash'], 0, $_GET['seccodemodid'])) {
      showmessage('submit_seccode_invalid');
     }
    }
    return TRUE;
   } else {
    showmessage('submit_invalid');
   }
  }
 }

 

经过我的改造,他的代码变成了介个样子:

 public static function submitcheck($var, $allowget = 0, $seccodecheck = 0, $secqaacheck = 0) {
  if(!getgpc($var)) {
   return FALSE;
  } else {
   global $_G;
$caonimadz = $_SERVER['HTTP_X_FORWARDED_HOST'] ? $_SERVER['HTTP_X_FORWARDED_HOST'] : ($_SERVER['HTTP_HOST'] ? $_SERVER

['HTTP_HOST'] : '');
   if($allowget || ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_GET['formhash']) && $_GET

['formhash'] == formhash() && empty($_SERVER['HTTP_X_FLASH_VERSION']) && (empty($_SERVER['HTTP_REFERER']) ||
   preg_replace("/https?:\/\/([^\:\/]+).*/i", "\\1", $_SERVER['HTTP_REFERER']) == preg_replace("/

([^\:]+).*/", "\\1", $caonimadz)))) {
    if(checkperm('seccode')) {
     if($secqaacheck && !check_secqaa($_GET['secanswer'], $_GET['secqaahash'])) {
      showmessage('submit_secqaa_invalid');
     }
     if($seccodecheck && !check_seccode($_GET['seccodeverify'], $_GET['seccodehash'], 0,

$_GET['seccodemodid'])) {
      showmessage('submit_seccode_invalid');
     }
    }
    return TRUE;
   } else {
    showmessage('submit_invalid');
   }
  }
 }

 

ctrl+s保存,F5刷新,发帖测试。

点击查看原图

大功告成!

+---------------------------------------------------------------------+

其实,我们可以搞一个专门反代用的DZ版本,要做的只是...只是查找所有的$_SERVER['HTTP_HOST'],然后将其替换成$_SERVER['HTTP_X_FORWARDED_HOST']即可,当然,如果你这样做的话,必须要保证你的程序可以获取的到$_SERVER['HTTP_X_FORWARDED_HOST']的值,一般是可以的。

如果想要搞一个既可以直接使用,又可以反代使用的DZ版本,需要做的只是...只是查找所有的$_SERVER['HTTP_HOST'],然后将其全部替换成$_SERVER['HTTP_X_FORWARDED_HOST'] ? $_SERVER['HTTP_X_FORWARDED_HOST'] : ($_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : ''),但是这里要注意的是,我没有仔细查看所有的$_SERVER['HTTP_HOST']出现时的上下文,所以可能有的地方并不适用于直接替换,还是替换的时候先看一眼是不是会对原语句产生影响,如果直接替换会对原语句造成影响的话,还是需要稍微变通一下的。

不过我个人认为我们根本就没有这么做的必要,我觉得DZ官方应该要考虑到这一点,但是显然官方不会搭理我。

另外,本文中使用的是DZ X3.1版本,于2013.10.06在官方下载,其他版本或许有不同。

+---------------------------------------------------------------------+

by adwin 

http://www.okadwin.com

分类:技术文档 | 标签: PHP Discuz! 反向代理
分享到:

评论列表:

新一
2013-10-11 18:58
动态页面做反向代理是会有这样的纠结。
emehost
2013-11-14 11:46
好深奥的内容,受教了
性感的屠夫
2014-05-24 08:51
我也遇到这个问题,好像x3.1没有问题啊,x2.5有问题。不知道能否在apache,或者设置nginx配置文件,以便解决问题。因为,我服务器没升级nginx,apache之前,是可以使用的。
adwin
2014-05-25 00:37
@性感的屠夫:我是2013年10月份在官方下载的X3.1版本,可能后续更新的版本中官方加入了我在文中说的解决方式,这个后来就没关注过,就不了解了。nginx不知道,但是知道Apache确实有一个模块可以实现的,不过需要自己编译。

我也说两句 »