批量更新数据库的某些字段的类型

开发中遇到一个需求,需要将数据库中所有表中的decimal(14,10)类型字段全部改成decimal(22,10),如果逐个去每个表改,工作量还是挺大的,于是上网搜了一下,发下一个sql就可以搞定,如下:

<?php

// 更新sql
$sql = "SELECT CONCAT( 'alter table ', table_name, ' MODIFY COLUMN ', column_name, ' decimal(22,10) DEFAULT NULL;' ) AS execSql, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.columns a WHERE TABLE_SCHEMA = '".$siteDB."' AND data_type IN ('decimal')";

$return = $this->dbh->query($sql)->fetchAll();
foreach($return as $sql)
{
        try
        {
             $this->dbh->query($sql->execSql);
        }
        catch (PDOException $e)
        {
             echo 'error: '. $e->getMessage() ."exec sql : ".$sql->execSql.PHP_EOL.PHP_EOL;
        }
}

所有的表的字段,以及类型都存储在系统表TABLE_SCHEMA中,sql的意思是从系统表取出类型为decimal的对应的表名,字段名,并拼接了一个alter更新的语句。执行这句sql,就能得到所有的更新sql,接下来只需要遍历执行即可。

关于CSRF攻击的一篇博客有感

文章地址:http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html?login=1#!comments

今天看了一篇讲CSRF攻击的文章。主要如下

一.CSRF是什么?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

二.CSRF可以做什么?

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。

博主用银行转账举了一个例子,然后有几个想法

  1. 恶意网站B模拟请求,浏览器带上A的cookie去访问A,通过cookie就能完成身份认证,问题来了,A是怎么记录用户的登陆状态的?如果通过session,B发了一个get去请求A,那么请求头里带上的应该是B的会话ID,A是取不到对应的session记录的。如果通过cookie,可能是这样:cookie里存放了账号密码,请求A的时候,A的后台通过传过来的账号密码比对登陆,显然这种做法不太可能,另外就是cookie里存放了一个类似token的字串,有过期时间,这个token是通过一定的规则生成的,A的后台判断cookie里有这样的token值,并且符合生成的规则,那么就视为这次访问是已登录的状态。
  2. 第一种做法里,A的更新资源操作用get方式也可以实现。这本身就是一个很危险的做法,违反了http的规范。

防止CSRF攻击的主要做法,就是使用令牌。

就是每次请求时都带上一个随机的令牌。这个令牌是保存在session里。将这个值放在隐藏表单里,提交再和后台作比对。

比如后台有这么一段代码

<?php
define("SECRET","123456");
function m_token(){
    return uniqid(rand(),true).SCERET;
}
// 返回的这个值放入session或者cookie'都可以
setcookie("value", m_token(), 3600);

// 前端里有个隐藏表单
// <input type='hidden' value="<?php echo md5($_COOKIE['value']);>" name="hidden_value">

