在 C++ 中模糊敏感字符串的技术
我需要在我的 C++ 应用程序中存储敏感信息(我想要保密的对称加密密钥)。简单的方法是这样做:
std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";
但是,通过 strings 进程(或任何其他从二进制应用程序中提取字符串的进程)运行应用程序将显示上述字符串。
应该使用什么技术来隐藏此类敏感数据?
编辑:
好吧,几乎所有人都说过“你的可执行文件可以被逆向工程”-当然!这是我的一个小烦恼,所以我要在这里咆哮一下:
为什么这个网站上 99%(好吧,也许我夸大了一点)所有与安全相关的问题都是用一大堆的答案来回答的“没有可能的方法来创建一个完全安全的程序” - 这不是一个有用的答案!安全性是一种介于完美可用性和无安全性以及完美安全性但无可用性之间的滑动标尺。
关键是,您可以根据您想要执行的操作以及软件运行的环境来选择您在滑动标尺上的位置。 我不是在为军事设施编写应用程序,而是为家用电脑编写应用程序。我需要使用预先知道的加密密钥在不受信任的网络上加密数据。在这些情况下,“通过默默无闻实现安全”可能就足够了!当然,有足够时间、精力和技能的人可以对二进制文件进行逆向工程并找到密码,但你猜怎么着?我不在乎:
我实施一流安全系统所花费的时间比破解版本造成的销售损失更昂贵(不是我实际上在卖这个,但你明白我的意思)。至少可以说,新程序员中这种“让我们用绝对最好的方式来做”的编程趋势是愚蠢的。
感谢您花时间回答这个问题 - 他们非常有帮助。不幸的是,我只能接受一个答案,但我已经投票了所有有用的答案。
I need to store sensitive information (a symmetric encryption key that I want to keep private) in my C++ application. The simple approach is to do this:
std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";
However, running the application through the strings
process (or any other that extracts strings from a binary app) will reveal the above string.
What techniques should be used to obscure such sensitive data?
Edit:
OK, so pretty much all of you have said "your executable can be reverse engineered" - of course! This is a pet peeve of mine, so I'm going to rant a bit here:
Why is it that 99% (OK, so perhaps I exaggerate a little) of all security-related questions on this site are answered with a torrent of "there is no possible way to create a perfectly secure program" - that is not a helpful answer! Security is a sliding scale between perfect usability and no security at one end, and perfect security but no usability at the other.
The point is that you pick your position on that sliding scale depending on what you're trying to do and the environment in which your software will run. I'm not writing an app for a military installation, I'm writing an app for a home PC. I need to encrypt data across an untrusted network with a pre-known encryption key. In these cases, "security through obscurity" is probably good enough! Sure, someone with enough time, energy and skill could reverse-engineer the binary and find the password, but guess what? I don't care:
The time it takes me to implement a top-notch secure system is more expensive than the loss of sales due to the cracked versions (not that I'm actually selling this, but you get my point). This blue-sky "lets do it the absolute best way possible" trend in programming amongst new programmers is foolish to say the least.
Thank you for taking the time to answer this question - they were most helpful. Unfortunately I can only accept one answer, but I've up-voted all the useful answers.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
基本上,任何有权访问您的程序和调试器的人都可以并且如果愿意的话,将会在应用程序中找到密钥。
但是,如果您只是想确保在二进制文件上运行
strings
时密钥不会显示,您可以例如确保密钥不在可打印范围内。使用 XOR 隐藏密钥
例如,您可以使用 XOR 将密钥拆分为两个字节数组:
如果您创建的 key1 与
key
具有相同的字节长度,则可以使用 (完全)随机字节值,然后计算key2
:您可以在构建环境中执行此操作,然后仅将
key1
和key2
存储在您的应用程序中。保护您的二进制文件
另一种方法是使用工具来保护您的二进制文件。例如,有多种安全工具可以确保您的二进制文件被混淆并启动其运行的虚拟机。这使得调试变得困难,并且也是许多商业级安全应用程序(还有,恶意软件)受到保护的常规方式。
最重要的工具之一是 Themida,它在保护二进制文件方面做得非常出色。它经常被 Spotify 等知名程序用来防止逆向工程。它具有防止在 OllyDbg 和 Ida Pro 等程序中进行调试的功能。
还有一个更大的列表,可能有些过时了,保护二进制文件的工具< /a>.
其中一些是免费的。
密码匹配
这里有人讨论了哈希密码+盐。
如果您需要存储密钥以将其与某种用户提交的密码进行匹配,则应使用单向哈希函数,最好将用户名、密码和盐结合起来。但这样做的问题是,您的应用程序必须知道盐才能执行单向操作并比较生成的哈希值。因此,您仍然需要将盐存储在应用程序中的某个位置。但是,正如 @Edward 在下面的评论中指出的那样,这将有效地防止使用彩虹表等字典攻击。
最后,您可以结合使用上述所有技术。
Basically, anyone with access to your program and a debugger can and will find the key in the application if they want to.
But, if you just want to make sure the key doesn't show up when running
strings
on your binary, you could for instance make sure that the key is not within the printable range.Obscuring key with XOR
For instance, you could use XOR to split the key into two byte arrays:
If you create key1 with the same byte-length as
key
you can use (completely) random byte values and then computekey2
:You can do this in your build environment, and then only store
key1
andkey2
in your application.Protecting your binary
Another approach is to use a tool to protect your binary. For instance, there are several security tools that can make sure your binary is obfuscated and starts a virtual machine that it runs on. This makes it hard(er) to debug, and is also the convential way many commercial grade secure applications (also, alas, malware) is protected.
One of the premier tools is Themida, which does an awesome job of protecting your binaries. It is often used by well known programs, such as Spotify, to protect against reverse engineering. It has features to prevent debugging in programs such as OllyDbg and Ida Pro.
There is also a larger list, maybe somewhat outdated, of tools to protect your binary.
Some of them are free.
Password matching
Someone here discussed hashing password+salt.
If you need to store the key to match it against some kind of user submitted password, you should use a one-way hashing function, preferrably by combining username, password and a salt. The problem with this, though, is that your application has to know the salt to be able to do the one-way and compare the resulting hashes. So therefore you still need to store the salt somewhere in your application. But, as @Edward points out in the comments below, this will effectively protect against a dictionary attack using, e.g, rainbow tables.
Finally, you can use a combination of all the techniques above.
有一个(非常轻)只有标头的项目 obfuscate 由 adamyaxley 制作,效果非常好。它基于 lambda 函数和宏,并在编译时使用 XOR 密码对字符串进行加密。如果需要,我们可以更改每个字符串的种子。
以下代码不会在编译的二进制文件中存储字符串“hello world”。
我已经使用 c++17 和 Visual Studio 2019 进行了测试,并通过 IDA 检查,确认该字符串已隐藏。与 ADVobfuscator 相比,一个宝贵的优势是它可以转换为 std::string (同时仍然隐藏在编译后的二进制文件):
There is a (very light) header-only project obfuscate made by adamyaxley that works perfectly. It is based on lambda functions and macros and it encrypts strings litteral with a XOR cipher at compile-time. If needed, we can change the seed for each string.
The following code will not store the string "hello world" in the compiled binary.
I have tested with c++17 and visual studio 2019, and check via IDA and I confirm the string is hidden. One precious advantage compared to ADVobfuscator is that it is convertible to a std::string (while being still hidden in the compiled binary) :
首先,要意识到你无法阻止足够坚定的黑客,而且周围有很多这样的黑客。每个游戏和主机的保护最终都会被破解,所以这只是一个临时修复。
您可以做四件事来增加您隐藏一段时间的机会。
1)以某种方式隐藏字符串的元素——一些明显的东西,比如将字符串与另一个字符串进行异或(^运算符),就足以使字符串无法被搜索。
2)将字符串分割成几部分——分割字符串并将其弹出到奇怪模块中奇怪命名的方法中。不要让搜索和查找包含该字符串的方法变得容易。当然,某些方法必须调用所有这些位,但这仍然使它变得有点困难。
3) 永远不要在内存中构建字符串——大多数黑客使用的工具可以让他们在编码后看到内存中的字符串。如果可能的话,请避免这种情况。例如,如果您要将密钥发送到服务器,请逐个字符地发送,这样整个字符串就不会存在。当然,如果您通过 RSA 编码之类的方式使用它,那么这就更棘手了。
4) 执行一个临时算法——除此之外,添加一两个独特的变化。也许只需在您生成的所有内容中添加 1,或者进行两次加密,或者添加糖。这只会让黑客变得更加困难,因为他们已经知道当有人使用普通 md5 散列或 RSA 加密时要寻找什么。
最重要的是,请确保您的密钥何时被发现(如果您的应用程序变得足够流行,那么将会是何时),这并不是太重要!
First of all, realise that there is nothing you can do that will stop a sufficiently determined hacker, and there are plenty of those around. The protection on every game and console around is cracked eventually, so this is only a temporary fix.
There are 4 things you can do that will increase you chances of staying hidden for a while.
1) Hide the elements of the string in some way -- something obvious like xoring ( the ^ operator) the string with another string will be good enough to make the string impossible to search for.
2) Split the string into pieces -- split up your string and pop bits of it into strangely named methods in strange modules. Don't make it easy to search through and find the method with the string in it. Of course some method will have to call all these bits, but it still makes it a little harder.
3) Don't ever build the string in memory -- most hackers use tools that let them see the string in memory after you have encoded it. If possible, avoid this. If for example you are sending the key off to a server, send it character by character, so the whole string is never around. Of course, if you are using it from something like RSA encoding, then this is trickier.
4) Do an ad-hoc algorithm -- on top of all this, add a unique twist or two. Maybe just add 1 to everything you produce, or do any encryption twice, or add a sugar. This just makes it a little harder for the hacker who already knows what to look for when someone is using, for example, vanilla md5 hashing or RSA encryption.
Above all, make sure it isn't too important when (and it will be when if you application becomes popular enough) your key is discovered!
我过去使用的策略是创建一系列看似随机的角色。您首先插入,然后通过代数过程定位您的特定字符,其中从 0 到 N 的每一步都会产生一个数字 <包含混淆字符串中下一个字符的数组的大小。 (这个答案现在感觉很混乱!)
示例:
给定一个字符数组(数字和破折号仅供参考)
和一个前六个结果为:3,6,7,10,21,47的方程
将产生单词“你好!”从上面的数组中。
A strategy i've used in the past is to create an array of seemingly-random characters. You initially insert, and then locate your particular characters with a algebraic process where each step from 0 to N will yield a number < size of the array which contains the next char in your obfuscated string. (This answer is feeling obfuscated now!)
Example:
Given an array of chars (numbers and dashes are for reference only)
And an equation whose first six results are: 3, 6, 7, 10, 21, 47
Would yield the word "HELLO!" from the array above.
我同意@Checkers,你的可执行文件可以进行逆向工程。
更好的方法是动态创建它,例如:
I agree with @Checkers, your executable can be reverse-engineered.
A bit better way is to create it dynamically, for example:
当然,将私有数据存储在交付给用户的软件中始终存在风险。任何受过充分教育(且专注)的工程师都可以对数据进行逆向工程。
话虽如此,您通常可以通过提高人们泄露您的私人数据所需克服的障碍来使事情足够安全。这通常是一个很好的妥协。
在您的情况下,您可以使用不可打印的数据使字符串变得混乱,然后在运行时使用简单的辅助函数对其进行解码,如下所示:
Of course, storing private data in software which is shipped to the user is always a risk. Any sufficiently educated (and dedicated) engineer could reverse engineer the data.
That being said, you can often make things secure enough by raising the barrier which people need to overcome to reveal your private data. That's usually a good compromise.
In your case, you could clutter your strings with non-printable data, and then decode that at runtime using a simple helper function, like this:
我为字符串创建了一个简单的加密工具,它可以自动生成加密的字符串,并且有一些额外的选项可以做到这一点,例如:
作为全局变量的字符串:
带解密循环的 unicode 字符串:
作为全局变量的字符串:
I've created a simple encryption tool for strings, it can automatically generate encrypted strings and has a few extra options to do that, a few examples:
String as a global variable:
As unicode string with decryption loop:
String as a global variable:
正如 joshperry 指出的那样,这在某种程度上取决于您想要保护的内容。
根据经验,我想说,如果它是保护您的软件的某些许可计划的一部分,那么就不必费心了。他们最终会对它进行逆向工程。只需使用像 ROT-13 这样的简单密码来保护它免受简单攻击(在其上运行字符串的行)。
如果是为了保护用户敏感数据,我会质疑使用本地存储的私钥保护该数据是否是明智之举。这又取决于你想要保护什么。
编辑:如果你打算这样做,那么克里斯指出的技术组合将比 rot13 好得多。
Somewhat dependent on what you are trying to protect as joshperry points out.
From experience, I would say that if it is part of some licensing scheme to protect your software then don't bother. They will eventially reverse engineer it. Simply use a simple cipher like ROT-13 to protect it from simple attacks (line running strings over it).
If it is to secure users sensitive data I would be questioning whether protecting that data with a private key stored locally is a wise move. Again it comes down to what you are trying to protect.
EDIT: If you are going to do it then a combination of techniques that Chris points out will be far better than rot13.
正如之前所说,没有办法完全保护你的琴弦。但有一些方法可以以合理的安全性来保护它。
当我必须这样做时,我确实在代码中放入了一些看起来无辜的字符串(例如,版权声明,或一些伪造的用户提示或修复不相关代码的人不会更改的任何其他内容),并使用自身进行加密作为密钥,对其进行哈希处理(添加一些盐),然后使用结果作为密钥来加密我真正想要加密的内容。
当然,这可能会被黑客攻击,但确实需要坚定的黑客才能做到这一点。
As was said before, there's no way to totally protect your string. But there are ways to protect it with a reasonable safety.
When I had to do this, I did put some innocent looking string into the code (a copyright notice, for example, or some faked user prompt or anything else that won't be changed by someone fixing unrelated code), encrypted that using itself as a key, hashed that (adding some salt), and used the result as a key to encrypt what I actually wanted to encrypt.
Of course this could be hacked, but it does take a determined hacker to do so.
如果您使用 Windows 用户 DPAPI,http://msdn.microsoft.com/ en-us/library/ms995355.aspx
正如之前的文章所说,如果您使用的是 Mac,请使用钥匙串。
基本上,从安全角度来看,所有这些关于如何将私钥存储在二进制文件中的可爱想法都非常糟糕,因此您不应该这样做。任何人获得您的私钥都是一件大事,不要将其保存在您的程序中。根据您的应用程序的导入方式,您可以将私钥保存在智能卡上、代码与之对话的远程计算机上,或者您可以执行大多数人所做的操作并将其保存在本地计算机上非常安全的位置(“密钥”) store”,有点像一个奇怪的安全注册表),它受到权限和操作系统的所有强度的保护。
这是一个已解决的问题,答案不是将密钥保留在程序中:)
If you are on windows user DPAPI, http://msdn.microsoft.com/en-us/library/ms995355.aspx
As a previous post said if you are on mac use the keychain.
Basically all of these cute ideas about how to store your private key inside your binary are sufficiently poor from a security perspective that you should not do them. Anyone getting your private key is a big deal, don't keep it inside your program. Depending on how import your app is you can keep your private keys on a smart card, on a remote computer your code talks to or you can do what most people do and keep it in a very secure place on the local computer (the "key store" which is kind of like a weird secure registry) that is protected by permissions and all the strength of your OS.
This is a solved problem and the answer is NOT to keep the key inside your program :)
尝试这个。源代码解释了如何动态加密和解密给定 Visual Studio C++ 项目中的所有字符串。
Try this. The source code explains how to encrypt and decrypt on the fly all strings in a given Visual Studio c++ project.
我最近尝试的一种方法是:
part1
part2
code>part1 和
part2
part1
进行比较。它将验证私有数据的完整性。填充数据的宏:
假设私有数据有 4 个字节。我们为它定义一个宏,它以某种随机顺序保存带有赋值指令的数据。
现在,在需要保存
part1
和part2
的代码中使用此宏,如下所示:One method I recently tried is:
part1
part2
part1
andpart2
part1
. It will verify the integrity of private data.MACRO to populate data:
Suppose, private data is of 4 bytes. We define a macro for it which saves the data with assignment instructions in some random order.
Now use this macro in code where you need to save
part1
andpart2
, as follows:您可能希望向用户请求并通过外部 密码管理器,类似于 Mac OS X 钥匙串访问。
Instead of storing private key in your executable, you may want to request it from the user and store it by means of an external password manager, something similar to Mac OS X Keychain Access.
依赖于上下文,但您可以只存储密钥的哈希加上盐(常量字符串,容易混淆)。
然后,当(如果)用户输入密钥时,您添加盐,计算哈希并进行比较。
在这种情况下,salt 可能是不必要的,如果可以隔离哈希值,它可以阻止暴力字典攻击(Google 搜索也可以工作)。
黑客仍然只需在某处插入 jmp 指令即可绕过整个过程,但这比简单的文本搜索要复杂得多。
Context dependent but you could just store the hash of the key plus a salt (constant string, easy to obscure).
Then when (if) the user enters the key, you add the salt, calculate the hash and compare.
The salt is probably unnecessary in this case, it stops a brute-force dictionary attack if the hash can be isolated (a Google search has also been know to work).
A hacker still only has to insert a jmp instruction somewhere to bypass the whole lot, but that's rather more complicated than a simple text search.