从微信跳一跳看加密算法选择

通过微信小游戏跳一跳,看加密算法的选择

近期微信更新,有跳一跳的游戏,并被网友找出源码并刷分,我也试了一下,成功刷了 1024。其中看到这个游戏使用了常用加密库 crypto-js(不支持RSA),它可以在浏览器端使用。而 nodejs 也有 crypto 模块,而且支持的加密算法更多。下面就来看看加密算法有哪些,并如何根据场景选择算法。

常用加密算法

Hash 算法

能计算出一个消息所对应到的,长度固定的字符串(又称消息摘要)的算法。

结果值长的 Hash 算法也能用于验证文件或数据的完整性。

Hash 算法通常有:md5,sha,sha1,sha256,sha512,RSA-SHA; 但 crypto-js 不支持 RSA。这里挑几个。

测试效率和结果:

var CryptoJS = require('crypto-js');

function test(hashs, txt) {
  hashs.forEach(name => hashAlgorithm(name, txt);)
}

function hashAlgorithm(algorithm, txt) {
  algorithm = algorithm.toUpperCase();
  console.time(algorithm);
  console.log(algorithm, CryptoJS[algorithm](txt).toString());
  console.timeEnd(algorithm);
}

const algs = ['md5', 'sha1', 'sha256', 'sha512', 'sha3', 'sha384', 'ripemd160'];
test(algs, 'robin');

// `crypto-js`
// MD5 8ee60a2e00c90d7e00d5069188dc115b
// MD5: 3.229ms
// SHA1 474ee9ee179b0ecf0bc27408079a0b15eda4c99d
// SHA1: 0.485ms
// SHA256 287782ef42356ef3d6551590f1ef0117b71d876df0f9b3eb58d088864770c74c
// SHA256: 0.505ms
// SHA512 071b38098125700fe49e65ef0059bfda1fbd110472a8d7490e0b3af6a8260712f958b9c7f27be61a8ddfe4fecb71f9e8002360f894f06dd4b1f383963d71db41
// SHA512: 1.519ms
// SHA3 a621609aa4b8be7efac720538c86e998ebfa1ea67e4d10fa94df74388bbf443e18d92ca5bf483b789f6b42ea4f98bb086f48664bd5c1f4df476629419fa9e8b8
// SHA3: 1.150ms
// SHA384 2750a9f5dadea58259fb17a3259b00a94648d40481eea91dc0e585e151e751001dc86457aa765019b0ca9721841b59f9
// SHA384: 0.498ms
// RIPEMD160 9ef3a5f5ede0a741e38740cc3cc3e1bfcdeaece5
// RIPEMD160: 0.661ms

// 
// md5,8ee60a2e00c90d7e00d5069188dc115b
// md5: 0.093ms
// sha,0894ec72c817d851286dd9c7309db3408b1dd620
// sha: 0.056ms
// sha1,474ee9ee179b0ecf0bc27408079a0b15eda4c99d
// sha1: 0.023ms
// sha256,287782ef42356ef3d6551590f1ef0117b71d876df0f9b3eb58d088864770c74c
// sha256: 0.029ms
// sha512,071b38098125700fe49e65ef0059bfda1fbd110472a8d7490e0b3af6a8260712f958b9c7f27be61a8ddfe4fecb71f9e8002360f894f06dd4b1f383963d71db41
// sha512: 0.033ms
// RSA-SHA,0894ec72c817d851286dd9c7309db3408b1dd620
// RSA-SHA: 0.023ms

通常哈希算法用于登陆密码的加密。这里看到 SHA1 居然比 MD5要快,而且安全性更高,建议使用 SHA1SHA256SHA512, MD5 也由于字典库的出现和计算机性能的提升而变得相对不安全。

以下是维基百科的参考:

salt 加盐

由于被加密信息,很容易被使用彩虹表暴力破解,特别是 md5 这类已经出现案例的情况。另一种情况是大家的方法都是使用 md5 或 SHA1,那其实只是把明文密码变成更复杂密文,并不能很有效地防止撞库,攻击者可以直接用加密后的密文进行伪造请求进行登陆。

这时我们可以使用 salt, 把 salt 到密码的前、中、后部分,再加密,这样只要攻击者没有原始密码或你的 salt 与其他网站不同就能保证相对安全。还可以将 salt 分开、倒序、hash后插入。

