CINXE.COM

Security - RubyGems Guides

<!DOCTYPE html> <html lang="en-us"> <head> <title>Security - RubyGems Guides</title> <meta charset="utf-8" /> <link rel="icon" href="/favicon.ico" type="image/x-icon"> <link rel="icon" href="/favicon.icns"> <meta http-equiv="Description" name="Description" content="Tutorials, guides, FAQs for RubyGems package management" /> <meta http-equiv="Keywords" name="Keywords" content="rubygems, gems, programming, ruby, packages" /> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" name="viewport"> <link href="/stylesheets/application.css" rel="stylesheet" type="text/css" /> <script src="//code.jquery.com/jquery-3.6.0.min.js"></script> <script src="/javascripts/mobile-nav.js" type="text/javascript"></script> <script src="//use.typekit.net/omu5dik.js" type="text/javascript"></script> <script> try{Typekit.load();}catch(e){} </script> <script> $(function() { var path = window.location.pathname; $('a.nav--v__link[href="'+path.substring(0, path.length -1)+'"]').addClass('is-active'); var header_link = $('a.header__nav-link[href="'+path.substring(0, path.length -1)+'"]'); if(header_link.length > 0) { $('.header__nav-links a').removeClass('is-active'); header_link.addClass('is-active'); } }); </script> <!--[if !IE]>--> <meta name="viewport" content="width=device-width"> <script type="text/javascript"> window.scrollTo(0, 1); </script> <!--<![endif]--> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body class="default"> <header class="header header--interior"> <div class="l-wrap--header"> <a class="header__logo-wrap" href="https://rubygems.org/"> <span class="header__logo" data-icon="⬡">⬢</span> <span class="t-hidden">RubyGems</span> </a> <a class="header__club-sandwich" href="#"> <span class="t-hidden">Navigation menu</span> </a> <div class="header__nav-links-wrap"> <form accept-charset="UTF-8" action="https://rubygems.org/search" class="header__search-wrap" id="main-search" method="get"> <input class="header__search" id="query" name="query" placeholder="Search Gems&hellip;" type="search" /> <label for="query"> <span class="t-hidden">Search gems</span> </label> <input class="header__search__icon" id="search_submit" type="submit" value="⌕" /> </form> <nav class="header__nav-links"> <a class="header__nav-link" href="https://rubygems.org/releases">Releases</a> <a class="header__nav-link" href="https://blog.rubygems.org">Blog</a> <a class="header__nav-link" href="https://rubygems.org/gems">Gems</a> <a class="header__nav-link is-active" href="/">Guides</a> <a class="header__nav-link" href="/contributing">Contribute</a> </nav> </div> </div> </header> <main class="main--interior"> <div class="l-wrap--b"> <a class="t-display page__heading" href="/">Guides</a> <div class="l-overflow"> <div class="l-col--l"> <ul class="nav--v"> <li><a class="nav--v__link" href="/rubygems-basics">RubyGems Basics</a></li> <li><a class="nav--v__link" href="/what-is-a-gem">What is a gem?</a></li> <li><a class="nav--v__link" href="/make-your-own-gem">Make your own gem</a></li> <li><a class="nav--v__link" href="/gems-with-extensions">Gems with Extensions</a></li> <li><a class="nav--v__link" href="/name-your-gem">Name your gem</a></li> <li><a class="nav--v__link" href="/publishing">Publishing your gem</a></li> <li><a class="nav--v__link" href="/security">Security Practices</a></li> <li><a class="nav--v__link" href="/managing-owners-using-ui">Managing owners using UI</a></li> <li><a class="nav--v__link" href="/removing-a-published-gem">Removing a Published gem</a></li> <li><a class="nav--v__link" href="/ssl-certificate-update">SSL Certificate Update</a></li> <li><a class="nav--v__link" href="/patterns">Patterns</a></li> <li><a class="nav--v__link" href="/specification-reference">Specification Reference</a></li> <li><a class="nav--v__link" href="/command-reference">Command Reference</a></li> <li><a class="nav--v__link" href="/rubygems-org-api">RubyGems.org API</a></li> <li><a class="nav--v__link" href="/rubygems-org-api-v2">RubyGems.org API V2.0</a></li> <li><a class="nav--v__link" href="/rubygems-org-compact-index-api">RubyGems.org Compact Index API</a></li> <li><a class="nav--v__link" href="/rubygems-org-rate-limits">RubyGems.org rate limits</a></li> <li><a class="nav--v__link" href="/api-key-scopes">API key scopes</a></li> <li><a class="nav--v__link" href="/run-your-own-gem-server">Run your own gem server</a></li> <li><a class="nav--v__link" href="/setting-up-multifactor-authentication">Setting up multi-factor authentication</a></li> <ul class="nav--v"> <li><a class="nav--v__link" href="/setting-up-webauthn-mfa">Setting up WebAuthn MFA</a></li> <li><a class="nav--v__link" href="/setting-up-otp-mfa">Setting up OTP MFA</a></li> </ul> <li><a class="nav--v__link" href="/using-mfa-in-command-line">Using multi-factor authentication in command line</a></li> <ul class="nav--v"> <li><a class="nav--v__link" href="/using-webauthn-mfa-in-command-line">Using WebAuthn for MFA</a></li> <li><a class="nav--v__link" href="/using-otp-mfa-in-command-line">Using OTP for MFA</a></li> </ul> <li><a class="nav--v__link" href="/mfa-requirement-opt-in">MFA requirement opt in</a></li> <li><a class="nav--v__link" href="/using-s3-source">Using S3 as gem source</a></li> <li><a class="nav--v__link" href="/resources">Resources</a></li> <li><a class="nav--v__link" href="/contributing">Contributing to RubyGems</a></li> <li><a class="nav--v__link" href="/faqs">Frequently Asked Questions</a></li> <li><a class="nav--v__link" href="/plugins">Plugins</a></li> <li><a class="nav--v__link" href="/cve">Common Vulnerabilities and Exposures</a></li> <li><a class="nav--v__link" href="/releasing-rubygems">Releasing RubyGems</a></li> <li><a class="nav--v__link" href="/trusted-publishing">Trusted Publishing</a></li> <ul class="nav--v"> <li><a class="nav--v__link" href="/trusted-publishing/adding-a-publisher">Adding to an existing gem</a></li> <li><a class="nav--v__link" href="/trusted-publishing/pushing-a-new-gem">Pushing a new gem</a></li> <li><a class="nav--v__link" href="/trusted-publishing/releasing-gems">Releasing gems</a></li> </ul> <li><a class="nav--v__link" href="/credits">Credits</a></li> </ul> </div> <div class="l-colspan--r"> <div class="t-body"> <p><em class="t-gray">How to build and install cryptographically signed gems– and other security concerns.</em></p> <p>Security practices are being actively discussed. Check back often.</p> <ul> <li><a href="#general">General</a></li> <li><a href="#using-gems">Using Gems</a></li> <li><a href="#building-gems">Building Gems</a></li> <li><a href="#reporting-security-vulnerabilities">Reporting Security Vulnerabilities</a></li> </ul> <h2 id="general">General</h2> <p>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.</p> <p>RubyGems has had the ability to <a href="https://docs.seattlerb.org/rubygems/Gem/Security.html">cryptographically sign gems</a> since version 0.8.11. This signing works by using the <code class="language-plaintext highlighter-rouge">gem cert</code> command to create a key pair, and then packaging signing data inside the gem itself. The <code class="language-plaintext highlighter-rouge">gem install</code> command optionally lets you set a security policy, and you can verify the signing key for a gem before you install it.</p> <p>However, this method of securing gems is not widely used. It requires a number of <a href="#building-gems">manual steps on the part of the developer</a>, 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 <a href="https://github.com/rubygems-trust/rubygems.org/wiki/_pages">rubygems-trust wiki</a>, the <a href="https://groups.google.com/d/msg/rubygems-developers/lnnGTlfsuYo/TLDcJ2RPSDoJ">RubyGems-Developers list</a> and in <a href="irc://chat.freenode.net/#rubygems-trust">IRC</a>. The goal is to improve (or replace) the signing system so that it is easy for authors and transparent for users.</p> <h2 id="using-gems">Using Gems</h2> <p>Install with a trust policy.</p> <ul> <li> <p><code class="language-plaintext highlighter-rouge">gem install gemname -P HighSecurity</code>: All dependent gems must be signed and verified.</p> </li> <li> <p><code class="language-plaintext highlighter-rouge">gem install gemname -P MediumSecurity</code>: All signed dependent gems must be verified.</p> </li> <li> <p><code class="language-plaintext highlighter-rouge">bundle --trust-policy MediumSecurity</code>: Same as above, except Bundler only recognizes the long <code class="language-plaintext highlighter-rouge">--trust-policy</code> flag, not the short <code class="language-plaintext highlighter-rouge">-P</code>.</p> </li> <li> <p><em>Caveat:</em> Gem certificates are trusted globally, such that adding a cert.pem for one gem automatically trusts all gems signed by that cert.</p> </li> </ul> <p>Verify the checksum, if available</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem fetch gemname -v version ruby -rdigest/sha2 -e "puts Digest::SHA512.new.hexdigest(File.read('gemname-version.gem'))" </code></pre></div></div> <p>Know the risks of being pwned, as described by <a href="https://youtu.be/zEBReauO-vg">Benjamin Smith’s Hacking with Gems talk</a></p> <h2 id="building-gems">Building Gems</h2> <h3 id="sign-with-gem-cert">Sign with: <code class="language-plaintext highlighter-rouge">gem cert</code></h3> <p>1) Create self-signed gem cert</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd ~/.ssh gem cert --build your@email.com chmod 600 gem-p* </code></pre></div></div> <ul> <li>use the email address you specify in your gemspecs</li> </ul> <p>2) Configure gemspec with cert</p> <p>Add cert public key to your repository</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd /path/to/your/gem mkdir certs cp ~/.ssh/gem-public_cert.pem certs/yourhandle.pem git add certs/yourhandle.pem </code></pre></div></div> <p>Add cert paths to your gemspec</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>s.cert_chain = ['certs/yourhandle.pem'] s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/ </code></pre></div></div> <p>3) Add your own cert to your approved list, just like anyone else</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem cert --add certs/yourhandle.pem </code></pre></div></div> <p>4) Build gem and test that you can install it</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem build gemname.gemspec gem install gemname-version.gem -P HighSecurity # or -P MediumSecurity if your gem depends on unsigned gems </code></pre></div></div> <p>5) Example text for installation documentation</p> <blockquote> <p>MetricFu is cryptographically signed. To be sure the gem you install hasn’t been tampered with:</p> <p>Add my public key (if you haven’t already) as a trusted certificate</p> <p><code class="language-plaintext highlighter-rouge">gem cert --add &lt;(curl -Ls https://raw.github.com/metricfu/metric_fu/master/certs/bf4.pem)</code></p> <p><code class="language-plaintext highlighter-rouge">gem install metric_fu -P MediumSecurity</code></p> <p>The MediumSecurity trust profile will verify signed gems, but allow the installation of unsigned dependencies.</p> <p>This is necessary because not all of MetricFu’s dependencies are signed, so we cannot use HighSecurity.</p> </blockquote> <hr /> <h3 id="include-checksum-of-released-gems-in-your-repository">Include checksum of released gems in your repository</h3> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>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' </code></pre></div></div> <hr /> <h3 id="openpgp-signing-is-not-recommended-due-to-lack-of-support-archive">OpenPGP signing is <a href="https://web.archive.org/web/20131125012205/https://www.rubygems-openpgp-ca.org/blog/nobody-cares-about-signed-gems.html">not recommended due to lack of support (archive)</a>.</h3> <p>For details, see discussion <a href="https://github.com/rubygems/guides/pull/70#issuecomment-29007487">with Yorick Peterse</a>.</p> <h2 id="reporting-security-vulnerabilities">Reporting Security vulnerabilities</h2> <h3 id="reporting-a-security-vulnerability-with-someone-elses-gem">Reporting a security vulnerability with someone else’s gem</h3> <p>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 <a href="https://rubysec.com">RubySec</a>.</p> <p>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.</p> <h3 id="reporting-a-security-vulnerability-with-your-own-gem">Reporting a security vulnerability with your own gem</h3> <p>First, request a <a href="https://www.cve.org/ResourcesSupport/FAQs">CVE identifier</a> by emailing <a href="https://github.com/RedHatProductSecurity/CVE-HOWTO#how-do-i-request-a-cve">one of these places</a> or from GitHub by creating a <a href="https://docs.github.com/en/code-security/security-advisories/repository-security-advisories/about-repository-security-advisories#cve-identification-numbers">Security Advisory</a>. This identifier will make it easy to uniquely identify the vulnerability when talking about it.</p> <p>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.</p> <p>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:</p> <ul> <li> <p>Send an email to several lists including ruby-security-ann@googlegroups.com, rubysec-announce@googlegroups.com, and oss-security@lists.openwall.com 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.</p> </li> <li> <p>Add it to <a href="https://github.com/rubysec/ruby-advisory-db/">ruby-advisory-db</a>. You can do this by following the <a href="https://github.com/rubysec/ruby-advisory-db/blob/master/CONTRIBUTING.md">CONTRIBUTING</a> guidelines and submitting a pull request.</p> </li> </ul> <h2 id="credits">Credits</h2> <p>Several sources were used for content for this guide:</p> <ul> <li><a href="https://www.benjaminfleischer.com/2013/11/08/how-to-sign-your-rubygem-cert/">How to cryptographically sign your RubyGem</a> - Step-by-step guide</li> <li><a href="https://web.archive.org/web/20130218074304/https://developer.zendesk.com/blog/2013/02/03/signing-gems/">Signing rubygems - Pasteable instructions (archive)</a></li> <li><a href="https://github.com/metricfu/metric_fu/blob/master/metric_fu.gemspec">metric_fu gem gemspec</a></li> <li><a href="https://github.com/rubygems-trust/rubygems.org/wiki/Overview">RubyGems Trust Model Overview</a>, <a href="https://goo.gl/ybFIO">doc</a></li> <li><a href="https://tonyarcieri.com/lets-figure-out-a-way-to-start-signing-rubygems">Let’s figure out a way to start signing RubyGems</a></li> <li><a href="https://web.archive.org/web/20131125020053/https://blog.meldium.com/home/2013/3/6/signing-gems-how-to">A Practical Guide to Using Signed Ruby Gems - Part 3: Signing your Own (archive)</a></li> <li>Also see the <a href="/resources">Resources</a> page.</li> </ul> </div> </div> </div> <div class="paginated-nav-links"> <a class="paginated-nav-link--prev" data-icon="&lt;" href="/publishing"> <span>Previous</span> </a> <a class="paginated-nav-link--next" data-icon="&gt;" href="/managing-owners-using-ui"> <span>Next</span> </a> </div> </div> </main> <footer class="footer"> <div class="l-wrap--footer"> <div class="l-overflow"> <div class="nav--v l-col--r--pad"> <a class="nav--v__link--footer" href="https://status.rubygems.org/">Status</a> <a class="nav--v__link--footer" href="http://uptime.rubygems.org/">Uptime</a> <a class="nav--v__link--footer" href="https://github.com/rubygems/rubygems.org" target='_blank'>Code</a> <a class="nav--v__link--footer" href="https://groups.google.com/forum/#!forum/rubygems-org" target='_blank'>Discuss</a> <a class="nav--v__link--footer" href="https://rubygems.org/stats">Stats</a> <a class="nav--v__link--footer" href="https://blog.rubygems.org/">Blog</a> <a class="nav--v__link--footer" href="https://rubygems.org/pages/about">About</a> <a class="nav--v__link--footer" href="mailto:support@rubygems.org">Help</a> </div> <div class="l-colspan--l colspan--l--has-border"> <p class="footer__about">RubyGems.org is the Ruby community&rsquo;s gem hosting service. Instantly publish your gems and install them. Use the API to interact and find out more information about available gems. Become a contributor and enhance the site with your own changes.</p> </div> </div> </div> <div class="footer__sponsors-wrap"> <div class="footer__sponsors"> <a class="footer__sponsor footer__sponsor__ruby_central" href="https://rubycentral.org/" target="_blank" rel="noreferrer"> Supported by <span class="t-hidden">Ruby Central</span> </a> <a class="footer__sponsor footer__sponsor__dockyard" href="https://dockyard.com/ruby-on-rails-consulting" target="_blank" rel="noreferrer"> Designed by <span class="t-hidden">DockYard</span> </a> <a class="footer__sponsor footer__sponsor__dnsimple" href="https://dnsimple.link/resolving-rubygems" target="_blank" rel="noreferrer"> Resolved with <span class="t-hidden">DNSimple</span> </a> <a class="footer__sponsor footer__sponsor__datadog" href="https://www.datadoghq.com/" target="_blank" rel="noreferrer"> Monitored by <span class="t-hidden">Datadog</span> </a> <a class="footer__sponsor footer__sponsor__fastly" href="https://www.fastly.com/" target="_blank" rel="noreferrer"> Gems served by <span class="t-hidden">Fastly</span> </a> <a class="footer__sponsor footer__sponsor__honeybadger" href="https://www.honeybadger.io/" target="_blank" rel="noreferrer"> Monitored by <span class="t-hidden">Honeybadger</span> </a> <a class="footer__sponsor footer__sponsor__domainr" href="https://domainr.com/" target="_blank" rel="noreferrer"> Verified by <span class="t-hidden">Domainr</span> </a> <a class="footer__sponsor footer__sponsor__whitesource" href="https://www.whitesourcesoftware.com/" target="_blank" rel="noreferrer"> Secured by <span class="t-hidden">Whitesource</span> </a> </div> </div> </footer> <script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-10315684-2']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10