RubyGems.org has little in the way of defenses against tampering. Right now, new gems could be uploaded to S3 and distributed to users worldwide without detection, and the only thing preventing this is the security of the AWS credentials presently stored on world-facing web servers. S3 Versioning is a step that could be taken immediately to reduce the severity of compromise, but a larger solution is required.
This document proposes a .gemsig
file as a means of verifying that a gem has not been modified during distribution. This file would be distributed alongside the .gem
files, allowing clients to verify authenticity.
While the exact file format is not relevant at this stage of discussion, in terms of requirements, a .gemsig
file should contain:
- The gem name and version number
- The size of the gem in bytes
- At least one cryptographic hash of the corresponding
.gem
In principle, the name of the gem and a single cryptographic hash of the .gem
should be sufficient. In practice, specifying multiple hashes as well as the length of the file assists in defending against collisions.
The information in the .gemsig
must be itself cryptographically signed. Given the current reliance on OpenSSL, PKCS#7 seems appropriate.
RubyGems.org should operate its own Certificate Authority. This CA should exist offline, signing keys for intermediate CAs as new intermediate CAs are required. This CA's certificate would be bundled with the rubygems
package and associated with the rubygems.org
source.
The rubygems.org web servers receive new gems on an ongoing basis. These new gems must be signed automatically, but they need not be signed by the rubygems.org web servers directly. Instead, incoming gems could be signed by a signing server.
A signing server would operate on separate physical infrastructure. It should expose a single action: sign this .gem
, returning its .gemsig
. Upon receipt of a gem, the signing server would check its own local database to ensure it had not already signed that gem + version combination. If this gem is indeed new, it would sign the gem using its own intermediate certificate, record that signing event, and return the .gemsig
.
The signing record must be as durable as possible. It may as well also be public. One option is to publish new signatures to an Amazon SNS topic, enabling any and all interested parties to receive a list of signatures as they happen.
Web servers must authenticate to the signing server, not just to control access, but to provide an audit trail in the event that a web server is compromised. This information should be included in the signing record. It might even make sense to eliminate the "signing record" as a separate step and instead recording all relevant information directly into the .gemsig
-- after all, .gemsig
s will be signed and widely-distributed already.
Whenever a client downloads a .gem
from a secured source, it must also download a corresponding .gemsig
file. A secured source must provide a .gemsig
for all .gem
s -- after all, an attacker who can return a malicious .gem
can also destroy (but not forge!) its corresponding .gemsig
.
rubygems.org
would be one such secured source with its expected certificate distributed as part of the rubygems
package. It should be possible for others to create their own .gemsig
s and configure their source to expect signatures descending from a particular set of keys.