万字总结PHP与JavaScript、PHP与PHP 实现开箱即用的AES、RSA和较为安全的自 ...

打印 上一主题 下一主题

主题 1831|帖子 1831|积分 5493

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
实操(下方有理论)

没有绝对安全的系统



  • 对于前后端通信安全的声明:对于前端,网页代码是暴漏给用户的,用户可以恣意摆布,可以反编译混淆过的代码,也可以调试、研究代码去攻破,因此不能保证百分百的安全,能做的仅仅是让攻击者增加攻击资本。
  • 明文硬编码毛病:如果是硬编码,把key,secret等明文暴漏,写死在代码中,这是很不安全的行为,代码检察即可攻破,(世界是个巨大的草台班子,这种行为并不少)。
  • 调用毛病:即使加密的逻辑代码被加密,秘钥被加密,可是其它模块的代码是明文的(此地无银三百两)(比方decrypt(response_data)),那么攻击者乃至不用调试加密代码,直接调用明文函数就可绕过。
  • 硬破解:开发者再怎么加密或混淆代码,但攻击者就像破案一样,团结Console控制台,一点一点耐心还原实验逻辑。
  • 通信获取:有些秘钥key,secret,是通过接口获取的,但是获取这些配置的过程,也不安全,可以被获取。
  • 对于前后端通信:能做的就是混淆全部代码(html加密混淆方式自行谷歌,有在线的和一些npm插件);不要硬编码;勤更换秘钥key,Secret,最好使用随机较长的。服务端在不影响业务的前提及时作废老数据验证策略。
服务端与客户端(AES对称加密)



  • 留意:

    • AES有两个非常重要的参数:key和vi,若攻击者知道key与vi,等同于知道了钥匙。
    • key:加密算法的焦点,用于确保只有持有相同密钥的人才能解密数据,保举随机生成。
    • vi:一个随机或伪随机的值,用于在加密过程中初始化加密算法的状态,确保使用相同密钥加密的相同明文会产生差别的密文。

  • PHP加密,JavaScript解密(PHP需要开启openssl扩展)
  1. /**
  2. * @function PHP使用AES 256加密字符串数据
  3. * @param    $str string 被加密的字符串
  4. * @param    $key string 256位自定义key,是加密算法的核心,用于确保只有持有相同密钥的人才能解密数据,推荐随机生成32个随机字符,确保不可预测
  5. * @param    $iv  string 128位初始化向量,是一个随机或伪随机的值,用于在加密过程中初始化加密算法的状态,确保使用相同密钥加密的相同明文会产生不同的密文
  6. * @return string
  7. */
  8. function aesEncrypt($str, $key, $iv) {
  9.     return base64_encode(openssl_encrypt($str, 'AES-256-CBC', $key, true, $iv));
  10. }
  11. $str = '{"code":0,"msg":"success","data":{"lists":{"k1":"v1","k2":"v2"}}}';
  12. $key = 'abcdabcdabcdabcdabcdabcdabcdabcd';
  13. $iv  = 'abcdabcdabcdabcd';
  14. echo aesEncrypt($str, $key, $iv);
复制代码
  1. <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
  2. <script>
  3.     function decrypt(str, key, iv) {
  4.         return CryptoJS.AES.decrypt(str, CryptoJS.enc.Utf8.parse(key), {iv: CryptoJS.enc.Utf8.parse(iv), padding: CryptoJS.pad.Pkcs7}).toString(CryptoJS.enc.Utf8);
  5.     }
  6.     console.log(decrypt('naQ3ZnyGetarkvugVeNaroUe9Hrk+KW3sKXSs42QJ6tPER1zcxvS8E2xsCJG0UaJ5WMFhFgLjD6z3wbqPLFgRFIl+BaGqINmdPbm9B+DXMs=', 'abcdabcdabcdabcdabcdabcdabcdabcd', 'abcdabcdabcdabcd'));
  7. </script>
复制代码


  • JavaScript加密,PHP解密
  1. /**
  2. * @function PHP使用AES 256解密字符串数据
  3. * @param    $str string 被解密的字符串
  4. * @param    $key string 256位自定义key,是加密算法的核心,用于确保只有持有相同密钥的人才能解密数据,推荐随机生成32个随机字符,确保不可预测
  5. * @param    $iv  string 128位初始化向量,是一个随机或伪随机的值,用于在加密过程中初始化加密算法的状态,确保使用相同密钥加密的相同明文会产生不同的密文
  6. * @return string
  7. */
  8. function aesDecrypt($str, $key, $iv) {
  9.     return base64_decode(base64_encode(openssl_decrypt(base64_decode($str), 'AES-256-CBC', $key, true, $iv)));
  10. }
  11. $str = 'aZlC1imJbcUQblRR0QsMPGF+8Kbja/1fU+tdcTyAUVw=';
  12. $key = 'abcdabcdabcdabcdabcdabcdabcdabcd';
  13. $iv  = 'abcdabcdabcdabcd';
  14. echo aesDecrypt($str, $key, $iv);
