Open paragonie-scott opened 8 years ago
Aside: If you could use libsodium, you could adopt what CMS Airship uses instead, which combines Ed25519 signatures (for authenticity), Merkle trees (for userbase consistency), and verifiable reproducibility to make infrastructure-based attacks miserable to pull off, and silent, targeted infrastructure attacks near-impossible.
Industry-standard assurance technologies such as HTTPS and code signing are clearly desirable to ensure the authenticity and integrity of code in an automatic update. However, these technologies depend on software libraries external to the CMS and PHP, so assurance is not guaranteed to be possible on all servers running the CMS. The existing automatic update mechanism in WordPress opts to treat such technologies as optional benefits rather than requirements of the automatic update system (Source). The lack of any serious problems arising in the wild as a result of this design provides empirical evidence that a design is favorable if it is capable of using security libraries when they are available, but is also capable of falling back to less rigorous methods so servers lacking these technologies are still updated.
Strongly disagreed. A self-updater that isn't secured is a great avenue for deploying Trojans from trusted infrastructure. You'll essentially be creating a remote code execution backdoor without mandatory HTTPS and cryptographic signatures.
Don't repeat WordPress's mistakes.
Thank you so much for reviewing what I have here, @paragonie-scott! Alexpott of drupal.org posted a link to the CMS Airship blog post mere hours before your feedback here. I was probably going to be trying to get in touch with the author of that post tonight begging for guidance on the crypto aspects, and then you beat me to it! In addition to your observations here I need to think more about making master and signing keys and a revocation mechanism happen...
HMAC was the best answer I came up with since I started looking into this a couple weeks ago, and I recognize the dependence on a shared secret is suboptimal. I definitely want to get it right the first time and not someday be responsible for hacked sites, so if there's a better way, by all means it should be used.
To that end I'm trying to look into choices 2 and 3,
2 Deterministic ECDSA over seck1p256 3 RSA (via phpseclib) with PSS padding, never PKCS1 padding
I note that you don't mention any particular ECDSA implementation, but do for RSA via phpseclib. In practice, should I interpret this to mean that I should use OpenSSL for ECDSA unless I have to fall back on phpseclib? (An interesting argument FOR phpseclib is that the CMS organization would be empowered to push fixes in it as well, for all sites, whereas the patch status of OpenSSL on a given site is a big question-mark.) I'm also not finding any information about the "over seck1p256" part?
Yeah sorry, libsodium's not possible, I don't want to propose changing Drupal's system requirements. I also want to keep at the forefront the sites that will benefit the most from automatic updates - little operations on shared hosts that are at the mercy of the php build found there, with no continuing support from the firm that created the site years ago.
I note that you don't mention any particular ECDSA implementation, but do for RSA via phpseclib. In practice, should I interpret this to mean that I should use OpenSSL for ECDSA unless I have to fall back on phpseclib?
I made that edit earlier because I wasn't absolutely sure that phpseclib offered deterministic ECDSA. However, I also wrote the wrong name down. You want secp256r1 not seck1p256 (which is actually named secp256k1). n.b. secp256r1 is also known as NIST P-256, which is what most people use for ECDSA.
OpenSSL does support ECDSA, but I don't know if it supports RFC 6979. If it does, I can't figure out how to access that feature from PHP. See https://3v4l.org/E1C9Y
Feel free to use:
OpenSSL's implementations shouldn't reuse nonces, so you probably don't need to care about these details.
Recommended reading: Cryptographic best practices.
I was probably going to be trying to get in touch with the author of that post tonight begging for guidance on the crypto aspects, and then you beat me to it!
Oh, by the way, I work for the company that published the referenced blog post. :)
Shameless plug: If you opt for RSA, we wrote a library that makes RSA easy to get right.
Two other asides,
Thanks
The person receiving the SFTP credentials would need to have a 2048-bit RSA private key dedicated to encryption. The corresponding public key would need to be used to encrypt the SFTP credentials before they're uploaded.
Encryption:
use ParagonIE\EasyRSA\EasyRSA;
use ParagonIE\EasyRSA\PublicKey;
$publicKey = new PublicKey($publicKeyAsPEMEncodedString);
$encrypted = EasyRSA::encrypt($sensitiveCredentials, $publicKey);
Decryption:
use ParagonIE\EasyRSA\EasyRSA;
use ParagonIE\EasyRSA\PrivateKey;
$privateKey = new PrivateKey($privateKeyAsPEMEncodedString);
$stored = EasyRSA::decrypt($encrypted, $privateKeyObject);
What you could do is: Encrypt the ciphertexts and then send them (over HTTPS!) to your central infrastructure. When a security-critical update is needed, run a script that decrypts each client's credentials. Make sure the private key is stored in a memory-mapped file rather than to disk, and shred
it after being run just to make sure.
Bonus: Use something like TAILS, which will run the entire OS in memory and leave nothing on the disk.
The risks here are reduced to the realm of "nation state attacker", against which most systems are hosed anyway.
Fortunately, such adversaries are both rare and their budgets typically mandate being used on targets of interest. (Unfortunately, WordPress, Drupal, and Joomla could be targets of interest to some advanced threats due to their market share alone.)
Despite its name, EasyRSA's encryption is actually a hybrid cryptosystem consisting of:
This is ideal for arbitrary-length ciphertexts.
(I can't make any commitments that conflict with my time obligations to PIE's clients, of course, but I don't think this will be a problem at all.)
It's possible I'll be able to give it careful review before it gets merged. A once-over won't be much of a challenge.
(I can't make any commitments that conflict with my time obligations to PIE's clients, of course, but I don't think this will be a problem at all.)
Understood. I may well be the only person from the Drupal community to have ever written any code towards automatic updates, and that was yesterday, so at this rate it will be some time before there's something to review. I certainly wouldn't expect a firm commitment of time at some far future point. Nevertheless, this sounds promising! I get the sense from the Paragon blog post's review of CMSs that open source CMS developers haven't been doing a lot of collaborating with cryptographic consultants historically. Perhaps we will be able to get it right this time.
Also worth mentioning: We wrote a comparison chart of various CMSes compared to the one we developed. It's obviously biased in that it only focuses on security features, but if you only compare the big three, it's rather informative.
Outside auto-updates (which is very important), there are a lot of other items that addressing now will save a lot of pain going forward. Using real prepared statements, rather than emulated prepares, would net you another green checkmark. :)
https://paragonie.com/blog/2016/06/php-security-platinum-standard-raising-bar-cms-airship
You may also find it worth chatting with @dd32, who was working on a similar goal for WordPress (but obviously has to contend with their minimum version requirements, so I don't know if he's found a clear way forward).
As an update for this, I'd like to recommend https://github.com/paragonie/sodium_compat for Ed25519 signatures for the automatic updates as well as crypto_box_seal()
for public-key encryption.
This recommendation hinges on it being successfully audited by an independent third party, as per this issue.
Thanks @paragonie-scott. I'm still working away at a standalone application for in-browser updates of whatever CMS you desire. The first goal is just manual/attended upgrades, since Drupal doesn't do that, but once that is done I'll be looking at automating update deployment again and circling back to the attendant security and crypto concerns.
Using real prepared statements, rather than emulated prepares, would net you another green checkmark.
We've got an open issue for that one at https://www.drupal.org/node/2348931.
FYI, sodium_compat 1.0 is now available, for both digital signatures and anonymous public-key encryption.
It hasn't been audited, but all attempts to get a third party audit led nowhere, so this is the best we can do for now.
Don't use HMAC here. Instead, use in order of preference:
The reason is simple: HMAC is a symmetric key message authenticity verification that, in order to verify, you must be able to generate signatures. If the Drupal installation can verify the HMAC, it must have the HMAC key. What prevents an attacker from obtaining the key and using it to forge an update containing a trojan?
It's the wrong tool for the job. (See this primer on core cryptography concepts for an in-depth explanation on which tools work for the job.)
In Drupal's case, you really can't depend on libsodium yet (because of the current installed userbase) but you certainly use phpseclib to solve the problem.
You can also use RSA/ECDSA keys to sign a Phar directly. We do this for random_compat.