views:

303

answers:

6

I have to write a Windows service that handles at some point confidential data (such as PIN codes, passwords, and so on). Those informations are needed for a very short amount of time: usually they are sent almost immediately to a smart card reader.

Lets consider this piece of code:

{
  std::string password = getPassword(); // Get the password from the user

  writePasswordToSmartCard(password);

  // Okay, here we don't need password anymore.
  // We set it all to '\0' so it doesn't stay in memory.
  std::fill(password.begin(), password.end(), '\0');
}

Now my concern is about compiler optimizations. Here the compiler might detect that password is about to be deleted and that changing its value at this point is useless and just remove the call.

I don't expect my compiler to care about the value of future-unreferenced memory.

Are my concerns legitimate ? How can I be sure that such a piece of code won't be optimized-out ?

+20  A: 

Yes, your concerns are legitimate. You need to use specifically designed function like SecureZeroMemory() to prevent optimizations from modifying your code behavior.

Don't forget that the string class should have been specifically designed for handling passwords. For example, if the class reallocates the buffer to hold a longer string it has to erase the buffer before retunring it to the memory allocator. I'm not sure, but it's likely std::string doesn't do that (at least by default). Using an unsuitable string handling class makes all your concerns worthless - you'll have the password copied all over the program memory befoe you even know.

sharptooth
Thanks. I just wonder how this function works: what prevents the compiler to optimize it out ?
ereOn
@ereOn: That's easy, its code is just not presented to the compiler when your program is compiled, so the compiler can't see it and decide that it "does nothing useful". For example, it can be compiled into a DLL already and only linked dynamically to.
sharptooth
@sharptooth: Makes sense, indeed. I will accept this as soon as I can ;) Do you have any links/tutorial about writing a safe password-handling class ?
ereOn
@ereOn: No, I've never seen one. If you decide to write your own - take care of two things - memory deallocation and string shortening. Erase memory before returning it to the memory manager and when the string is shortened erase the previously occupied part. I guess `std::string` can be enhanced for that. Maybe you even get good hints if you ask a separate question - whether it's possible to tweak `std:string` to make it secure.
sharptooth
`std::string` is just `std::basic_string<char, std::char_traits<char>, std::alloc>`. I'd think the allocator should be both the 'right' place and sufficient to handle securely zeroing any memory about to be freed.
Christopher Creutzig
@Christopher Creutzig Yes, allocation can be handled easily, but what about cases when the string is shortened without reallocating the buffer?
sharptooth
@sharptooth: The memory will be reallocated or freed when the string object is destroyed. The allocator will be asked to do either of those and can do whatever safe-zero operation the OS or runtime provides. Yes, shortening a string may result in some data lying around longer than necessary – but never as free memory that might be re-used. (What password string is shortened anyway?)
Christopher Creutzig
@sharptooth: STL-style allocators handle all allocation and deallocation (they don't have a specific resize operation, string/vector/etc. use allocate/copy/deallocate).
Roger Pate
@Christopher: std::allocator<char> for the third template argument.
Roger Pate
@Roger: Right, sorry for the typo.
Christopher Creutzig
+5  A: 

It's problematic, but for another reason. Who said that std::string password = getPassword(); doesn't leave yet another copy in the memory? (Probably you need to write a "secure" allocator class for this that zeros memory on "destruct" or "deallocate")

In your peace of code you can avoid optimization by getting a volatile pointer to the string data (I don't know if you can do it in standard way) and then zero the data through.

ybungalobill
Well, the `getPassword()` thing was just made up to write my example code. But you're absolutely right, one must care about this too.
ereOn
@ereOn Whatever way you generate the password you must be extremely careful in this case. The easiest way is to use a custom allocator that zeros deallocated memory by one of the above methods (volatile or SecureZeroMemory). Note that std::string doesn't destruct its elements, only deallocates.
ybungalobill
A: 

Declare password volatile to prevent the compiler making any assumptions about removing explicit read or writes.

volatile std::string password = getPassword(); // Get the password from the user
Clifford
I might be wrong but I believe `std::string` wasn't designed to be `volatile`: all of its method are not declared `volatile` so I won't be able to call them.
ereOn
@ereOn: Perhaps then the most secure solution is to use a C string where you have complete control of the memory used to store the string.
Clifford
Yep. If in the end, there is nothing I feel comfortable enough with, I will surely end up using a C string.
ereOn
A: 

Why dont you just disable optimization for the code in question?

#pragma optimize( "", off )

// Code, not to optimize goes here

#pragma optimize( "", on )

This sample of #pragma optimize is specific to MSVC, but other compilers support it as well.

RED SOFT ADAIR
A: 

In this specific instance, I'd be really surprised if the compiler can optimise away a method invocation that could clearly have side effects. Or is std::fill inline so the compiler can see the implementation? (I'm not a C++ programmer).

Having said that, this kind of thing can be a concern generally. But you need to think about how easy it is to exploit. To read the memory of another process, an attacker would need some level of administrator access (if not, why are you using that operating system). If the machine is compromised to that level, you've already lost.

JeremyP
I tend to agree. However, in my place, when a software crashes, a dump file is sent to a specific service or the developer himself. Even if those people can be trusted, If I can avoid confidential data to be stored somewhere, that's even better.
ereOn
@ereOn: I'm afraid that dump file, if it contains the image of the process, is itself a security risk. Nothing you do to wipe out sensitive data can protect you because the developer has access to the code and can easily subvert your security measures.
JeremyP
The developer can't change the code that runs under our production environment. If the code is well and safely designed, knowing how it work can't help to hack the data. And if you manage to remove all sensitive data from the dumps, those can't help either.
ereOn
I'd be surprised if there's a platform where `std::fill()` is _not_ inlined. In fact, I'd expect my implementation to fall back to some platform-specific intrinsic for this. And a compiler should certainly be able to optimize this.
sbi
Because std::fill is a template, it is available to be inlined. (Even with exported templates, removed in C++0x, something has to be available to generate the right code.)
Roger Pate
+3  A: 

Don't use std::string for passwords, as it doesn't zero out it's memory when doing reallocations or destruction - design your own ConfidentialString class instead. When designing that class, you might want to take advantage of CryptProtectMemory... and be very, very careful when you need to use the decrypted version, especially when calling external code.

snemarch
I wasn't aware of that function. Thanks, it will surely help.
ereOn
You don't have to invent your own string class from scratch, just your own [allocator](http://stackoverflow.com/questions/3785366/how-to-ensure-that-compiler-optimizations-dont-introduce-a-security-risk/3785401#3785401).
Roger Pate
@Roger Pate: you're probably still better off rolling your own - makes it easier to control what external APIs you can (and can't!) pass the password to, lets you keep the string encrypted internally, and avoid unintended copy construction. Besides, how often do you need to do string manipulation on a passphrase? :)
snemarch