复制代码
  1. <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
  2. <script>
  3.     function encrypt(str, key, iv) {
  4.         return CryptoJS.AES.encrypt(str, CryptoJS.enc.Utf8.parse(key), {iv: CryptoJS.enc.Utf8.parse(iv), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7}).toString();
  5.     }
  6.     console.log(encrypt('{"name":"张三", "age": 20}', 'abcdabcdabcdabcdabcdabcdabcdabcd', 'abcdabcdabcdabcd'))
  7. </script>
复制代码


  • 前端针对key和vi的生成与通信题目不完美的办理方案,怎么选是当前业务的能容忍的安全底线,与开发便捷度的衡量。

    • 如果MVC架构:可在后端实时赋值给JavaScript,存入js内存,加载一次页面变一次秘钥。
    • 若求便捷:把代码写死后混淆,避免明文泄漏。大概每次通信都明文传输随机key和和随机vi,团结密文一起传送。
    • 若求安全:服务端写程序更换,或手动更换JavaScript脚本文件中的key和vi值,或根据用户的会话字符串(不要直接存会话中),前后端根据回话字符串使用相同的算法计算出新的key和vi值。

服务端与客户端(较为安全的自界说对称加密算法,避免AES算法的 key和vi在客户端存储题目)



  • 感受一下“Hello”的密文(安全品级绝对比不上主流加密算法)
    $2a34912a90c3cf9bb1ec6aa64351f8ef56ae5726697dcefb07f3a73b09e789545f1295726714ed66d3b04e7cf696fc32
  • PHP端加解密类
  1. <?php
  2. class Crypt {
  3.     //声明密文前缀
  4.     private static $prefix = '$';
  5.     /**
  6.      * @function 生成ASCII字符随机盐
  7.      * @return   array
  8.      */
  9.     public static function generateRandStr() {
  10.         //这里最小设置为1,最大设置为255,大小与密文长度成正比,随机长度用于加强混淆
  11.         $length = rand(1, 255);
  12.         $salt = '';
  13.         for ($i = 0; $i < $length; $i++) {
  14.             //按照ASCII,生成随机单字节字符
  15.             $salt .= chr(rand(0, 255));
  16.         }
  17.         return ['salt' => $salt, 'length' => $length];
  18.     }
  19.     /**
  20.      * @function 计算偏移量 加密用
  21.      * @param    $v int 明文每个字节的ASCII编码值
  22.      * @param    $s int 自定义算法取余后的值
  23.      * @return   int
  24.      */
  25.     private static function calcOffsetEn($v, $s) {
  26.         $r = 255 - $v;
  27.         if ($s > $r) {
  28.             return $s - $r - 1;
  29.         }
  30.         return $v + $s;
  31.     }
  32.     /**
  33.      * @function 计算偏移量 解密用
  34.      * @param    $v int 密文每个字节的ASCII编码值
  35.      * @param    $s int 自定义算法取余后的值
  36.      * @return   int
  37.      */
  38.     private static function calcOffsetDe($v, $s) {
  39.         if ($v >= $s) {
  40.             return $v - $s;
  41.         }
  42.         return 255 - ($s - $v) + 1;
  43.     }
  44.     /**
  45.      * @function 明文和盐混淆成密文
  46.      * @param    $data string 要被加密的明文
  47.      * @param    $salt array  盐
  48.      * @return   string       加密后的数据
  49.      */
  50.     private static function encryptWithSalt($data, $salt) {
  51.         $result   = '';
  52.         //获取明文有几个单字节的长度,此处不要用mb_strlen
  53.         $data_len = strlen($data);
  54.         for ($i = 0; $i < $data_len; $i ++) {
  55.             //逐个字节混淆 字符串中每个字节的ASCII值 与
  56.             $result .= chr(static::calcOffsetEn(ord($data[$i]), ord($salt['salt'][$i % $salt['length']])));
  57.         }
  58.         return $result;
  59.     }
  60.     /**
  61.      * @function 密文解密
  62.      * @param    $data string 要被解密的密文
  63.      * @param    $salt array   盐
  64.      * @return   string
  65.      */
  66.     private static function decryptWithSalt($data, $salt) {
  67.         $result = '';
  68.         $data_len = strlen($data);
  69.         for ($i = 0; $i < $data_len; $i++) {
  70.             // 去盐处理并转换为字符
  71.             $result .= chr(static::calcOffsetDe(ord($data[$i]), ord($salt['salt'][$i % $salt['length']])));
  72.         }
  73.         return $result;
  74.     }
  75.     /**
  76.      * @function 加密数据
  77.      * @param    $data string 要被加密的字符串
  78.      * @return   string
  79.      */
  80.     public static function encrypt($data) {
  81.         if ($data == '') {
  82.             return '';
  83.         }
  84.         // 生成随机盐
  85.         $salt = static::generateRandStr();
  86.         // 将盐的长度、盐本身、密文
  87.         return static::$prefix . bin2hex(chr($salt['length']) . $salt['salt'] . static::encryptWithSalt(mb_convert_encoding($data, 'UTF-8', 'UTF-8'), $salt));
  88.     }
  89.     /**
  90.      * @function 解密数据
  91.      * @param    $data string 要被加密的字符串
  92.      * @return   string
  93.      */
  94.     public static function decrypt($data) {
  95.         if ($data == '') {
  96.             return '';
  97.         }
  98.         // 检查是否是加密字符串,通过标签判断
  99.         if ($data[0] != static::$prefix) {
  100.             return '';
  101.         }
  102.         // 去掉标签并从16进制字符串转换回二进制数据
  103.         $clear_data = hex2bin(substr($data, strlen(static::$prefix)));
  104.         // 获取盐的长度
  105.         $salt_len = ord($clear_data[0]);
  106.         // 将解密数据转换为UTF-8编码的字符串
  107.         return mb_convert_encoding(static::decryptWithSalt(substr($clear_data, 1 + $salt_len), ['salt' => substr($clear_data, 1, $salt_len), 'length' => $salt_len]), 'UTF-8', 'UTF-8');
  108.     }
  109. }
