rubygems-trust / rubygems.org

The Ruby community's gem hosting service.
https://rubygems.org
MIT License
16 stars 2 forks source link

Alternative Implementation Proposal #4

Closed danielknell closed 11 years ago

danielknell commented 11 years ago

Embedded Signatures

Attempting to fix gem / rubygems itself in a way that could also allow repositories to sign gems (rubygems,org is not the only player involved here, any changes will be inflicted on other providers and risks splitting the project)

gem / include "rubygems"

Only installing gems from trusted sources.

A signatures.tar.gz is added to each gem archive, this will contain signed manifests of the files within the archive.

A manifest is a serialised ruby hash containing the name of the signer, the key that they signed with, and a hash of filenames to their plain sha1 digests.

{
    :name => 'Daniel Knell', 
    :key => '70096AD1',
    :files => {
        'data.tar.gz' => 'adc83b19e793491b1c6ea0fd8b46cd9f32e592fc',
        'metadata.gz' => 'f1d2d2f924e986ac86fdf7b36c94bcdf32beec15' 
    }
}

A manifest filename is the short key id of the users public key (e.g. 70096AD1) and is accompanied by the RSA SHA1 signature of the manifest.

At install time, the signatures archive is opened, and the the archive is searched for a trusted key, when found it is used to verify the manifest against the signature file and the hashes in the manifest are used to verify the remaining files.

When a key is not found, the user will be given a list of keys present, along with associated metadata, and asked if they would like to trust any of the keys, if no trusted key is available the installation fails.

Each user can manage their list of trusted keys.

bundler

Secure production installs

Gemfile should allow an optional :keys option for specifying keys trusted for that gem.

Gemfile.lock should specify the key used when installing the gem as well as its version.

rubygems.org / geminabox / etc

Authorising all gems in a repository.

A gem server may choose to sign all gems it hosts using its own private key, this allows users to trust the gem server and therefore install all gems it hosts,

The ifs and hows of signing packages will vary depending on the server.

rubygems.org signing

Automatic signing of gems

Rubygems.org may choose to implement a signing server that upon submission of a new gem will be passed to a signing server which will verify the credentials of the gems author, sign the gem itself, and then upload it / approve submission to the index.

This signing server should not be publicly accessible and be locked down tightly to protect the root certificate.

There are many ways to do this, but the first step should be getting gems signed and checks added to gem / bundle executables.

Issues addressed

man in the middle attacks / tampering with gems after upload

the signatures should prevent anyone staging a man in the middle attack unless the gem is compromised by someone the user has already trusted.

allow a gem to be revoked

gpg has key revocation built into the system and gem can be removed from index

warn on insecure downloads

the signature failures result in an error unless a --insecure argument is passed

verify gem owners identity

the multiple signatures allow a gem to be signed by individuals, organisations, etc to show they vouch for it.

strict security

users can remove rubygems.org certificate and add individual keys instead,

Implementation Details

The hpk protocol for retrieving the public keys is trivial to implement, and the openssl gem has the means of generating a the RSA signatures which is already a dependency of rubygems

dstufft commented 11 years ago

The problem with this proposal as written is that RubyGems.org is trusted wholesale as the source of authentication. If RubyGems.org has been compromised there is no warning here as they'll just replace the manifest files and everyone will continue along installing gems without any sort of error being raised.

danielknell commented 11 years ago

If the signing process and the index are kept separate, this risk can be mitigated, but there is no getting around that problem, and if you are concerned about rubygems.org being compromised then you would remove its keys and only trust the keys of the users/groups producing the gems.

the signing infrastructure should live on a separate box to the index, and there could be some lag in the uploading of keys and their distribution to the signing box, with the signing boxes kept non-web accessable the risk should be mitigated enough to keep rubygems keys secure.

the rubygems.org key is also more to ensure that a gem came from them and has not been tampered with since being uploaded, the authors key which has nothing to do with rubygems.org can be used for more fine grained authorisation.

