我使用QString
来存储密码。如果更准确,我使用QString
来从GUI获取密码。
关键是在密码使用/设备之后,我需要使用密码使内部QString
个数据字节无效(零),以完全从内存中消除它。
以下是我的调查:
QString
销毁之后,它的数据在内存中保持非零; QString
以使用零来实现它时,它会触发写时复制习惯用法并为修改后的数据变量分配新内存。旧数据保持不变。即使我使用QString::data()
方法也是如此。不太确定原因 - 可能是因为它返回的不是原始char *
而是QChar *
; QString::clear()
,= ""
实际上与上述COW相同。问:如何实施正确的QString
清理以防止密码泄露?
答案 0 :(得分:4)
我有两种绕过写时复制的方法。我已经尝试了它们并且它们似乎工作 - 没有使用Qt Creator的内存查看器,但我的代码中使用的隐式共享QStrings都指向了之后相同的归零数据。
使用constData()
正如Qt docs for QString::data() method所述:
对于只读访问,constData()更快,因为它永远不会导致 发生深层复制。
所以可能的解决方案如下:
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:
隐式共享类可以控制其内部数据。在任何 修改其数据的成员函数,它会自动分离 在修改数据之前。 但请注意特殊情况 容器迭代器;请参阅隐式共享迭代器问题。
让我们转到描述this iterator problem的部分:
隐式共享对STL样式的迭代器有另一个影响:你 应避免在迭代器处于活动状态时复制容器 容器。 迭代器指向内部结构,如果你 复制一个容器你应该非常小心你的迭代器。 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.
*/
据我了解,基于上述文档片段,您应该能够利用这个&#34;错误的用法&#34;迭代器用迭代器操作原始字符串,因为它们不会触发写时复制。你&#34;拦截&#34;是非常重要的。在进行任何复制之前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
答案 1 :(得分:1)
您必须了解可能完成的所有临时副本。如果要避免这种情况,则必须在取消分配每个临时副本之前手动擦除内存。遗憾的是,由于实施已关闭,因此无法使用标准QString
。
但是,您可以使用自定义分配器来专门化std::basic_string
,在删除之前可以清理内存块(请参阅下面的示例)。如果您觉得更方便,可以使用这个新的安全字符串来操作密码而不是普通字符数组。我不确定std::basic_string
是否可以使用QChar
,但如果不是,您可以使用C ++ 11中的任何Unicode字符(char16_t
,{{1} } ...)而不是你需要ANSI支持。
关于用户界面,我认为你有一个选择是创建自己的文本输入小部件,重新实现char32_t
/ keyPressEvent
以将输入的密码存储到安全字符串或char数组中。同时重新实现keyReleaseEvent
以仅显示星号,圆点或其他任何您想要的遮蔽字符。使用密码后,只需清除阵列或清空安全字符串即可。
更新:安全字符串示例
paintEvent