复制代码


  • JavaScript端加解密类
  1. class Crypt {
  2.     // 声明密文前缀
  3.     static prefix = '$';
  4.     /**
  5.      * 生成ASCII字符随机盐
  6.      * @returns {Object} {salt, length}
  7.      */
  8.     static generateRandStr() {
  9.         const length = Math.floor(Math.random() * 255) + 1;  // 随机盐的长度在1到255之间
  10.         let salt = '';
  11.         for (let i = 0; i < length; i++) {
  12.             salt += String.fromCharCode(Math.floor(Math.random() * 256));  // 生成一个随机的ASCII字符
  13.         }
  14.         return { salt, length };
  15.     }
  16.     /**
  17.      * 计算加密时的偏移量
  18.      * @param {number} v 明文字符的ASCII值
  19.      * @param {number} s 盐的ASCII值
  20.      * @returns {number}
  21.      */
  22.     static calcOffsetEn(v, s) {
  23.         const r = 255 - v;
  24.         if (s > r) {
  25.             return s - r - 1;
  26.         }
  27.         return v + s;
  28.     }
  29.     /**
  30.      * 计算解密时的偏移量
  31.      * @param {number} v 密文字符的ASCII值
  32.      * @param {number} s 盐的ASCII值
  33.      * @returns {number}
  34.      */
  35.     static calcOffsetDe(v, s) {
  36.         if (v >= s) {
  37.             return v - s;
  38.         }
  39.         return 255 - (s - v) + 1;
  40.     }
  41.     /**
  42.      * 明文与盐混淆加密
  43.      * @param {Uint8Array} data 明文的字节数据
  44.      * @param {Object} salt 盐
  45.      * @returns {Uint8Array} 加密后的密文
  46.      */
  47.     static encryptWithSalt(data, salt) {
  48.         let result = [];
  49.         const data_len = data.length;
  50.         for (let i = 0; i < data_len; i++) {
  51.             const data_char_code = data[i];
  52.             const salt_char_code = salt.salt.charCodeAt(i % salt.length);
  53.             result.push(Crypt.calcOffsetEn(data_char_code, salt_char_code));
  54.         }
  55.         return new Uint8Array(result);
  56.     }
  57.     /**
  58.      * 密文解密
  59.      * @param {Uint8Array} data 密文的字节数据
  60.      * @param {Object} salt 盐
  61.      * @returns {Uint8Array} 解密后的明文
  62.      */
  63.     static decryptWithSalt(data, salt) {
  64.         let result = [];
  65.         const data_len = data.length;
  66.         for (let i = 0; i < data_len; i++) {
  67.             const data_char_code = data[i];
  68.             const salt_char_code = salt.salt.charCodeAt(i % salt.length);
  69.             result.push(Crypt.calcOffsetDe(data_char_code, salt_char_code));
  70.         }
  71.         return new Uint8Array(result);
  72.     }
  73.     /**
  74.      * 将字符串转换为 UTF-8 字节数组
  75.      * @param {string} str
  76.      * @returns {Uint8Array}
  77.      */
  78.     static stringToBytes(str) {
  79.         const encoder = new TextEncoder();
  80.         return encoder.encode(str);
  81.     }
  82.     /**
  83.      * 将 UTF-8 字节数组转换为字符串
  84.      * @param {Uint8Array} bytes
  85.      * @returns {string}
  86.      */
  87.     static bytesToString(bytes) {
  88.         const decoder = new TextDecoder('utf-8');
  89.         return decoder.decode(bytes);
  90.     }
  91.     /**
  92.      * 加密数据
  93.      * @param {string} data 明文
  94.      * @returns {string} 加密后的字符串
  95.      */
  96.     static encrypt(data) {
  97.         if (data === '') return '';
  98.         // 生成随机盐
  99.         const salt = Crypt.generateRandStr();
  100.         // 将数据转换为字节数组
  101.         const byte_data = Crypt.stringToBytes(data);
  102.         // 加密数据
  103.         const encrypted_data = Crypt.encryptWithSalt(byte_data, salt);
  104.         // 生成盐长度字符并将其与盐和密文一起编码为十六进制字符串
  105.         const salt_length = String.fromCharCode(salt.length);
  106.         // 转换为 UTF-8 字节数组并进行十六进制编码
  107.         const all_data = new Uint8Array([salt_length.charCodeAt(0), ...salt.salt.split('').map(c => c.charCodeAt(0)), ...encrypted_data]);
  108.         return Crypt.prefix + Crypt.toHex(all_data);
  109.     }
  110.     /**
  111.      * 解密数据
  112.      * @param {string} data 加密后的字符串
  113.      * @returns {string} 解密后的明文
  114.      */
  115.     static decrypt(data) {
  116.         if (data === '') return '';
  117.         // 检查加密格式
  118.         if (data[0] !== Crypt.prefix) return '';
  119.         // 去掉前缀并解码16进制数据
  120.         const clear_data = Crypt.fromHex(data.slice(1));
  121.         // 获取盐的长度
  122.         const salt_length = clear_data[0];
  123.         // 解密数据并返回结果
  124.         const salt = {
  125.             salt: String.fromCharCode(...clear_data.slice(1, salt_length + 1)),
  126.             length: salt_length
  127.         };
  128.         const encrypted_data = clear_data.slice(salt_length + 1);
  129.         const decrypted_bytes = Crypt.decryptWithSalt(encrypted_data, salt);
  130.         return Crypt.bytesToString(decrypted_bytes);
  131.     }
  132.     /**
  133.      * 将字节数组转换为十六进制字符串
  134.      * @param {Uint8Array} bytes
  135.      * @returns {string}
  136.      */
  137.     static toHex(bytes) {
  138.         let hex = '';
  139.         for (let i = 0; i < bytes.length; i++) {
  140.             hex += bytes[i].toString(16).padStart(2, '0');
  141.         }
  142.         return hex;
  143.     }
  144.     /**
  145.      * 将十六进制字符串转换为字节数组
  146.      * @param {string} hex
  147.      * @returns {Uint8Array}
  148.      */
  149.     static fromHex(hex) {
  150.         let bytes = [];
  151.         for (let i = 0; i < hex.length; i += 2) {
  152.             bytes.push(parseInt(hex.substr(i, 2), 16));
  153.         }
  154.         return new Uint8Array(bytes);
  155.     }
  156. }
复制代码


  • PHP加密JavaScript解密(测试包含中文、英文、数字、心情)
[code]echo Crypt::encrypt(json_encode([
    'code' => 0,
    'msg'  => 'success',
    'data' => [
        'name'       => '张三',
        'en_name'    => 'SanZhang',
        'age'        => 20,
        'mood_emoji' => '
继续阅读请点击广告
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

络腮胡菲菲

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表