m-o-e commented 11 years ago

It seems this scheme trusts the repo-key unconditionally? (If gem is signed by rubygems.org -> accept)

What happens when an attacker hacks rubygems.org and replaces the rails-gem with his own creation (properly signed by the repo)? Which part of the proposed scheme prevents this or raises an alert on the user-side?

danielknell commented 11 years ago

it would fail the signature check for the rubygems.org cert, if the possibility of ruby-gems being completely compromised is unacceptable, then the user should be using stricter control and trusting the individual signers or some master keys for the rails project, multiple signatures allows you to pick which user or organisations you trust and rely on their keys.

rubygems.org signing anything would be a mere convenience step to bring the experience closer to the current setup, the ideas to get GPG signing and vouching for a gem working and then other processes can be built on top of that, be it something similar to say apt repositories as mentioned above where the repo is trusted, or on a case by case basis like ssh.

m-o-e commented 11 years ago

if the possibility of ruby-gems being completely compromised is unacceptable

But isn't that the exact situation that we're currently in and seeking to prevent in the future? If a fully automatic signature from the gemserver is to be trusted - then why sign at all?

I agree with most other aspects of the proposal, but this one seems to sabotage the whole thing (imho we should not start insecure by default).

danielknell commented 11 years ago

no, we currently have it partially compromised and no route to recovery, as long as signing infrastructure is kept separate from the index, then compromised s3 account would be useless (signature check would fail and gem would not install), likewise a compromised private key without a compromised index would be equally useless (gems can be signed but not distributed to users), and because the gems will have multiple signatures, if this is still unacceptable, you can authorise individual authors instead, which i suspect anyone security conscious would do by default.

building a system that is invulnerable to attack is impossible, the question is how secure is secure enough, and there are many ways that the index could sign a gem, the one above is just a suggestion, you could have automatic signing of everything, and a separate team to audit and sign the most popular gems manually with a further certificate to give them the nod.

I would be happy to run initially without the rubygems.org signing infrastructure, and just force people to trust the certs of projects and authors, we can think about how to make index wide authorisation secure later but regardless of which way we go but having every gem signed makes a man in the middle attack hard without compromised certificates, and regardless of what is done, be it a CA, this, or anything else a central cert for rubygems.org will always be a problem, when i say completely compromised i am thinking of a breach many orders of magnitude greater than the current one, which although had a large fallout, was actually fairly small in the grand scheme of things.

m-o-e commented 11 years ago

as long as signing infrastructure is kept separate from the index, then compromised s3 account would be useless

I disagree. Correct me if I'm wrong but as I understand it in your proposed scheme the signing infrastructure will automatically sign any gem presented to it, right?

Consequently when an attacker can overwrite files on S3, he could rebuild the rails-gem with his own author-key, have the signing-server auto-sign it, and overwrite the file on S3. He does not need to own the signing-server to modify a gem on S3 undetected.

likewise a compromised private key without a compromised index would be equally useless

How does the private key suddenly become useless when stolen?

I would be happy to run initially without the rubygems.org signing infrastructure

With that I would agree. Start paranoid by default and then see how more convenience can be added.

danielknell commented 11 years ago

The idea is that the signing server sign things uploaded via valid credentials, I don't have the time at the moment to spec out any such system adequately, hence the vagueness, but I'm inclined to think it should probably come later, get gems in general secure first and safe from the basic attacks, then worry about rubygems.org.

The comment about the private key was that unless you have both the repos private key, and some means of replacing the gem file a user downloads (MITM or access to filesystem) then you cant stage an attack, you need to compromise two systems in order to mount an attack on a user.

I am also less concerned with a rouge gem tanking a developers system as I am a production environment, if the Gemfile.lock lists locks to the key a gem was authorised by on the developer machine as well as the version then bundler can make sure the exact same gem is installed on production environments, regardless of MITM/rouge uploads/etc, and if a gem is for-instance dialling home form my laptop I will see it due to firewalls and such and can investigate / report.