如无特别说明,以下题目均可用各种技术方案来解答。

Posted by & filed under 每日一题.

四脚猫每日一题(4月1日) :文件上传是我们常常需要开发的功能,试试用最安全的方式,判断用户上传的图片为正常的图片(JPG\GIF\PNG)。

解题思路:
1、检查提交的文件的扩展名是否是图片(这一步很容易伪造的,所以不可靠)
2、依据文件的头信息检查文件是否真的是图片 (这一步基本就是图片了,但是依然可能包含木马的脚本)
3、用正则检查文件里面是否包含木马的脚本

以下是“坚持到底”的PHP版本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php


//允许的图片类型
$imageType = array('jpg','gif','png');
//上传后存放的路径
$uploadfile = './data/' . basename($_FILES['userfile']['name']);
//获取文件的扩展名
$finfo = new SplFileInfo($_FILES['userfile']['name']);
$extName=$finfo->getExtension();

//第一道关卡,简单过滤非法文件。
if(!in_array($extName,$imageType)){
    exit('只能上传gif、png和jpg的图片');
}

//依据文件头信息检查图片的真实类型
//1 IMAGETYPE_GIF
//2 IMAGETYPE_JPEG
//3 IMAGETYPE_PNG

$realType=exif_imagetype($_FILES['userfile']['tmp_name']);
if( 1 == $realType || 2 ==$realType || 3 ==$realType){
    //开始检查是否存在木马代码
    $safe=is_safe($_FILES['userfile']['tmp_name']);
    if(!$safe){
        exit ('图片包含木马,禁止上传!');
    }
    if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
        echo "文件上传成功.\n";
    } else {
        echo "上传失败!\n";
    }

}else{
    exit ('只能上传gif、png和jpg的图片');
}



function is_safe($fileurl) {
    $handle = fopen($fileurl, 'rb');
    $fileSize = filesize($fileurl);
    fseek($handle, 0);
    if ($fileSize > 512) { // 取头和尾
        $hexCode = bin2hex(fread($handle, 512));
        fseek($handle, $fileSize - 512);
        $hexCode .= bin2hex(fread($handle, 512));
    } else { // 取全部
        $hexCode = bin2hex(fread($handle, $fileSize));
    }
    fclose($handle);
    /* 匹配16进制中的 <% ( ) %> */
    /* 匹配16进制中的 <? ( ) ?> */
    /* 匹配16进制中的 <script | /script> 大小写亦可*/
    //匹配表示有木马
    return !preg_match("/(3c25.*?28.*?29.*?253e)|(3c3f.*?28.*?29.*?3f3e)|(3C534352495054)|(2F5343524950543E)|(3C736372697074)|(2F7363726970743E)/is", $hexCode);
}

Posted by & filed under 每日一题.

四脚猫每日一题(3月31日) :有时候一个网页上的内容特别多,我们想要知道是否所有链接都是正常的可以访问的,人工方式去点击查看费时费力,这时候如果有个程序能去检测就非常好。

解题思路:
1、 首先是采用curl的方式采集到这个网页的内容
2、 用正则的方式或者html解析器把url分析出来
3、 对于每一个url,进行请求,如果状态不是2xx、3xx等就定义为异常。

swordphp的正则和curl结合版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?php
class http_stat{
    public $url;
    private $document;
    private $links;
    private $domain;
    private $links_stat;
    public function get_all_link_status($url){
        if($this->__get_document($url)!=false){
            $this->url = $url;
            $this->document = $this->__get_document($this->url);
            $this->links = $this->__strip_links($this->document);
            foreach($this->links as $val){
                if($val == '#'){
                    $res['empty'] +=1;
                }elseif(strpos($val,'http')!==false){
                    $state_num = $this->__get_http_status($val);
                    $res[$state_num][] = $val;
                }else{
                    $url = $this->url.$val;
                    $state_num = $this->__get_http_status($url);
                    $res[$state_num][] = $val;
                }
            }
            return $res;
        }
    }
    private function __get_http_status($s_url){
        $curl = curl_init();
        curl_setopt($curl,CURLOPT_URL,$s_url);
        curl_setopt($curl,CURLOPT_HEADER,1);
        curl_setopt($curl,CURLOPT_NOBODY,1);
        curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($curl,CURLOPT_TIMEOUT,30);
        curl_exec($curl);
        $rtn= curl_getinfo($curl,CURLINFO_HTTP_CODE);
        curl_close($curl);
        return  $rtn;
    }
    private function __get_document($url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $response = curl_exec($ch);
        if(curl_errno($ch))
        {
            print curl_error($ch);
            return false;
        }
        curl_close($ch);
        return $response;
    }
    private function __strip_links($document){
        preg_match_all('|<a(.*?)href="(.*?)"(.*?)>(.*?)</a>|i', $document, $links);
        while(list($key,$val) = each($links[2])){
            if(!empty($val))$match[] = $val;
        }
        return $match;
    }
}
$t = new http_stat();
$res = $t->get_all_link_status("http://www.sina.com.cn");
var_dump($res);

