我需要存储敏感信息在我的C ++应用程序(即我想保持私人对称加密密钥)。 简单的方法是这样:
std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";
但是,运行通过该应用程序strings
处理(或从二进制程序中提取字符串的任何其它)将揭示在上述字符串。
哪些技术应该被用来掩盖这样的敏感数据?
编辑:
OK,所以几乎所有的你说:“你的可执行文件可以进行反向工程” -当然! 这是我的一个忌讳,所以我要在这里咆哮了一下:
为什么是99%(好吧,也许我夸张了点)对本网站的所有与安全相关的问题都用“有创造一个完全安全的程序没有可能的方式”洪流回答 - 这是不是一个有用的回答! 安全性是完美的可用性和在一端没有安全,完善的安全性,但在没有其他可用性之间的滑动标尺。
问题的关键是,你选择你的取决于你想要做什么滑动的规模和在软件运行环境中的位置。 我不是在写一个应用程序的一个军事设施,我正在写一个应用程序的家用电脑 。 我需要的数据跨不受信任的网络与预先知道的加密密钥进行加密。 在这种情况下,“通过隐藏的安全”恐怕是不够好! 当然,有人有足够的时间,精力和技能可能反向工程二进制,找到密码,但你猜怎么着? 我不在乎:
它需要我来实现一流的安全系统的时间比销售损失更昂贵,由于破解版(不,我其实卖这个,但你明白我的意思)。 这个蓝色的天空“让这样做的绝对最好的方式”,在节目之间的趋势新的程序员是愚蠢的,至少可以说。
感谢您抽出时间来回答这个问题 - 他们是最有帮助的。 不幸的是我只能接受一个答案,但我已经上投票的所有有用的答案。
基本上,任何人都可以访问您的程序和调试器能够而且将会发现在应用的关键,如果他们想。
但是,如果你只是想确保运行时,关键不露面strings
上的二进制文件,你可以例如确保关键不是可打印范围内。
模糊键与XOR
例如,你可以使用XOR的关键分为两个字节数组:
key = key1 XOR key2
如果创建key1的具有相同字节长度的key
就可以使用(完全)随机字节的值,然后计算key2
:
key1[n] = crypto_grade_random_number(0..255)
key2[n] = key[n] XOR key1[n]
您可以在构建环境做到这一点,然后只存储key1
和key2
在应用程序中。
保护您的二进制
另一种方法是使用工具来保护您的二进制文件。 举例来说,有几个安全工具,可以确保您的二进制文件进行模糊处理,并启动它运行在虚拟机。 这使得它很难(ER)调试,而且还(也,唉,恶意软件)的保护convential方式很多商业级安全应用。
其中的首要工具是Themida ,这确实保护您的二进制文件的真棒工作。 它经常被用来通过众所周知的程序,如Spotify的,以防止逆向工程。 它具有的功能,以防止在调试诸如OllyDbg中和国际开发协会专业课程。
还有一个大名单,或许有些过时的工具来保护您的二进制文件 。
其中有些是免费的。
密码匹配
这里有人讨论散列密码+盐。
如果你需要存储的关键匹配它某种类型的用户提交的密码,你应该使用一个单向散列函数,preferrably通过结合用户名,密码和盐。 这样做的问题,不过,是你的应用程序必须知道盐可以做单向然后比较所产生的哈希值。 所以,所以你仍然需要存储一些盐在你的应用程序。 但是,正如@Edward在下面的评论指出,这将有效地使用,如彩虹表防止字典攻击。
最后,您可以使用上述所有技术的组合。
首先,认识到,有什么可以做,将停止足够决心的黑客,并有大量的周围的人。 在每场比赛和控制台周围的保护最终被破解,所以这只是一个临时的解决办法。
有4件事情可以做,这会增加你的隐藏住了一段时间的机会。
1)隐藏的字符串以某种方式中的元素 - 这是明显的像异或(^运算符)与另一个字符串的字符串将是不够好,使字符串无法搜索。
2)分割字符串成片 - 分手了它的字符串和流行位进入陌生的模块奇怪命名方法。 不要让您轻松进行搜索,找到与它的字符串的方法。 当然,有些方法将不得不把所有这些位,但它仍然使得它有点困难。
3)永远不要建在内存中的串 - 大多数黑客使用让他们看到了字符串在内存中已编码后的工具。 如果可能的话,避免这种情况。 例如,如果您要发送关闭到服务器的关键,通过文字把它的性格,所以整个字符串不舍。 当然,如果你使用它像RSA编码,那么这是棘手。
4)做一个特设的算法 - 在这一切之上,添加一个独特的风味或两个。 也许只是加1,你们的全部产品,或做任何加密两次,或添加糖。 这只是使它成为谁已经知道,寻找当有人使用的东西,例如,香草MD5哈希或RSA加密黑客有点困难。
首先,要确保这是不是太重要的,当(如果你的应用程序变得很流行,这将是时),您的密钥被发现!
我已经在过去使用的策略是创建看似随机的字符数组。 最初插入,然后用代数方法找到你的特定字符,其中从0各步骤至N将产生一个数<包含混淆的串中的下一个字符数组的大小。 (这个答案是现在的感觉混淆!)
例:
给定字符数组(数字和连仅供参考)
0123456789
----------
ALFHNFELKD
LKFKFLEHGT
FLKRKLFRFK
FJFJJFJ!JL
和一个方程,其前六个结果是:3,6,7,10,21,47
将产生单词“HELLO!” 从上面的阵列。
我@Checkers同意,你的可执行文件可以被反向工程。
好一点的方法是动态创建它,例如:
std::string myKey = part1() + part2() + ... + partN();
我创建的字符串简单的加密工具,它可以自动生成加密的字符串,并有一些额外的选项来做到这一点,有几个例子:
String作为一个全局变量:
// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
unsigned char myKey[48] = { 0xCF, 0x34, 0xF8, 0x5F, 0x5C, 0x3D, 0x22, 0x13, 0xB4, 0xF3, 0x63, 0x7E, 0x6B, 0x34, 0x01, 0xB7, 0xDB, 0x89, 0x9A, 0xB5, 0x1B, 0x22, 0xD4, 0x29, 0xE6, 0x7C, 0x43, 0x0B, 0x27, 0x00, 0x91, 0x5F, 0x14, 0x39, 0xED, 0x74, 0x7D, 0x4B, 0x22, 0x04, 0x48, 0x49, 0xF1, 0x88, 0xBE, 0x29, 0x1F, 0x27 };
myKey[30] -= 0x18;
myKey[39] -= 0x8E;
myKey[3] += 0x16;
myKey[1] += 0x45;
myKey[0] ^= 0xA2;
myKey[24] += 0x8C;
myKey[44] ^= 0xDB;
myKey[15] ^= 0xC5;
myKey[7] += 0x60;
myKey[27] ^= 0x63;
myKey[37] += 0x23;
myKey[2] ^= 0x8B;
myKey[25] ^= 0x18;
myKey[12] ^= 0x18;
myKey[14] ^= 0x62;
myKey[11] ^= 0x0C;
myKey[13] += 0x31;
myKey[6] -= 0xB0;
myKey[22] ^= 0xA3;
myKey[43] += 0xED;
myKey[29] -= 0x8C;
myKey[38] ^= 0x47;
myKey[19] -= 0x54;
myKey[33] -= 0xC2;
myKey[40] += 0x1D;
myKey[20] -= 0xA8;
myKey[34] ^= 0x84;
myKey[8] += 0xC1;
myKey[28] -= 0xC6;
myKey[18] -= 0x2A;
myKey[17] -= 0x15;
myKey[4] ^= 0x2C;
myKey[9] -= 0x83;
myKey[26] += 0x31;
myKey[10] ^= 0x06;
myKey[16] += 0x8A;
myKey[42] += 0x76;
myKey[5] ^= 0x58;
myKey[23] ^= 0x46;
myKey[32] += 0x61;
myKey[41] ^= 0x3B;
myKey[31] ^= 0x30;
myKey[46] ^= 0x6C;
myKey[35] -= 0x08;
myKey[36] ^= 0x11;
myKey[45] -= 0xB6;
myKey[21] += 0x51;
myKey[47] += 0xD9;
为Unicode字符串解密循环:
// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
wchar_t myKey[48];
myKey[21] = 0x00A6;
myKey[10] = 0x00B0;
myKey[29] = 0x00A1;
myKey[22] = 0x00A2;
myKey[19] = 0x00B4;
myKey[33] = 0x00A2;
myKey[0] = 0x00B8;
myKey[32] = 0x00A0;
myKey[16] = 0x00B0;
myKey[40] = 0x00B0;
myKey[4] = 0x00A5;
myKey[26] = 0x00A1;
myKey[18] = 0x00A5;
myKey[17] = 0x00A1;
myKey[8] = 0x00A0;
myKey[36] = 0x00B9;
myKey[34] = 0x00BC;
myKey[44] = 0x00B0;
myKey[30] = 0x00AC;
myKey[23] = 0x00BA;
myKey[35] = 0x00B9;
myKey[25] = 0x00B1;
myKey[6] = 0x00A7;
myKey[27] = 0x00BD;
myKey[45] = 0x00A6;
myKey[3] = 0x00A0;
myKey[28] = 0x00B4;
myKey[14] = 0x00B6;
myKey[7] = 0x00A6;
myKey[11] = 0x00A7;
myKey[13] = 0x00B0;
myKey[39] = 0x00A3;
myKey[9] = 0x00A5;
myKey[2] = 0x00A6;
myKey[24] = 0x00A7;
myKey[46] = 0x00A6;
myKey[43] = 0x00A0;
myKey[37] = 0x00BB;
myKey[41] = 0x00A7;
myKey[15] = 0x00A7;
myKey[31] = 0x00BA;
myKey[1] = 0x00AC;
myKey[47] = 0x00D5;
myKey[20] = 0x00A6;
myKey[5] = 0x00B0;
myKey[38] = 0x00B0;
myKey[42] = 0x00B2;
myKey[12] = 0x00A6;
for (unsigned int fngdouk = 0; fngdouk < 48; fngdouk++) myKey[fngdouk] ^= 0x00D5;
String作为一个全局变量:
// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
unsigned char myKey[48] = { 0xAF, 0xBB, 0xB5, 0xB7, 0xB2, 0xA7, 0xB4, 0xB5, 0xB7, 0xB2, 0xA7, 0xB4, 0xB5, 0xA7, 0xA5, 0xB4, 0xA7, 0xB6, 0xB2, 0xA3, 0xB5, 0xB5, 0xB9, 0xB1, 0xB4, 0xA6, 0xB6, 0xAA, 0xA3, 0xB6, 0xBB, 0xB1, 0xB7, 0xB9, 0xAB, 0xAE, 0xAE, 0xB0, 0xA7, 0xB8, 0xA7, 0xB4, 0xA9, 0xB7, 0xA7, 0xB5, 0xB5, 0x42 };
for (unsigned int dzxykdo = 0; dzxykdo < 48; dzxykdo++) myKey[dzxykdo] -= 0x42;
当然,在其中运到用户的软件存储私人数据始终是一个风险。 任何足够的教育(及专用),工程师可以逆向工程的数据。
话虽这么说,你可以经常做的事情通过提高人们需要克服透露您的私人数据的屏障足够安全。 这通常是一个很好的妥协。
在你的情况,你可以与非可打印的数据弄乱你的字符串,然后用一个简单的辅助功能,这样的解码,在运行时:
void unscramble( char *s )
{
for ( char *str = s + 1; *str != 0; str += 2 ) {
*s++ = *str;
}
*s = '\0';
}
void f()
{
char privateStr[] = "\001H\002e\003l\004l\005o";
unscramble( privateStr ); // privateStr is 'Hello' now.
string s = privateStr;
// ...
}
在某种程度上取决于你试图保护作为joshperry指出的是什么。 从以往的经验,我会说,如果是一些牌制度,以保护你的软件的一部分,那么不要打扰。 他们将eventially反向工程了。 只需使用简单的密码一样ROT-13,以保护它从简单的攻击(线串运行在其上)。 如果是确保用户的敏感数据,我会质疑是否保护数据与本地存储的私钥是明智之举。 同样它归结为你想保护的是什么。
编辑:如果你要做到这一点则是克里斯指出,将远远超过ROT13更好的技术组合。
正如前面所说的,有没有办法完全保护您的字符串。 但也有保护它滚回一个合理的安全的方式。
当我不得不这样做,我也把一些无辜的看着串入代码(版权声明,例如,或者一些伪造的,不会被人固定无关的代码进行更改用户提示或其他任何东西),加密,使用本身作为重点,即哈希(加点盐),以及使用的结果作为密钥来加密什么,我其实是想进行加密。
当然,这可能是遭到黑客攻击,但它确实需要一个确定的黑客这样做。
相反,在你的可执行文件中存储的私钥,您可能希望向用户请求,并将其存放通过外部手段密码管理器 ,类似于Mac OS X的钥匙串访问的东西。
如果您使用的是Windows用户DPAPI, http://msdn.microsoft.com/en-us/library/ms995355.aspx
由于以前的帖子说,如果你是在Mac上使用的钥匙串。
基本上所有的有关如何存储您的二进制文件内私钥这些可爱的想法是从安全角度来看,你不应该做他们足够差。 任何人都让你的私钥是一个大问题,不要把它你的程序中。 这取决于如何导入您的应用程序是你可以保持您的私人密钥的智能卡上,在远程计算机上的代码会谈,或者你可以做大多数人一样,并保持在一个非常安全的地方,在本地计算机上(“关键店”,这是一种像一个奇怪的安全注册表)是通过对权限和您的操作系统的所有的力量保护。
这是一个解决问题的答案是不是让你的程序中的关键:)
试试这个 。 源代码解释了如何加密,并在给定的Visual Studio C ++项目解密在飞行的所有字符串。
一种方法我最近尝试是:
- 乘坐私人数据的散列(SHA256)和代码填充它
part1
- 以私人数据的XOR和其散列和代码填充它
part2
- 填充数据:不要将其存储为字符STR [],但在使用运行时赋值指令填充(如下所示在宏)
- 现在,通过采取的XOR产生上运行时的私人数据
part1
和part2
- 附加步骤 :计算所生成的数据的哈希值,并将它与比较
part1
。 它会验证私密数据的完整性。
万家乐填充数据:
假设,私人数据是4个字节。 我们为它定义一个宏节省一些随机的顺序与分配指令的数据。
#define POPULATE_DATA(str, i0, i1, i2, i3)\
{\
char *p = str;\
p[3] = i3;\
p[2] = i2;\
p[0] = i0;\
p[1] = i1;\
}
现在使用代码这个宏,你需要保存part1
和part2
,如下所示:
char part1[4] = {0};
char part2[4] = {0};
POPULATE_DATA(part1, 1, 2, 3, 4);
POPULATE_DATA(part2, 5, 6, 7, 8);
上下文相关的,但你可以只存储密钥的散列加上盐 (字符串常量,容易混淆)。
然后,当(如果)用户输入键,添加盐 ,计算哈希和比较。
盐在这种情况下可能是不必要的,它停止蛮力字典攻击如果哈希可以被分离(谷歌搜索也已经知道工作)。
黑客仍然只需要插入的地方JMP指令绕过一大堆,但是这不是一个简单的文本搜索更加复杂。