layout | title | url | previous | next |
---|---|---|---|---|
default |
Security |
/security |
/publishing |
/ssl-certificate-update |
How to build and install cryptographically signed gems-- and other security concerns.
Security practices are being actively discussed. Check back often.
Installing a gem allows that gem's code to run in the context of your application. Clearly this has security implications: installing a malicious gem on a server could ultimately result in that server being completely penetrated by the gem's author. Because of this, the security of gem code is a topic of active discussion within the Ruby community.
RubyGems has had the ability to cryptographically sign
gems since version
0.8.11. This signing works by using the gem cert
command to create a key
pair, and then packaging signing data inside the gem itself. The gem install
command optionally lets you set a security policy, and you can verify the
signing key for a gem before you install it.
However, this method of securing gems is not widely used. It requires a number of manual steps on the part of the developer, and there is no well-established chain of trust for gem signing keys. Discussion of new signing models such as X509 and OpenPGP is going on in the rubygems-trust wiki, the RubyGems-Developers list and in IRC. The goal is to improve (or replace) the signing system so that it is easy for authors and transparent for users.
Install with a trust policy.
-
gem install gemname -P HighSecurity
: All dependent gems must be signed and verified. -
gem install gemname -P MediumSecurity
: All signed dependent gems must be verified. -
bundle --trust-policy MediumSecurity
: Same as above, except Bundler only recognizes the long--trust-policy
flag, not the short-P
. -
Caveat: Gem certificates are trusted globally, such that adding a cert.pem for one gem automatically trusts all gems signed by that cert.
Verify the checksum, if available
gem fetch gemname -v version
ruby -rdigest/sha2 -e "puts Digest::SHA512.new.hexdigest(File.read('gemname-version.gem'))
Know the risks of being pwned, as described by Benjamin Smith's Hacking with Gems talk
-
Create self-signed gem cert
cd ~/.ssh gem cert --build [email protected] chmod 600 gem-p*
- use the email address you specify in your gemspecs
- Configure gemspec with cert
Add cert public key to your repository
cd /path/to/your/gem
mkdir certs
cp ~/.ssh/gem-public_cert.pem certs/yourhandle.pem
git add certs/yourhandle.pem
Add cert paths to your gemspec
s.cert_chain = ['certs/yourhandle.pem']
s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
-
Add your own cert to your approved list, just like anyone else
gem cert --add certs/yourhandle.pem
-
Build gem and test that you can install it
gem build gemname.gemspec gem install gemname-version.gem -P HighSecurity
-
Example text for installation documentation
MetricFu is cryptographically signed. To be sure the gem you install hasn't been tampered with:
Add my public key (if you haven't already) as a trusted certificate
gem cert --add <(curl -Ls https://raw.github.com/metricfu/metric_fu/master/certs/bf4.pem)
gem install metric_fu -P MediumSecurity
The MediumSecurity trust profile will verify signed gems, but allow the installation of unsigned dependencies.
This is necessary because not all of MetricFu's dependencies are signed, so we cannot use HighSecurity.
require 'digest/sha2'
built_gem_path = 'pkg/gemname-version.gem'
checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path))
checksum_path = 'checksum/gemname-version.gem.sha512'
File.open(checksum_path, 'w' ) {|f| f.write(checksum) }
# add and commit 'checksum_path'
OpenPGP signing is not recommended due to lack of support.
For details, see discussion with Yorick Peterse.
If you spot a security vulnerability in someone else's gem, then you first step should be to check whether this is a known vulnerability. One way is by searching for an advisory on RubySec.
If this looks like a newly discovered vulnerability, then you should contact the author(s) privately (i.e., not via a pull request or issue on a public project) explaining the issue, how it can be exploited, and ideally offering an indication of how it might be fixed.
First, request a CVE identifier by emailing one of these places. This identifier will make it easy to uniquely identify the vulnerability when talking about it.
Second, work out what people who depend on your gem should do to resolve the vulnerability. This may involve releasing a patched version of your gem that you can recommend they upgrade to.
Finally, you need to tell people about the vulnerability. Currently there is no single place to broadcast this information but some good places to start might be to:
-
Send an email to several lists including [email protected], [email protected], and [email protected] outlining the vulnerability, which versions of your gem it affects, and what actions those depending on the gem should take. Make sure to use a subject that includes the gem name, some short summary of the vulnerability, and the CVE ID if you have one.
-
Add it to ruby-advisory-db. You can do this by following the CONTRIBUTING guidelines and submitting a pull request.
Several sources were used for content for this guide:
- How to cryptographically sign your RubyGem - Step-by-step guide
- Signing rubygems - Pasteable instructions
- Twitter gem gemspec
- RubyGems Trust Model Overview, doc
- Let's figure out a way to start signing RubyGems
- A Practical Guide to Using Signed Ruby Gems - Part 3: Signing your Own
- Also see the Resources page.