我使用 QString
来存储密码。更准确地说,我使用 QString
从 GUI 获取密码。
重点是,在密码使用/设备之后,我需要用密码使内部 QString
的数据字节无效(零),以将其从内存中完全消除。
这是我的调查:
- 在
QString
销毁后,它的数据在内存中保持非零; - 当我尝试修改
QString
以用零填充它时,它会触发写时复制习惯用法并为修改后的数据变体分配新内存。旧数据保持不变。即使我使用QString::data()
方法也会发生同样的情况。不太确定为什么 - 可能是因为它返回的不是原始char *
而是QChar *
; QString::clear()
,= ""
实际上执行与上述相同的 COW。
问:如何实现适当的 QString
清理以防止密码泄露?
最佳答案
我有两种绕过写时复制的可能方法。我已经尝试过它们并且它们似乎工作 - 没有使用 Qt Creator 的内存查看器,但我的代码中使用的隐式共享 QString 之后都指向相同的归零数据。
使用 constData()
如 Qt docs for QString::data() method 中所写:
For read-only access, constData() is faster because it never causes a deep copy to occur.
所以可能的解决方案是这样的:
QString str = "password";
QString str2 = str;
QChar* chars = const_cast<QChar*>(str.constData());
for (int i = 0; i < str.length(); ++i)
chars[i] = '0';
// str and str2 are now both zeroed
这是 const_cast
的合法使用,因为底层数据并不是真正的 const
,所以这里没有未定义的行为。
使用迭代器
来自Qt docs for implicit sharing :
An implicitly shared class has control of its internal data. In any member functions that modify its data, it automatically detaches before modifying the data. Notice, however, the special case with container iterators; see Implicit sharing iterator problem.
那么让我们转到描述 this iterator problem 的部分:
Implicit sharing has another consequence on STL-style iterators: you should avoid copying a container while iterators are active on that container. The iterators point to an internal structure, and if you copy a container you should be very careful with your iterators. E.g.:
QVector<int> a, b;
a.resize(100000); // make a big vector filled with 0.
QVector<int>::iterator i = a.begin();
// WRONG way of using the iterator i:
b = a;
/*
Now we should be careful with iterator i since it will point to shared data
If we do *i = 4 then we would change the shared instance (both vectors)
The behavior differs from STL containers. Avoid doing such things in Qt.
*/
a[0] = 5;
/*
Container a is now detached from the shared data,
and even though i was an iterator from the container a, it now works as an iterator in b.
*/
据我了解,基于上述文档片段,您应该能够利用迭代器的这种“错误用法”来使用迭代器操作您的原始字符串,因为它们不会触发写时复制。在任何复制发生之前“拦截”begin()
和 end()
很重要:
QString str = "password";
QString::iterator itr = str.begin();
QString::iterator nd = str.end();
QString str2 = str;
while (itr != nd)
{
*itr = '0';
++itr;
} // str and str2 still point to the same data and are both zeroed
关于c++ - 使 QString 数据字节无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44913102/