阳光的JS版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// ==UserScript==
// @name         baidunews_url_check
// @namespace    jQueryForChromeExample
// @include      http://shehui.news.baidu.com/*
// @author       sun
// @description  检查百度社会新闻页面的相关网址是否正常。
// ==/UserScript==


var result = {}
    , urls = []
       
function main($) {
   
    $('a').each(function() {
   
        var url = $(this).attr('href').trim()
        if ( url.substr(0, 1) !== '#') {
            urls.push(url)
        }
    })
   
    check()
}


// 目前是每次检查1个,串行
function check() {
    var len = urls.length
   
    if ( len ) {
       
       
        var url = urls.pop()
       
        $.ajax({
            url: url
            , cache: false
            , timeout: 5000
            , complete: function(xhr, status){
                if ( !result[status] ) {
                    result[status] = []
                }
               
                result[status].push(url)
                console.log([len, status, url])
                check()
            }
        })
    } else {
        // 完成
        console.log(result)
    }
}


/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license
//@ sourceMappingURL=jquery.min.map
*/

//此处引入jquery,省略...

if ( location.href.indexOf('&debug') > 0 ) {
    main(jQuery)
}

坚持到底的html解析器版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
//编码为GBK
include 'simple_html_dom.php';

$url='http://news.baidu.com';
$html=file_get_html($url);

stream_context_set_default(
    array(
    'http' => array(
        'method' => 'GET',
        'protocol_version' =>'1.1',
        'header'=>"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\nAccept-Encoding:gzip,deflate,sdch\nAccept-Language:zh-CN,zh;q=0.8,en;q=0.6\nConnection:keep-alive\nReferer:http://news.baidu.com/\nCookie:pgv_pvi=601060352; pgv_si=s8683061248;",
        'user_agent' => 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36',
        'timeout'=>5)
    )
);

foreach($html->find('a') as $a) {
    if(FALSE !== strpos($a->href,'http')){
        @$headers = get_headers($a->href);
        if(empty($headers)){
            echo $a->href ."  Timeout !  \n";
            continue;
        }
        if( FALSE === strpos($headers[0],'200')){
            echo $a->href ." ".$headers[0]."  \n";
        }
    }

}

Posted by & filed under 每日一题.

四脚猫每日一题(3月28日) :使用JS实现一个函数,这个方法可以接收数量不限的整型参数,此函数需要在这组数里面去掉一个最大值和一个最小值,然后返回剩下的所有数的平均值(整数)。

Posted by & filed under 每日一题.

四脚猫每日一题(3月27日) :SQL分页过多时(例如 limit 1000000, 20),效率会明显降低下,如何优化?

 

关于分页显示的高效率解决办法

SQLServer用的是类似:

1
SELECT top 10 * FROM USER ORDER BY uid ASC ;

MySQL用的是类似:

1
SELECT * FROM USER ORDER BY uid ASC LIMIT 0,10;

 

如果要显示第二页的数据,通常的做法是

1
SELECT * FROM USER ORDER BY uid ASC LIMIT 10,10;

 

这样的问题是,当数据量过多,越往后翻页速度越慢,那么有什么简单的解决办法吗?如下几种方式可参考:

 

一、如果是连续分页查询,可通过上次结果中的最大id,直接定位下一页的数据集合。

1、第一页:

1
SELECT * FROM USER ORDER BY uid ASC LIMIT 0,10;

2、找到最后一条记录的uid,$uid 。
3、第二页:

1
SELECT * FROM USER WHERE uid &gt;$uid ORDER BY uid ASC LIMIT 0,10;

这样的话,由于查询出来的结果集合变小了,所以各方面效率都高。

而且后面的limit条件基本不需要改变。

 

二、对于主见ID连续的表,可以先通过程序计算出所需分页位置的 ID 起始值,然后通过BETWEEN .. AND 方式提交查询。

 

三、使用子查询获取分页主键值,充分利用主键索引,如:

1
2
SELECT t.* FROM ( SELECT id FROM you_table ORDER BY id LIMIT 1000000, 20 ) s JOIN your_table t ON t.id = s.id;
SELECT * FROM your_table WHERE ID &gt;= ( SELECT id FROM your_table ORDER BY id ASC LIMIT 1000001,1) LIMIT 20;

四、采用NoSQL或单独的表作为索引表,或按数据新旧冷热等规则分表存储。

Posted by & filed under 每日一题.

