As you are probably aware, KDE software uses the Qt library extensively. As do many other 3rd party applications.
Most of these applications use the Qt’s default text component –
QLineEdit
– when they need password input, because
QLineEdit
has a nice convenient mode where it masks the
content of the text field – it shows asterisks or circles instead of the
actual characters it contains.
This is a nice way to block over-the-shoulder snooping, and is a common approach to do password entry even in non-Qt software.
The underbelly
In order to see what the problem here is, we need to understand how
components like QLineEdit
work.
While the typed characters are not shown to the user, they are stored somewhere. Usually in a string variable as plain text (not encrypted). This means that is someone can see the contents of the process memory while the user types in the password, it will be easy to read it (problem 1).
Other problems are due to how dynamic memory works on our computers.
String data is stored (usually) in the dynamic memory. When
QString
wants to store its data, it will reserve a chunk of
memory and start filling it while the string grows (in this case, while
the user is typing the password). When that chunk is filled, a new
larger chunk is allocated and the data from the old chunk is copied into
it. The old chunk is freed/deleted so that some future memory
allocation can use the same space.
When the QLineEdit
is destroyed, so is the
QString
variable that stores the password. But, while the
buffer that QString
uses to store the data is
freed/deleted, its contents remain in memory until some other
dynamically allocated object is created in the same memory space and
overwrites the data. This is because QString
does not fill
its buffer with zeroes on destruction. This means that the passwords
remain in memory for much longer than needed (problem 2).
Furthermore, because of string reallocation on resizing (creating a bigger chunk of memory and copying the old data into it), it is quite possible that, while the user is typing the password, that the string will be resized/reallocated at least once. This means that partial passwords (data from the buffer in the string before reallocation which is not zeroed-out) will remain in memory until something else overwrites them (problem 3).
All this memory can end up written to the HDD/SSD if the application is moved to swap by the OS (problem 4).
Small potatoes
Before you throw your computer into the fireplace, and go to the tried and tested coding systems of (c)old, we need to see how big these problems really are.
Reading passwords stored in the process memory can be done from that same process. This is usually benign because if you’re entering a password into an application, you probably trust it with said password. This can only become a problem if that application has a plugin system (with native code) and you install untrusted plugins.
This should not be a common case. Usually, privacy-aware people do not use untrusted software.
Reading memory from a separate process is a different story. This is
not possible on a hardened system, but most Linux distributions tend to
be developer-friendly, and this means that they allow attaching a
debugger to a running process with PTRACE
. And a debugger
(or any process that pretends to be a debugger) can read the memory of
the process it is attached to.
Regardless of whether PTRACE
is enabled or not, for this
to be a problem, you need to be running untrusted software that could
try to extract the passwords from a process’ memory.
It is worth noting that even in the worst case scenario in which an
attacker is allowed to read the memory of your processes either
through a plugin or through PTRACE
, finding the password
(or other secret data) is like searching for a needle in a huge
haystack. Normal programs tend to be filled with strings.
Furthermore, if an attacker is allowed any of this, your system is seriously compromised.
Current improvements
I’ve started this post explaining 4 different problems of using
QLineEdit
for password input. I’m glad to say that two of
them do not exist anymore.
The latest version of Qt includes two patches (by yours truly) which
remove the problem of passwords remaining in the dynamic memory of a
process after the input component is destroyed. Namely, when
QLineEdit
is used for password input, it will fill the
memory where the password was stored with zeros. Furthermore, it will
preallocate a buffer for the password which is long enough so that
string reallocations don’t happen for 99.99% of users.
This means that problems 2 and 3 are no longer present if you are using the latest version of Qt.
The fourth problem – the operating system moving the application that asks you for the password to the swap should also not happen in common usage. Since problems 2 and 3 are gone, the only time when the password is in memory is while you’re typing it and the OS is not going to swap an application that you are currently using.
If you don’t think this is secure enough, you can either disable the swap on your system or encrypt it.
The last problem
The last remaining problem is that the password is stored non-encrypted in memory. Luckily, thanks to the previously mentioned patches, this is a much smaller issue than it seems at first.
The password is in memory only while it is being entered/used. Which means that the window that the attacker has to try to get it is extremely narrow.
Unfortunately, this problem can not be fixed completely. If the password was kept in memory encrypted, the encryption key would need to be readily available (in memory) for the application to be able to use the password. Which means that while your password would be encrypted, the key to decrypt it wouldn’t.
So, the attacker could try to find the encryption key, then find the encrypted password, and then decrypt the password.
Now, this would be much harder to do than finding an unencrypted password. The attacker would need to find two separate things (located in different parts of the process memory) and would not be able to use any type of string analysis when searching for the chunks of memory where the secrets are stored. The encryption key would be a true random set of bytes, and the encrypted password would look like a random set of bytes – they would not look like strings at all.
Unfortunately, this is not something that can be fixed in
QLineEdit
. This will need a custom password edit component
that uses a safe string implementation instead of
QString
.
To be continued…