Which is the preferred way to initialize sqlite3_key?:
1) Via function:
const char* key = "BIGsecret";
sqlite3_key(db, key, strlen(key));
-or-
2) Via PRAGMA:
sqlite3_exec(db, "PRAGMA key = 'BIGsecret', NULL, NULL, NULL);
Once the key has been set, is the value encrypted or obfuscated somehow in RAM? How easy would it be to determine the key by inspecting the memory space?
Thanks!
-- Tito
All software is vulnerable one way or
another. I'm not being negative about SLCipher any more than I would
be with my own software.
> For example, would it be
> possible to set the key before accessing the database and setting it
> back to nil once finished? In other words, clear key and memset().
> Then, set the key again before accessing the database and so on.
> On iOS, I could even use the Keychain to store it permanently and read
> it back automagically.
The question is, can I set and clear the key
> (i.e. sqlite3_key(db, key, strlen(key)) and sqlite3_key(db, nil, 0)
> respectively) *while* the database connection is open? I'm assuming
> you're a contributor to SQLCipher... so if this feature is not
> implemented, would you consider adding it?
In this light, I've tried to address your points below.> For example, would it be
> possible to set the key before accessing the database and setting it
> back to nil once finished? In other words, clear key and memset().
> Then, set the key again before accessing the database and so on.This is possible - it's basically where I was going with the suggestion earlier to open the database, use it, and then close it when you are done. This is the safest way to do it because closing the database will ensure that everything is properly deallocated and wiped by SQLCipher.
I would recommend against calling sqlite3_key to overwrite the key in memory. The reason is that if you changed the key, and then accidentally touched the database while an invalid key were set, bad things could happen. In the best case the database handle would become unusable, in a slightly worse case you could get invalid data back out to the application, or worst case even database corruption.
That said, the main concern I have about this approach in general is that it just shifts the responsibility onto the application. In other words, what does the application do with the key when SQLCipher isn't using it?
Even if the SQLCipher doesn't have the key in memory, the application must if it will set the key before the database is used. In other words, unless an application will prompt the user to enter a password or key on every single operation, then as long as a database exists in a readable and writeable state, the key must exist in memory somewhere. We can move the key around, but it's sort of a shell game.
Further, to make it perform acceptably to set / unset the key on any sort of rapid schedule you would need to disable key derivation (provide a raw hex key to sqlcipher) or tune down key derivation (PRAGMA kdf_iter = x). The drawback is that while this would make setting the key faster, it could result in less security for the data at rest (i.e. the system could be more susceptible to brute force or dictionary attacks).> On iOS, I could even use the Keychain to store it permanently and read
> it back automagically.If you assume that the Keychain is secure, you could wire the application to pull the key out of it dynamically. But that key is still going to be in memory at that point. If another application is able to read the process memory it could be compromised just as easily.
If the developer chose to store the data in the keychain and instructed the system to make the stored credentials available even when the device is locked by setting the accessibility to kSecAttrAccessibleAlways (cp. [App10a]), then the app is affected. Otherwise the credentials of the app are not affected by the attack method.
Also, as an aside, I think there still some debate about how secure keychain is on iOS (http://sit.sit.fraunhofer.de/studies/en/sc-iphone-passwords-faq.pdf / https://github.com/ptoomey3/Keychain-Dumper), at least depending on how an application and individual phone is setup.
The question is, can I set and clear the key
> (i.e. sqlite3_key(db, key, strlen(key)) and sqlite3_key(db, nil, 0)
> respectively) *while* the database connection is open? I'm assuming
> you're a contributor to SQLCipher... so if this feature is not
> implemented, would you consider adding it?In summary the preferred way of doing this with SQLCipher today would be to open/close the database as necessary. Using sqlite3_key to overwrite the key in a live connection is possible (though not with a nil value), but should be considered dangerous.
In the longer term, perhaps a good compromise solution would be to allow a application to register a callback that would provide the key when needed. This would allow an application to implement it's own measures for key protection and/or obfuscation, while minimizing potentially incompatible changes in the SQLCipher core. We'd still need to be careful though, because this could have some potentially significant impacts on performance, and we'd need to make sure the system could fail safe if a key was unavailable. We'll investigate this further as a potential enhancement in the new year.Thanks, and Happy Holidays!Cheers,Stephen
The Keychain *is*secure. It all depends what mechanism the developer used to store and access the key.
After reading the latest version of the report, it seems to really depend on two things:1) The cracker must be in possession of the device2) The key must have been stored using the kSecAttrAccessibleAlways method
• Items are stored with a protection class that makes them only available when the device is unlocked. (see Section 2.14)
• A strong passcode of 6 alphanumeric digits is enforced (reduces the risk of brute-force attacks)
The standard simple code of 4 numeric digits would be brute-forced in less than
9 minutes. Based on the assumption that the counter for wrong tries in the iOS can be
bypassed, as it is not hardware-based. [BS11b] provides a tool for passcode bruteforce
attack.
2.18. What are the effects when no passcode is set? While being very convenient, this would eliminate the security provided by the iOS keychain. All entries, regardless of protection class, will be accessible with our attack.
The question is, can I set and clear the key
> (i.e. sqlite3_key(db, key, strlen(key)) and sqlite3_key(db, nil, 0)
> respectively) *while* the database connection is open? I'm assuming
> you're a contributor to SQLCipher... so if this feature is not
> implemented, would you consider adding it?In summary the preferred way of doing this with SQLCipher today would be to open/close the database as necessary. Using sqlite3_key to overwrite the key in a live connection is possible (though not with a nil value), but should be considered dangerous.I'll set it with an empty string then. See how it goes.
In the longer term, perhaps a good compromise solution would be to allow a application to register a callback that would provide the key when needed.
We get a lot of questions on this list about key management, embedding keys, vaulting keys externally, etc. The general recommendation is that applications / libraries should not store the key on the device or in the application code.
That said, like most things, there are tradeoffs involved. It seems like using the keychain to store the encryption key could negate some of the security of the data at rest, especially as level of security would be out of the hands of the application developer and could vary on a per-device basis. On the other hand, using the keychain could add a significant level of convenience by not requiring a secondary passphrase, and would minimize the amount of time the key is stored in memory. There is no perfect solution, so the right approach depends solely on the goals and the required security profile of the application.
I did a bit of code review. Calling sqlite3_key with an empty string will *not* immediately overwrite and clear the key in memory. This is because the key derivation doesn't occur at the time the key is set. Instead, it occurs on the first database operation after the key is set. This facility allows a user to change other settings that might affect derivation, like the number of KDF iterations, but it means that you'd actually need to try a database operation with a bad key to clear the correct key.In short, at this time, calling sqlite3_key will not have the effect you are looking for, and the only current way to do it without further code changes would be to close / open the database.In the longer term, perhaps a good compromise solution would be to allow a application to register a callback that would provide the key when needed.I still think this is the most feasible approach in the long run, and will continue to investigate in light of the previously mentioned considerations. Another alternative might be to dig into the v2 sqlcipher code to change the way the key data is extracted.
What if the function setting the key also performed a standard database call such as "select * from sqlite_master"? In other words:1) set the desired key (either a dummy or valid value)2) execute "select * from sqlite_master"This function would ignore the results, allowing SQLCipher to perform the key derivation and set the specified key. If an empty string is not valid, another dummy value could be used, such as "0x0000FFFF", "0x0F0F0F0F" or anything else. Would that work?