【四脚猫】每日一题(3 月 26 日) :
cookie 是普遍采用的技术,如何加密 cookie 比较安全?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 通用加密解密函数,phpwind、phpcms、dedecms都用此函数 */

function strcode($string, $auth_key, $action='ENCODE') {
$key = substr(md5($_SERVER["HTTP_USER_AGENT"]. $auth_key), 8, 18);
$string = $action == 'ENCODE' ? $string : base64_decode($string);
$len = strlen($key);
$code = '';
for ($i = 0; $i &lt; strlen($string); $i++) {
$k = $i % $len;
$code .= $string[$i] ^ $key[$k];
}
$code = $action == 'DECODE' ? $code : base64_encode($code);
return $code;
}

Posted by & filed under 每日一题.

四脚猫每日一题(3月25日) :

页面的文字太长,有时不需要全部显示出来,实现一个字符串截取不乱码的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
function cutstr($string, $length, $dot = ' ...') {
        if (strlen($string) &lt;= $length) {
            return $string;
        }

        $pre = chr(1);
        $end = chr(1);
        $string = str_replace(array('&amp;amp;', '&amp;quot;', '&amp;lt;', '&amp;gt;'), array($pre . '&amp;' . $end, $pre . '"' . $end, $pre . '&lt;' . $end, $pre . '&gt;' . $end), $string);

        $strcut = '';
        if (strtolower(CHARSET) == 'utf-8') {

            $n = $tn = $noc = 0;
            while ($n &lt; strlen($string)) {

                $t = ord($string[$n]);
                if ($t == 9 || $t == 10 || (32 &lt;= $t &amp;&amp; $t &lt;= 126)) {
                    $tn = 1;
                    $n++;
                    $noc++;
                } elseif (194 &lt;= $t &amp;&amp; $t &lt;= 223) {
                    $tn = 2;
                    $n += 2;
                    $noc += 2;
                } elseif (224 &lt;= $t &amp;&amp; $t &lt;= 239) {
                    $tn = 3;
                    $n += 3;
                    $noc += 2;
                } elseif (240 &lt;= $t &amp;&amp; $t &lt;= 247) {
                    $tn = 4;
                    $n += 4;
                    $noc += 2;
                } elseif (248 &lt;= $t &amp;&amp; $t &lt;= 251) {
                    $tn = 5;
                    $n += 5;
                    $noc += 2;
                } elseif ($t == 252 || $t == 253) {
                    $tn = 6;
                    $n += 6;
                    $noc += 2;
                } else {
                    $n++;
                }

                if ($noc &gt;= $length) {
                    break;
                }
            }
            if ($noc &gt; $length) {
                $n -= $tn;
            }

            $strcut = substr($string, 0, $n);
        } else {
            for ($i = 0; $i &lt; $length; $i++) {
                $strcut .= ord($string[$i]) &gt; 127 ? $string[$i] . $string[++$i] : $string[$i];
            }
        }

        $strcut = str_replace(array($pre . '&amp;' . $end, $pre . '"' . $end, $pre . '&lt;' . $end, $pre . '&gt;' . $end), array('&amp;amp;', '&amp;quot;', '&amp;lt;', '&amp;gt;'), $strcut);

        $pos = strrpos($strcut, chr(1));
        if ($pos !== false) {
            $strcut = substr($strcut, 0, $pos);
        }
        return $strcut . $dot;
    }

Posted by & filed under 每日一题.

【四脚猫】每日一题(3月24日) :

Web程序,常常需要生成随机密码,试试看,实现一个随机字符串的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
&lt;?php

//Zjmainstay超强版本
//动态开启错误提示

ini_set('display_errors','on');
error_reporting(E_ALL);

//默认长度和类型
$length = '32';
$type = 'number-lower-upper';

