Dependency Security

Ruby Gems Supply Chain Security

Protecting your Ruby applications from gem-based supply chain attacks with Bundler security features, gem signing, and auditing.

Michael
Staff Security Engineer
4 min read

Ruby's gem ecosystem powers hundreds of thousands of production applications, from Rails monoliths to microservices. RubyGems.org hosts over 170,000 gems, and the average Rails application pulls in well over 100 dependencies. The ecosystem has a strong track record, but supply chain attacks have hit Ruby too. Here is how to protect your applications.

The Ruby Supply Chain Risk Profile

Ruby shares the common supply chain risks with other interpreted language ecosystems:

  • Gem extensions. Native extensions compile C code during gem install. A malicious extension can execute arbitrary code.
  • Post-install hooks. While less common than npm postinstall scripts, gems can define installation hooks.
  • Typosquatting. In 2020, researchers found malicious gems like atlas-client on RubyGems.org that exfiltrated credentials.
  • Account takeover. Compromised maintainer accounts can push malicious gem versions.
  • Dependency confusion. Organizations using private gem servers alongside RubyGems.org face the same confusion risks as other ecosystems.

RubyGems.org has responded well. They now require MFA for privileged operations, support WebAuthn, and have a security team that actively removes malicious gems. But you should not rely on the registry alone.

Gemfile.lock: Your First Defense

Gemfile.lock records exact versions of every gem in your dependency tree. It is the most important security file in your Ruby project.

GEM
  remote: https://rubygems.org/
  specs:
    actioncable (7.1.2)
      actionpack (= 7.1.2)
      activesupport (= 7.1.2)
      nio4r (~> 2.0)
      websocket-driver (>= 0.6.1)

Rules:

  1. Always commit Gemfile.lock. For applications, this is mandatory. For libraries, it is still useful for reproducible CI.
  2. Use bundle install --frozen in CI. This fails if Gemfile.lock is out of sync with the Gemfile.
  3. Review Gemfile.lock changes in PRs. Every version change is a potential risk.
  4. Never run bundle update without reviewing the diff. bundle update can change dozens of dependencies at once.

bundler-audit

bundler-audit checks your Gemfile.lock against the Ruby Advisory Database:

gem install bundler-audit
bundle-audit check --update

The --update flag pulls the latest advisory database before scanning. Run this in CI:

bundle-audit check --update || exit 1

For JSON output suitable for CI integration:

bundle-audit check --format json

Gem Signing

RubyGems supports package signing with X.509 certificates, but adoption is low. You can enforce signature verification for gems that are signed:

gem install some_gem -P MediumSecurity

Security policies:

  • NoSecurity - No verification (default).
  • LowSecurity - Verify signatures if present.
  • MediumSecurity - Require signatures, verify against trusted certificates.
  • HighSecurity - Require signatures, verify the full certificate chain.

In practice, most gems are not signed, so HighSecurity is not viable for most applications. However, you can configure Bundler to verify signatures for gems that do sign their releases.

Dependency Confusion Defenses

If you host private gems:

  1. Use Bundler source blocks to restrict where specific gems come from:
# Gemfile
source "https://rubygems.org"

source "https://gems.yourcompany.com" do
  gem "yourcompany-auth"
  gem "yourcompany-logging"
end

This ensures your internal gems can only be resolved from your private server.

  1. Reserve gem names on RubyGems.org. Publish placeholder gems with your internal package names.

  2. Use gem name prefixes. Prefix all internal gems with your organization name.

Reviewing New Dependencies

Before adding a gem, evaluate it:

# Check gem info
gem info rails

# Check reverse dependencies (who uses this gem?)
gem dependency rails --reverse-dependencies

# Check the gem's source
bundle open some_gem

Look for:

  • Active maintenance and recent releases
  • Multiple maintainers
  • CI pipeline with tests
  • Native extensions (higher risk)
  • Post-install hooks

Use bundle viz or bundle list to understand your full dependency tree.

Securing Bundler Configuration

Your .bundle/config and environment variables control Bundler behavior:

# Disable post-install messages (reduces social engineering surface)
bundle config set --local ignore_messages true

# Set deployment mode for production
bundle config set --local deployment true

# Freeze the bundle
bundle config set --local frozen true

In Dockerfiles:

RUN bundle config set --local deployment true && \
    bundle config set --local frozen true && \
    bundle install --jobs 4 --retry 3

SBOM Generation

Generate a CycloneDX SBOM for your Ruby application:

gem install cyclonedx-ruby
cyclonedx-ruby -p /path/to/project -o sbom.json

This captures your complete gem dependency tree with versions and license information.

How Safeguard.sh Helps

Safeguard.sh monitors your Ruby gem dependencies across every application in your organization. It ingests SBOMs from your CI builds, maps gem versions to known vulnerabilities, and provides a unified dashboard showing your Ruby supply chain risk. When a new advisory appears in the Ruby Advisory Database, Safeguard.sh immediately identifies which of your applications are affected and provides prioritized remediation steps. It gives your security team visibility that individual bundler-audit runs in separate repositories cannot.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.