PHP字符编码问题,但只与char T,至少这是我的猜测


PHP char encoding issue but only with char T, at least that is my guess

我在不同的地方发布了这个类,但现在发现了一个错误。它变得非常流行,所以我想知道是否有人能帮我弄清楚这个问题。我已经用了几个小时了,我发现99%的情况下,问题都出在小写字母T和大写字母T上。我尝试了不同的编码,并将t更改为不同的字符。但它总是像t一样。

<?php
/**
 * An open source PHP library collection
 *
 * @category     RoboTamer
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      http://www.RoboTamer.com/license.html
 * @link         http://www.RoboTamer.com
 **/
/**
 * RTCrypt
 *
 * RTCrypt allows for encryption and decryption on the fly using
 * a simple but effective method.
 *
 * RTCrypt does not require mcrypt, mhash or any other PHP extension,
 * it uses only PHP.
 *
 * @category     RoboTamer
 * @package      RTCrypt
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2011, Dennis T Kaplan
 * @license      http://www.RoboTamer.com/license.html
 * @link         http://www.RoboTamer.com
 **/
class RTCrypt
{
    const streight = '012345679ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    private $scramble1 = NULL;
    private $scramble2 = NULL;
    public function __construct()
    {
        $this->setScramble1();
    }
    public function __destruct(){}
    public function getStreight()
    {
        return self::streight;
    }
    public function getScramble1()
    {
        return implode($this->scramble1);
    }
    public function getScramble2()
    {
        return implode($this->scramble2);
    }
    /**
     * Set the characters you like to replace
     *
     * @access  private
     * @param   string $str
     */
    private function setScramble1()
    {
        $this->scramble1 = str_split(self::streight);
    }
    /**
     * This is your private key.
     * You can generate a random private key based on scramble1 via
     * the randomizeString($scramble1) function.
     *
     * @access  public
     * @param   string $str
     * @return  bool TRUE
     */
    public function setScramble2($str=NULL)
    {
        if($str===NULL){
            trigger_error('No key, use genKey($str)', E_USER_ERROR );
            die;
        }
        $this->scramble2 = str_split($str);
        return TRUE;
    }
    /**
     * This will encrypt your data
     *
     * @access  public
     * @param   string $str
     * @return  string encrypt data
     */
    public function encrypt($str)
    {
        if($this->scramble2 === NULL) $this->setScramble2();
        $str = base64_encode($str);
        $len = strlen($str);
        $newstr='';
        for($i=0; $i < $len;$i++){
            $r = substr($str, -1);
            $str = substr($str, 0, -1);
            $an = array_search($r,$this->scramble1);
            if($an > 0){
                $newstr .= $this->scramble2[$an];
            }else{
                $newstr .= $r;
            }
        }
        return $newstr;
    }
    /**
     * This will decrypt a Crypted string back to the original data
     *
     * @access  public
     * @param   string $str
     * @return  string
     */
    public function decrypt($str)
    {
        if($this->scramble2 === NULL) $this->setScramble2();
        $len = strlen($str);
        $newstr='';
        for($i=0; $i < $len;$i++){
            $r = substr($str, -1);
            $str = substr($str, 0, -1);
            $an = array_search($r,$this->scramble2);
            if($an > 0){
                $newstr .= $this->scramble1[$an];
            }else{
                $newstr .= $r;
            }
        }
        $str = base64_decode($newstr);
        return $str;
    }
    /**
     * Generates your private key.
     * You would use it to set scramble2
     * Keep it save!
     *
     * @access  public
     * @return  string
     */
    public function genKey()
    {
        $array = str_split(self::streight);
        shuffle($array);
        return implode($array);
    }
}
?>

测试脚本:

define('BR',"'n");
Locale::setDefault('en-US');
include 'RTCrypt.php';
$Crypter = new RTCrypt();
$key = $Crypter->genKey();
echo BR.'Your Key: '.BR.$Crypter->getStreight().BR.$key.BR.BR;
$Crypter->setScramble2($key);
$str = '/**
 * encode, decode and also serialize when nessesery.
 * Works with anything that php can serialize.
 * string, array, etc.
 *
 * @category     RoboTaMeR
 * @package      Strsafe
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      http://RoboTamer.com/license.html
 * @link         http://RoboTamer.com
 * @todo         combine this with RTCrypt to one class
 */';
echo 'String before RTCrypt: '.BR.$str.BR.BR;
//echo BR.base64_encode($str).BR.BR;
$str = $Crypter->encrypt($str);
echo 'RTCrpted: '.$str.BR.BR;
echo 'String after RTCrypt: '.BR.$Crypter->decrypt($str);
echo BR.BR;
?>

String before RTCrypt:

/**
 * encode, decode and also serialize when nessesery.
 * Works with anything that php can serialize.
 * string, array, etc.
 *
 * @category     RoboTaMeR
 * @package      Strsafe
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      http://RoboTamer.com/license.html
 * @link         http://RoboTamer.com
 * @todo         combine this with RTCrypt to one class
 */

RTCrypt后的字符串:

/**
 * encode, decode and also serialize when nessesery.
 * Works wich anyching chac php can serialize.
 * string, array, etc.
 *
 * @category     RoboTaMeR
 * @package      Strsafe
 * @auchor       Dennis T Kaplan
 * @copyrighc    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      hctp://RoboTamer.com/license.html
 * @link         hctp://RoboTamer.com
 * @codo         combine chis with RTCrypt co one class
 */

我唯一能想到的是:

if($an > 0){

索引0上可以有一个键。您应该检查FALSE,通过使用:

if ($an !== FALSE)

即使修复了这个错误,您的代码也是完全不安全的。我一眼就能看出三个缺陷:

  1. 这是一个替换密码。一种非常脆弱的古老方法。字符频率计数是一个简单的方法来击败它。使用Base64会使攻击变得有些复杂,但不会太复杂。

  2. Base64输出中的几个字符将始终映射到它们自己。即+, /, 8(看起来您只是在字符串中忘记了这个)和==的身份映射是可以接受的,因为它只是字符串结束标记,但其他三个是安全缺陷。

  3. 您正在使用shuffle创建私钥。shuffle不是为加密目的而设计的,因此可能使用弱随机数生成器。
    如果它映射到只有32位种子的c RNG,我不会感到惊讶。这意味着只有40亿个不同的密钥,这对于暴力破解来说是微不足道的。