function v_token()
{
    if(md5($_COOKIE['value]) ==$_POST['hidden_value'] ) // 通过
}

 

开发中如何加密用户的密码

最近看到的一篇文章,作者详细讲解了加盐哈希密码,http://blog.jobbole.com/61872/#php,深有感触,结合一些其他的见闻,写点东西。

首先,大部分网站都不会直接存储用户的明文密码。因为一旦数据库暴露,后果很严重。所以一般都会进行哈希加密。哈希加密是一种单向的加密算法,任何长度的输入,都会输出一个固定长度的指纹。常见的有MD5,sha1等函数。但随着技术的飞速发展,这种算法已经出现很多安全的漏洞,甚至可以很轻易的破解。比如常见的有字典法,查表法,彩虹表等,说白了就是去不断的去试,直到得到正确的结果。

于是,加盐哈希的概念就引出了,在对密码哈希前,再随机生成一段字符串,混入密码中再进行加密。这个随机的字串称作为“盐”,这个“盐”需要同密码一起保存进数据库,以便验证。

看似简单,但是如何加盐,又有很多误区,比如说:

使用一样的盐值:这样做两个一样的密码就生成了一样的哈希密码。一旦泄露,破解者一样可以用字典法等进行暴力破解,只不过带上这个不变的盐值。

短盐值:如果盐值太短,攻击者可以构造一个查询表包含所有可能的盐值。以只有3个ASCII字符的盐值为例,一共有95x95x95=857,375种可能。这看起来很多,但是如果对于每个盐值查询表只包含1MB最常见的密码,那么总共只需要837GB的储存空间。一个不到100美元的1000GB硬盘就能解决问题。

组合使用不同的哈希函数:这个其实是有争议的,但是基本上是否定的意见了。

这里有些低端的组合哈希函数,我在网上某些论坛看到它们被推荐使用:

  • md5(sha1(password))
  • md5(md5(salt) + md5(password))
  • sha1(sha1(password))
  • sha1(str_rot13(password + salt))
  • md5(sha1(md5(md5(password) + sha1(password)) + md5(password)))

不要使用其中任何一种。

那如何正确使用加盐哈希密码呢?

盐值应该使用基于加密的伪随机数生成器(Cryptographically Secure Pseudo-Random Number Generator – CSPRNG)来生成,该生成专门被设计用于加密,所以生成随机字串具有很高的随机性和不可预测性,下面列出一些主流语言对应的加密函数:

关于加盐哈希的几个建议:

  1. 盐值至少哈希函数的输出一样长
  2. 盐值保存进数据库
  3. 永远在服务端进行加密和验证

应该使用那些加密函数:

  1. 本文末尾的PHP source code, Java source code, C# source code or the Ruby source code
  2. OpenWall的Portable PHP password hashing framework,即phpass类。
  3. 任何先进的、被良好测试过的哈希加密算法,比如SHA256,SHA512,RipeMD,WHIRLPOOL,SHA3等等
  4. 设计良好的密钥扩展算法,如PBKDF2bcryptscrypt
  5. 安全的crypt()版本($2y$,$5$,$6$)
  6. pass_hash()等函数,5.5.0版本以上。

关于phpass类,给出一个简单的例子:

主要有几个函数

  1. 构造函数
  2. 生成哈希密码 HashPassword($str)
  3. 验证密码 CheckPassword($hashedpass);

 

<?php

// 引入passwordhash.php
require "PasswordHash.php";

// 初始化散列器为不可移植(这样更安全) 
$hash = new PasswordHash(8, false);

$str = "hello world";
$pass = $hash->HashPassword($str);

$hash->CheckPassword("hellow world", $pass); //true

 

贴出一份代码php版本的,下载

<?php
/*
 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
 * Copyright (c) 2013, Taylor Hornby
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

// These constants may be changed without breaking existing hashes.
define("PBKDF2_HASH_ALGORITHM", "sha256");
define("PBKDF2_ITERATIONS", 1000);
define("PBKDF2_SALT_BYTE_SIZE", 24);
define("PBKDF2_HASH_BYTE_SIZE", 24);

define("HASH_SECTIONS", 4);
define("HASH_ALGORITHM_INDEX", 0);
define("HASH_ITERATION_INDEX", 1);
define("HASH_SALT_INDEX", 2);
define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)
{
    // format: algorithm:iterations:salt:hash
    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));
    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" .
        base64_encode(pbkdf2(
            PBKDF2_HASH_ALGORITHM,
            $password,
            $salt,
            PBKDF2_ITERATIONS,
            PBKDF2_HASH_BYTE_SIZE,
            true
        ));
}

function validate_password($password, $correct_hash)
{
    $params = explode(":", $correct_hash);
    if(count($params) < HASH_SECTIONS)
       return false;
    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);
    return slow_equals(
        $pbkdf2,
        pbkdf2(
            $params[HASH_ALGORITHM_INDEX],
            $password,
            $params[HASH_SALT_INDEX],
            (int)$params[HASH_ITERATION_INDEX],
            strlen($pbkdf2),
            true
        )
    );
}

// Compares two strings $a and $b in length-constant time.
function slow_equals($a, $b)
{
    $diff = strlen($a) ^ strlen($b);
    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)
    {
        $diff |= ord($a[$i]) ^ ord($b[$i]);
    }
    return $diff === 0;
}

/*
 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
 * $algorithm - The hash algorithm to use. Recommended: SHA256
 * $password - The password.
 * $salt - A salt that is unique to the password.
 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
 * $key_length - The length of the derived key in bytes.
 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
 * Returns: A $key_length-byte key derived from the password and salt.
 *
 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
 *
 * This implementation of PBKDF2 was originally created by https://defuse.ca
 * With improvements by http://www.variations-of-shadow.com
 */
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
{
    $algorithm = strtolower($algorithm);
    if(!in_array($algorithm, hash_algos(), true))
        trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);
    if($count <= 0 || $key_length <= 0)
        trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);

    if (function_exists("hash_pbkdf2")) {
        // The output length is in NIBBLES (4-bits) if $raw_output is false!
        if (!$raw_output) {
            $key_length = $key_length * 2;
        }
        return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
    }

    $hash_length = strlen(hash($algorithm, "", true));
    $block_count = ceil($key_length / $hash_length);

    $output = "";
    for($i = 1; $i <= $block_count; $i++) {
        // $i encoded as 4 bytes, big endian.
        $last = $salt . pack("N", $i);
        // first iteration
        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
        // perform the other $count - 1 iterations
        for ($j = 1; $j < $count; $j++) {
            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
        }
        $output .= $xorsum;
    }

    if($raw_output)
        return substr($output, 0, $key_length);
    else
        return bin2hex(substr($output, 0, $key_length));
}
?>

 

php开发里遇到的跨域问题

跨域是什么?跨域是由于浏览器的同源策略而引发的问题,所谓同源是指,域名,协议,端口均相同。举个例子:

www.123.com访问www.456.com,属于跨域,域名不同

abc.123.com访问def.123.com,属于跨域,子域名不同

http://123.com访问https://123.com,属于跨域,协议不同

123.com:8080访问123.com:8081,属于跨域,端口不同

开发中主要有三种方法来处理跨域问题:

1. 设置header

可以再在header里设置ACCESS-CONTROL-ALLOW-ORIGIN,比如

header(“ACCESS-CONTROL-ALLOW-ORIGIN:www.example.com”);允许示例网站访问当前页面。

或者使用通配符:

header(“ACCESS-CONTROL-ALLOW-ORIGIN:*”);允许任何网站来访问当前页面。

2. 代理访问

比如我当前域名是123.com,我的123.com/index.html需要访问456.com/api.php,如果直接访问是不可行的。如果在123.com下有一个getapi.php页面,在这个页面里去请求456.com/api.php,再把得到的结果返回给index.html,这样做相当于跳过了浏览器的检测。

3. jsonp

http://blog.csdn.net/navy_xue/article/details/40016475