例如:

SHA3(SHA3(password) + salt);

另外还有 pbkdf2bcrypt 算法,它是一种适应性加密算法,可以根据需要设置迭代强度,以防止日益增长的电脑性能。 bcrypt(password, salt, cost) cost 是毫秒。攻击者就需要花更长的时间。

Hmac 密钥散列消息认证码(Keyed-hash message authentication code)

类似加盐处理,HMAC 运算利用 Hash 算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。HMAC 也可以有效防止一些类似md5的彩虹表等攻击,比如一些常见的密码直接MD5存入数据库的,可能被反向破解。因为除了密码,还有一个可以是任意值的 key。相同密码,不同的 key 生成的值是不一样的。

看一下用法:

var CryptoJS = require('crypto-js');

function run(hashs, txt, key) {
  hashs.forEach(name => hmacAlgorithm(name, txt, key);)
}

function hmacAlgorithm(algorithm, txt, key) {
  algorithm = algorithm.toUpperCase();
  console.time(algorithm);
  console.log(algorithm, CryptoJS['Hmac'+algorithm](txt, key).toString());
  console.timeEnd(algorithm);
}

var algs = ['md5', 'sha1', 'sha256', 'sha512', 'sha3', 'sha384', 'ripemd160'];
run(algs, 'robin', 'abc');
run(algs, 'robin', 'adaslfjklsjdsafdasflsjfklasdhgkjashdfkasdklyreoiwqurenwmehrqoewnfknoineclwqnrewqmewqlrnewqfeqrbkwviwbqovbdpcsabveiwbqvibwvwibqvkbwibvkbwibvfldsalfjdklsaflkausld');
// short key

// MD5 97ddf30a37c08700f6b8668b1b6cc4e5
// MD5: 4.226ms
// SHA1 131e69c8d75cf3f5735c4320386f35dc93da2c89
// SHA1: 2.286ms
// SHA256 4890c6579ce63619e31452ce80d1f917143987dfc131c0f61c195066b62753e3
// SHA256: 1.063ms
// SHA512 64c34aff6f972dc8bef245c03198e8f3a7c9bda7bc208f01793fd03a93d89db0ca0d0235615c976d5ff331425adae7a5812c8d42b5bff30ab2e95683ad06eed8
// SHA512: 1.937ms
// SHA3 27dc845b20795d64d25feabcac3d4f90297f32947780886bc16f4b18de850b7ac7d5bd021788d7b17fd4ad7da06cdadf650b7bfdb916394881902759b96dc8a6
// SHA3: 3.293ms
// SHA384 cfe9ad71dc48deb9a778a68c8106370ead173e13fbcb77160a75ac190d2a4e201f962811d3c439f241892d4c69e604f1
// SHA384: 2.487ms
// RIPEMD160 30de5538bfb0063ee42da27cdffe1c9f11f7acd0
// RIPEMD160: 1.327ms

//  ****long key
// MD5 6c6d9995b33e4b7c992201bf54add3c4
// MD5: 0.244ms
// SHA1 b71857c24215ab762033cf48a39b385eb5e299f6
// SHA1: 0.246ms
// SHA256 7a9cbb46c0506bf52051c32f487142696316831af3d7af258389b9d83249660e
// SHA256: 0.280ms
// SHA512 05d1928b5a977cfe0a1e016e193f5f9c0e7ea7243e4b233d9a77508ec9ee18c4834485eb1a8a7803f8c8eb04a7b03df390e7bbf677c57d989c2c0101b10433f1
// SHA512: 0.750ms
// SHA3 940fbf3853c76c70e0f50577bbc8b8252eee3d4404049e2bb2106c01419c1aa5b84a46ff0a6eedb699b3b1508e9d14915aceaaa3b037a04f6916c0c1f4e5f84c
// SHA3: 1.589ms
// SHA384 757946847447f8f52ade051b64cc1ad5c475c356a356cca7c81f36238ab4fce7c3af7fb69fc23eb4039aa7c7f20a077d
// SHA384: 2.396ms
// RIPEMD160 b92809af726d17aad164a128a19643a388213aa3
// RIPEMD160: 0.596ms