if(!empty($_POST)) {
function getPassword($type = 'number-lower-upper', $customStr = '', $length = 32) {
$strArr = array(
'number' =&gt; '0123456789',
'lower' =&gt; 'abcdefghijklmnopqrstuvwxyz',
'upper' =&gt; 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'special' =&gt; '~!@#$%^&amp;*()_+{}|[]\-=:&lt;&gt;?/',
);

if($type == 'all') { //全部
$str = implode('', $strArr);
} else if($type == 'custom') { //自定义类型
if(empty($customStr)) { //未填写自定义类型,则默认为数字+大小写字母
$type = 'number-lower-upper';
} else {
$str = $customStr;
}
}

//custom 没带自定义类型 或 其他类型
if(empty($str)) {
$typeParts = explode('-', $type);
$str = '';
foreach($typeParts as $part) {
$str .= $strArr[$part];
}
}

$maxLength = 32;
$length = empty($length) ? $maxLength : min((int)$length, $maxLength);
if(empty($length)) {
$length = $maxLength;
}

$randStr = str_shuffle($str);
if(strlen($randStr) &lt; $length) { //随机字符长度不够密码长度,循环生成
$passwordStr = '';
do {
$passwordLength = strlen($passwordStr);
$passwordStr .= substr($randStr, 0, $length-$passwordLength); //如果
$passwordLength = strlen($passwordStr);
} while($passwordLength &lt; $length);

} else {
$passwordStr = substr($randStr, 0, $length);
}

return $passwordStr;
}

$password = getPassword($_POST['type'], $_POST['customType'], $_POST['length']);
$length = empty($_POST['length']) ? $length : $_POST['length'];
$type = empty($_POST['type']) ? $type : $_POST['type'];
}
?&gt;
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
&lt;head&gt;
&lt;title&gt;密码生成器&lt;/title&gt;
&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
&lt;meta http-equiv="Content-Language" content="zh-CN" /&gt;
&lt;script type="text/javascript" src="http://zjmainstay.cn/jquerylib/jquery-1.8.2.min.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;style type="text/css"&gt;
* {
margin: 5px;
}
&lt;/style&gt;
&lt;form action="&lt;?php echo $_SERVER['SCRIPT_NAME']?&gt;" method="POST"&gt;
&lt;div&gt;
&lt;label&gt;长度:&lt;/label&gt;
&lt;input type="text" name="length" value="&lt;?php echo $length ?&gt;" style="width: 100px;" placeholder="长度为0-32"/&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;label&gt;类型:&lt;/label&gt;
&lt;select name="type" style="width: 150px;" id="type"&gt;
&lt;option value="number"&gt;纯数字&lt;/option&gt;
&lt;option value="lower"&gt;小写字母&lt;/option&gt;
&lt;option value="upper"&gt;大写字母&lt;/option&gt;
&lt;option value="lower-upper"&gt;大小写字母&lt;/option&gt;
&lt;option value="number-lower"&gt;数字+小写字母&lt;/option&gt;
&lt;option value="number-upper"&gt;数字+大写字母&lt;/option&gt;
&lt;option value="number-lower-upper"&gt;数字+大小写字母&lt;/option&gt;
&lt;option value="special"&gt;特殊字符&lt;/option&gt;
&lt;option value="number-special"&gt;数字+特殊字符&lt;/option&gt;
&lt;option value="lower-special"&gt;小写字母+特殊字符&lt;/option&gt;
&lt;option value="upper-special"&gt;大写字母+特殊字符&lt;/option&gt;
&lt;option value="all"&gt;全部&lt;/option&gt;
&lt;option value="custom"&gt;自定义&lt;/option&gt;
&lt;/select&gt;
&lt;input name="customType" type="text" style="display:none;" size="50" value="&lt;?php echo @$_POST['customType'] ?&gt;"/&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;input type="submit" value="生成"/&gt;

&lt;?php if(!empty($password)) { ?&gt;
&lt;input type="text" id="password" readOnly="true" value="&lt;?php echo $password ?&gt;" size="50"/&gt;&lt;span class="tips"&gt;&lt;/span&gt;
&lt;?php } ?&gt;
&lt;/div&gt;
&lt;/form&gt;
&lt;script type="text/javascript"&gt;
$(document).ready(function(){
//初始化默认选择数字+大小写字母
$("#type").get(0).selectedIndex = $("#type option").index($("#type option[value=&lt;?php echo $type ?&gt;]"));
if($("#type").val() == 'custom') {
$("input[name=customType]").show();
}

$("#type").change(function() {
if($(this).val() == 'custom') {
$("input[name=customType]").show();
} else {
$("input[name=customType]").hide();
}
});
$("#password").click(function() {
$(this).select();
$(".tips").html("请使用 Ctrl + C 复制");
});
});
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

 

Posted by & filed under 每日一题.

【四脚猫】每日一题(3月21日) :找出Linux下指定目录中最大的10个文件,不限制方法。

可以用PHP,Shell,难道你用JS也行?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*爽歪歪PHP版本*/
<?php

$all_file = array();
function get_max_file($dir) {
    $list = scandir($dir);
    foreach ( $list as $file ) {
        $file_location = $dir . '/' . $file;
        if ( is_dir($file_location) && $file != "." && $file != ".." ) {
            get_max_file($file_location);
        } elseif ( $file_location ) {
            if ( $file != '.' && $file != '..' ) {
                $file_size = filesize($file_location);
                $GLOBALS['all_file'][$file_location] = $file_size;
            }
        }
    }
}
get_max_file('./');
arsort($all_file);
$max_size = array_slice($all_file, 0, 10);
var_dump($max_size);

# blacktree的shell版本

1
find . -type f -exec du -k {} \; | sort  -nrk 1 | head

利用find替du将文件过滤出来,无需使用du进行递归遍历,这个命令执行的结果不会包含目录。