若问生涯原是梦,除梦里,没人知。

  • 2016-03-04

    ##Discuz authcode

    <?php
        /** 
         * $string 明文或密文
         * $operation 加密ENCODE或解密DECODE
         * $key 密钥
         * $expiry 密钥有效期
         */
        function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
            // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
            // 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
            // 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
            // 当此值为 0 时,则不产生随机密钥
            $ckey_length = 4;
            // 密匙
            // $GLOBALS['discuz_auth_key'] 这里可以根据自己的需要修改
            $key = md5($key ? $key : $GLOBALS['discuz_auth_key']);
            // 密匙a会参与加解密
            $keya = md5(substr($key, 0, 16));
            // 密匙b会用来做数据完整性验证
            $keyb = md5(substr($key, 16, 16));
            // 密匙c用于变化生成的密文
            $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()) , -$ckey_length)) : '';
            // 参与运算的密匙
            $cryptkey = $keya . md5($keya . $keyc);
            $key_length = strlen($cryptkey);
            // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
            // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
            $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb) , 0, 16) . $string;
            $string_length = strlen($string);
            $result = '';
            $box = range(0, 255);
            $rndkey = array();
            // 产生密匙簿
            for ($i = 0; $i <= 255; $i++) {
                $rndkey[$i] = ord($cryptkey[$i % $key_length]);
            }
            // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上并不会增加密文的强度
            for ($j = $i = 0; $i < 256; $i++) {
                $j = ($j + $box[$i] + $rndkey[$i]) % 256;
                $tmp = $box[$i];
                $box[$i] = $box[$j];
                $box[$j] = $tmp;
            }
            // 核心加解密部分
            for ($a = $j = $i = 0; $i < $string_length; $i++) {
                $a = ($a + 1) % 256;
                $j = ($j + $box[$a]) % 256;
                $tmp = $box[$a];
                $box[$a] = $box[$j];
                $box[$j] = $tmp;
                // 从密匙簿得出密匙进行异或,再转成字符
                $result.= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
            }
            if ($operation == 'DECODE') {
                // substr($result, 0, 10) == 0 验证数据有效性
                // substr($result, 0, 10) - time() > 0 验证数据有效性
                // substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
                // 验证数据有效性,请看未加密明文的格式
                if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb) , 0, 16)) {
                    return substr($result, 26);
                } else {
                    return '';
                }
            } else {
                // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
                // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
                return $keyc . str_replace('=', '', base64_encode($result));
            }
        }
        $a = "www.test.com";
        $b = authcode($a, "ENCODE", "abc123");
        echo $b . "<br/>";
        echo authcode($b, "DECODE", "abc123");
    ?>
    

    >>More

    #PHP #encrypt
  • 2016-03-02

    在学习React的过程中看到 dangerouslySetInnerHTML 这样奇怪的prop命名。

    React特意设计这样的,以此提醒开发者,它的prop值应该是净化后的安全数据。

    官网给出的原因是:

    不合时宜的使用 innerHTML 可能会导致 cross-site scripting (XSS) 攻击。 净化用户的输入来显示的时候,经常会出现错误,不合适的净化也是导致网页攻击 的原因之一。

    在彻底的理解安全问题后果并正确地净化数据之后,生成只包含唯一 key __html 的对象,并且对象的值是净化后的数据。

    使用dangerouslySetInnerHTML:

    function createMarkup() { return {__html: 'First &middot; Second'}; };
    <div dangerouslySetInnerHTML={createMarkup()} />
    

    这么做的意义在于,当你不是有意地使用 <div dangerouslySetInnerHTML={getUsername()} />时候,它并不会被渲染,因为 getUsername() 返回的格式是 字符串 而不是一个 {__html: ''} 对象。

    {__html:...} 背后的目的是表明它会被当成 "type/taint" 类型处理。 这种包裹对象,可以通过方法调用返回净化后的数据,随后这种标记过的数据可以被传递给 dangerouslySetInnerHTML。 基于这种原因,我们不推荐写这种形式的代码:<div dangerouslySetInnerHTML= />.

    这个功能主要被用来与 DOM 字符串操作类库一起使用,所以提供的 HTML 必须要格式清晰(例如:传递 XML 校验 )

    #React
  • 2016-03-01

    通过微信分享的URL链接都将通过微信内置的浏览器打开,那么如何区别判断是否是微信内置浏览器呢,很简单。

    微信Android UA

    mozilla/5.0 (linux; u; android 4.1.2; zh-cn; mi-one plus build/jzo54k) applewebkit/534.30 (khtml, like gecko) version/4.0 mobile safari/534.30 micromessenger/5.0.1.352
    

    微信iPhone UA

    mozilla/5.0 (iphone; cpu iphone os 5_1_1 like mac os x) applewebkit/534.46 (khtml, like gecko) mobile/9b206 micromessenger/5.0
    

    微信的 User Agent 都有micromessenger字符串标示,通过判断是否含有这个字符串就OK了。

    JS判断是否是微信内置的浏览器

    function isWeixinBrowser(){
        var ua = navigator.userAgent.toLowerCase();
        return (/micromessenger/.test(ua)) ? true : false ;
    }
    
    #微信 #UserAgent
  • 2016-02-29

    ##安装composer

    cd /usr/local/bin
    curl -sS https://getcomposer.org/installer | php
    

    执行发现失败了,提示如下:

    Some settings on your machine may cause stability issues with Composer.
    If you encounter issues, try to change the following:
    
    The OpenSSL library (0.9.8zc) used by PHP does not support TLSv1.2 or TLSv1.1.
    If possible you should upgrade OpenSSL to version 1.0.1 or above.
    
    Downloading...
    Could not create file /usr/local/bin/composer.phar: fopen(/usr/local/bin/composer.phar): failed to open stream: Permission denied
    Download failed: fopen(/usr/local/bin/composer.phar): failed to open stream: Permission denied
    fwrite() expects parameter 1 to be resource, boolean given
    

    原来是Composer 依赖于 PHP,

    #composer #Mac #OS X
  • 2015-12-29

    在某些情况下,如系统负载过大server无法申请到内存而挂掉、server底层发生段错误、server占用内存过大被内核Kill,或者被某些程序误杀。那server将无法提供服务,导致业务中断,公司收入出现损失。

    有一个非常有效并且常用的方案是crontab重启监控。

    原理是每1分钟执行一次shell脚本,检测server的进程是否存活,如果存在则跳过。如果发现主进程已经挂掉,则执行restart逻辑,先kill掉所有残留的子进程,然后重新启动Server。

    >>More

    #linux #shell
  • 2015-12-25

    需求背景:有个调用统计日志存储和统计需求,要求存储到mysql中;存储数据高峰能达到日均千万,瓶颈在于直接入库并发太高,可能会把mysql干垮。

    问题分析

    思考:应用网站架构的衍化过程中,应用最新的框架和工具技术固然是最优选择;但是,如果能在现有的框架的基础上提出简单可依赖的解决方案,未尝不是一种提升自我的尝试。

    解决:

    • 问题一:要求日志最好入库;但是,直接入库mysql确实扛不住,批量入库没有问题,done。【批量入库和直接入库性能差异参考文章】
    • 问题二:批量入库就需要有高并发的消息队列,决定采用redis list 仿真实现,而且方便回滚。
    • 问题三:日志量毕竟大,保存最近30条足矣,决定用php写个离线统计和清理脚本。

    done,下面是小拽的简单实现过程

    >>More

    #php #redis #mysql #高并发
  • 2015-12-25

    定时运行任务对于一个网站来说,是一个比较重要的任务,比如定时发布文档,定时清理垃圾信息等,现在的网站大多数都是采用PHP动态语言开发的,而对于PHP的实现决定了它没有Java和.Net这种AppServer的概念,而http协议是一个无状态的协议,PHP只能被用户触发,被调用,调用后会自动退出内存,没有常驻内存。

    >>More

    #php #定时任务
  • 2015-12-25

    ##获取IP地址

    <?php
    function get_client_ip(){
        if (isset($_SERVER)) {
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                $realip = $_SERVER['HTTP_X_FORWARDED_FOR'];
            } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
                $realip = $_SERVER['HTTP_CLIENT_IP'];
            } else {
                $realip = $_SERVER['REMOTE_ADDR'];
            }
        } else {
            if (getenv("HTTP_X_FORWARDED_FOR")) {
                $realip = getenv( "HTTP_X_FORWARDED_FOR");
            } elseif (getenv("HTTP_CLIENT_IP")) {
                $realip = getenv("HTTP_CLIENT_IP");
            } else {
                $realip = getenv("REMOTE_ADDR");
            }
        }
        return $realip;
    }
    
    echo $ip = get_client_ip();
    ?>
    

    >>More

    #php #ip