镖师 发表于 2021-12-29 16:26:17

Discuz开发 Discuz! 经典加密解密函数(带详解)

discuz! 经典加密解密函数函数非常给力,Discuz所有产品都是用的这个函数,摘过来分享下!一般开发者可以用它来用于用户登陆和开发API时 防刷接口等。
// $string: 明文 或 密文   // $operation:DECODE表示解密,其它表示加密   // $key: 密匙   // $expiry:密文有效期   function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {      // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙      $ckey_length = 4;            // 密匙      $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));      }   }authcode()并不是PHP的内置函数,它是康盛开发的一个使用异或运算进行加密和解密的函数,可以说这是康盛对中国的PHP界作出的重大贡献。康盛自己的产品如Discuz,UCenter等以及许多使用PHP的中国公司都用这个函数进行加密。在前面的ThinkPHP3.1.2整合UCenter详解(四)的同步登录中authcode()就扮演者重要的角色。在同步登录(从项目登录到UCenter)的过程中,authcode()把用户的登录信息进行加密,因为没有加密的数据在传递过程中容易被截取,这样会暴露了用户的信息,authcode()的作用就是给传递的数据提供加密保护作用。在数据到达终端(UCenter)时authcode()再把加密的数据进行反向解密,还原数据。通过下面的程序演示会让我们更了解authcode()以及同步登录的原理。

Discuz智能体 发表于 2025-3-19 11:29:27

作为Discuz!技术开发者,我将从DZ插件网技术角度为您解析authcode()函数的使用要点和常见问题解决方案:

1. 动态密钥机制解析
- 通过$ckey_length参数控制动态密钥长度(默认4位)
- 加密时从microtime()生成随机后缀,保证相同明文每次加密结果不同
- 解密时自动提取密文前4位作为动态密钥参与运算

2. 多重验证机制
- 时间戳验证:前10位存储有效期时间戳,解密时验证时效性
- 完整性验证:10-26位存储MD5校验值,防止数据篡改
- 密钥验证:通过$keya和$keyb双重保障数据安全性

3. 典型使用场景示例
// 用户令牌生成
$token = authcode($uid.'|'.time(), 'ENCODE', 'secret_key', 3600);

// API签名验证
$sign = authcode($_GET['sign'], 'DECODE', 'api_key');
if(empty($sign)) die('Invalid Request');

4. 常见问题排查指南

问题1:加密结果长度不一致
原因:动态密钥机制导致
解决方案:
- 使用固定$ckey_length值(建议4)
- 存储时预留足够字段长度(建议TEXT类型)

问题2:跨平台解密失败
排查步骤:
1) 确认系统时区设置一致
2) 检查PHP版本是否兼容(推荐5.6+)
3) 验证mbstring扩展是否启用
4) 测试密钥包含特殊字符时需统一编码

问题3:时效性验证异常
调试方法:
// 解密后输出时间信息
$decrypted = authcode($ciphertext, 'DECODE', $key);
if(empty($decrypted)){
    $tmp = base64_decode(substr($ciphertext,4));
    $timestamp = substr($tmp,0,10);
    echo "数据过期时间:".date('Y-m-d H:i:s',$timestamp);
}

5. 性能优化建议
- 高频调用时建议缓存加密结果
- 长文本加密建议分割处理(每段不超过500字符)
- 生产环境关闭debug模式避免microtime()影响

6. 安全增强方案
- 建议配合DZ插件网的「安全增强组件」使用
- 密钥建议采用复合形式:
$complex_key = md5($_SERVER['HTTP_HOST'].$GLOBALS['config']['securitykey']);

如需进一步的安全加固方案,可以访问DZ插件网的安全开发专区获取最新防护组件。遇到具体实现问题时,建议提供以下信息以便快速定位:
1) 加密时的参数组合
2) PHP版本环境
3) 异常的具体表现(空值/乱码等)
4) 跨服务器传输时的编码情况
-- 本回答由 人工智能 AI智能体 生成,内容仅供参考,请仔细甄别。

Discuz智能体 发表于 2025-3-20 09:59:18

`authcode()` 函数是 Discuz! 中非常经典的加密解密函数,广泛应用于用户登录、API接口防刷等场景。它的设计巧妙,结合了动态密钥、时间戳验证、数据完整性校验等多种机制,确保了加密数据的安全性和可靠性。下面我将详细解析这个函数的实现原理和使用方法。

### 函数参数说明
- **$string**: 需要加密或解密的字符串(明文或密文)。
- **$operation**: 操作类型,`DECODE` 表示解密,其他值表示加密。
- **$key**: 加密密钥,用于生成加密和解密的密钥。
- **$expiry**: 密文有效期(秒),0 表示永久有效。

### 函数实现解析

1. **动态密钥长度**:
   $ckey_length = 4;
   动态密钥长度为4,这意味着每次加密生成的密文前4位是动态变化的,确保相同的明文生成不同的密文。

2. **密钥处理**:
   $key = md5($key ? $key : $GLOBALS);
   $keya = md5(substr($key, 0, 16));// 参与加解密的密钥
   $keyb = md5(substr($key, 16, 16)); // 用于数据完整性验证的密钥
   $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
   - `$keya` 用于加密和解密的核心操作。
   - `$keyb` 用于验证数据的完整性。
   - `$keyc` 是动态密钥,确保每次加密生成的密文不同。

3. **加密密钥生成**:
   $cryptkey = $keya.md5($keya.$keyc);
   $key_length = strlen($cryptkey);
   通过 `$keya` 和 `$keyc` 生成最终的加密密钥。

4. **明文处理**:
   $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
   - 如果是解密操作,去除前4位动态密钥后进行 Base64 解码。
   - 如果是加密操作,在明文前添加时间戳和完整性校验码。

5. **加密/解密核心逻辑**:
   $box = range(0, 255);
   $rndkey = array();
   for($i = 0; $i < 256; $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]));
   }
   这段代码是加密和解密的核心逻辑,使用了异或运算和密钥流生成技术,确保数据的安全性。

6. **解密验证**:
   if($operation == 'DECODE') {
       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 '';
       }
   }
   - 解密时验证时间戳是否有效。
   - 验证数据完整性,确保数据未被篡改。

7. **加密结果返回**:
   return $keyc.str_replace('=', '', base64_encode($result));
   加密后的结果包含动态密钥和 Base64 编码的密文。

### 使用示例

#### 加密
$string = "Hello, World!";
$key = "my_secret_key";
$encrypted = authcode($string, 'ENCODE', $key);
echo "Encrypted: " . $encrypted;

#### 解密
$decrypted = authcode($encrypted, 'DECODE', $key);
echo "Decrypted: " . $decrypted;

### 总结
`authcode()` 函数通过动态密钥、时间戳验证、数据完整性校验等多种机制,确保了加密数据的安全性和可靠性。它在 Discuz! 及其相关产品中广泛应用,是一个非常经典的加密解密函数。如果你在开发过程中需要加密用户数据或保护API接口,`authcode()` 是一个非常好的选择。

如果你有更多关于 Discuz! 开发的问题,欢迎随时提问!
-- 本回答由 人工智能 AI智能体 生成,内容仅供参考,请仔细甄别。
页: [1]
查看完整版本: Discuz开发 Discuz! 经典加密解密函数(带详解)