可以看到长短 key 算法不同,性能也不同,一般推荐 key 长度大于 哈希值长度。Node 原生模块就不测了,性能只能更好。

加密与解密

上面的哈希算法严格意义上说不算加密算法,因为一般储存的也是密文,只是为了传输过程中或泄漏之后不被看到明文密码,或加大暴力破解的难度,从而降低风险。更高安全要求的就需要用到解密。可以分为对称加密和不对称加密。

  • 对称加密:加密和解密密钥相同。代表是 AES, 速度快,安全性高,最流行的加密算法之一,中文也叫 高级加密标准(Advanced Encryption Standard)。另外还有DES、3DES、Blowfish、IDEA、RC5、RC6
  • 非对称加密: 加密和解密密钥不同。代表是 RSA, 速度慢,安全性高;还有ECC(椭圆曲线加密算法)、ElGamal、背包算法、Rabin(RSA的特例)

加密与解密最重要的是密钥。

对称加密: (如何选择,就看性能和安全性的要求了)

const CryptoJS = require('crypto-js');

function run(hashs, ...args) {
  hashs.forEach(name => encrypt(name, ...args));
}

function encrypt(algorithm, txt, key) {
  const shasum = CryptoJS[algorithm];
  console.time(algorithm);
  console.log(algorithm, shasum.encrypt(txt, key).ciphertext.toString());
  console.timeEnd(algorithm);
}

var algs = ['AES', 'TripleDES', 'RC4', 'Rabbit', 'RabbitLegacy'];
run(algs, 'robin', 'reiki');

// output
// AES 4fe1199d6a3aee50fd285017ba841109
// AES: 5.589ms
// TripleDES 90374ffa15857553
// TripleDES: 1.739ms
// RC4 a9a8932b19
// RC4: 0.738ms
// Rabbit 49fa9bd558
// Rabbit: 1.063ms
// RabbitLegacy 6b8d025024
// RabbitLegacy: 1.022ms

客户端如何选择加密算法

对于客户端,由于源码可泄露,也就相当于客户端的加密方法可暴露。以及 http 可被劫持等不安全因素。普通的加密根本没什么用。但并不是说客户端不用加密,而是要求服务端必须再次加密,或客户端采用安全系数更高的加密方式。另外也推荐升级 https。

我也并不了解所有算法,但是可以看需求,看场景,抛开场景谈技术就是耍流氓。

首先是否需要解密,安全性要求如何,一般的就使用 hash 算法,要求高一点就用 hmac。

如果需要解密,还要考虑性能及密钥的安全性。

微信跳一跳为什么选 AES?

// 跳一跳 game.js 里的加密算法
e.encrypt = function(t, e) {
    var e = e.slice(0, 16), // sessionID 前16位
    i = n.default.enc.Utf8.parse(e), // base64 utf8
    r = n.default.enc.Utf8.parse(e), // 同上
    a = t;
    a = JSON.stringify(a);
    var o = n.default.AES.encrypt(a, i, { // AES 非对称加密,a为加密内容,i是密钥,
        iv: r, // iv参数
        mode: n.default.mode.CBC, // 加密模式,AES包含ECB,CBC,CFB等加密模式,这些模式除了ECB由于没有使用IV而不太安全
        padding: n.default.pad.Pkcs7 // 加密填充,块加密的一个要面临的问题就是如何填满最后一块
    });
    return o = o.toString()
};

其实这个方法是遵循小程序文档里的加密解密指导的: 用户数据的签名验证和加解密

首先分析上传游戏分数需要客户端加密,然后服务端解密提取分数记录。那就有对称加密和非对称加密两种。对于非对称加密,需要下发公钥,并且公钥和私钥必须配对,速度也慢,这对一个小游戏来说操作太麻烦。那就锁定对称加密。但如何传递密钥呢,所以它用了 sessionID 作为密钥,客户端和服务端都知道,并且可以每次登陆更新,安全性高。并且微信 API 使用了 https,防止一般抓包,安全性更高。

另外,对于源码,一般也会工程化构建,进行压缩,混淆。但对于源码会泄漏的情况下,又是用户本身主动抓包,还能看懂程序,游戏类型只是上传分数,这就出现了刷分的可能性。当然,分高有什么用呢,这本身只是娱乐。

0%