Nullify QString数据字节

时间:2017-07-04 19:37:33

标签: c++ qt qstring

我使用QString来存储密码。如果更准确,我使用QString来从GUI获取密码。 关键是在密码使用/设备之后,我需要使用密码使内部QString个数据字节无效(零),以完全从内存中消除它。

以下是我的调查:

  • QString销毁之后,它的数据在内存中保持非零;
  • 当我尝试修改QString以使用零来实现它时,它会触发写时复制习惯用法并为修改后的数据变量分配新内存。旧数据保持不变。即使我使用QString::data()方法也是如此。不太确定原因 - 可能是因为它返回的不是原始char *而是QChar *;
  • QString::clear()= ""实际上与上述COW相同。

问:如何实施正确的QString清理以防止密码泄露?

2 个答案:

答案 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