modul Gem::Security

Gems signieren

Die Gem::Security implementiert kryptographische Signaturen für Gems. Der folgende Abschnitt ist eine Schritt-für-Schritt-Anleitung zur Verwendung von signierten Gems und zur Erzeugung eigener.

Anleitung

Erstellen Ihres Zertifikats

Um Ihre Gems zu signieren, müssen Sie einen privaten Schlüssel und ein selbstsigniertes Zertifikat erstellen. Hier ist, wie das geht:

# build a private key and certificate for yourself:
$ gem cert --build you@example.com

Dies kann je nach Geschwindigkeit Ihres Computers einige Sekunden bis ein oder zwei Minuten dauern (Public-Key-Algorithmen sind nicht gerade die schnellsten Kryptographie-Algorithmen der Welt). Wenn es fertig ist, sehen Sie die Dateien „gem-private_key.pem“ und „gem-public_cert.pem“ im aktuellen Verzeichnis.

Zuerst: Verschieben Sie beide Dateien nach ~/.gem, falls Sie in diesem Verzeichnis noch keinen Schlüssel und kein Zertifikat haben. Stellen Sie sicher, dass die Dateiberechtigungen den Schlüssel für andere unlesbar machen (standardmäßig wird die Datei sicher gespeichert).

Halten Sie Ihren privaten Schlüssel geheim; wenn er kompromittiert wird, kann jemand Pakete unter Ihrem Namen signieren (Hinweis: PKI bietet Möglichkeiten, das Risiko gestohlener Schlüssel zu mindern; mehr dazu später).

Gems signieren

In RubyGems 2 und neuer ist keine zusätzliche Arbeit erforderlich, um einen Gem zu signieren. RubyGems findet automatisch Ihren Schlüssel und Ihr Zertifikat in Ihrem Home-Verzeichnis und verwendet sie, um neu verpackte Gems zu signieren.

Wenn Ihr Zertifikat nicht selbstsigniert ist (von einem Dritten signiert), versucht RubyGems, die Zertifikatkette aus den vertrauenswürdigen Zertifikaten zu laden. Verwenden Sie gem cert --add signing_cert.pem, um Ihre Unterzeichner als vertrauenswürdige Zertifikate hinzuzufügen. Siehe unten für weitere Informationen zu Zertifikatsketten.

Wenn Sie Ihren Gem erstellen, wird er automatisch signiert. Wenn Sie in Ihre Gem-Datei schauen, werden Sie sehen, dass ein paar neue Dateien hinzugefügt wurden.

$ tar tf your-gem-1.0.gem
metadata.gz
metadata.gz.sig # metadata signature
data.tar.gz
data.tar.gz.sig # data signature
checksums.yaml.gz
checksums.yaml.gz.sig # checksums signature

Gems manuell signieren

Wenn Sie Ihren Schlüssel an einem separaten sicheren Ort speichern möchten, müssen Sie Ihre Gems manuell für die Signatur einrichten. Tun Sie dies, indem Sie signing_key und cert_chain im Gem-Spezifikationsfile vor dem Verpacken Ihres Gems festlegen.

s.signing_key = '/secure/path/to/gem-private_key.pem'
s.cert_chain = %w[/secure/path/to/gem-public_cert.pem]

Wenn Sie Ihren Gem mit diesen Optionen verpacken, lädt RubyGems automatisch Ihren Schlüssel und Ihr Zertifikat von den sicheren Pfaden.

Signierte Gems und Sicherheitsrichtlinien

Jetzt überprüfen wir die Signatur. Installieren Sie den Gem, aber fügen Sie die folgenden Optionen hinzu: -P HighSecurity, wie folgt:

# install the gem with using the security policy "HighSecurity"
$ sudo gem install your.gem -P HighSecurity

Die Option -P legt Ihre Sicherheitsrichtlinie fest – darüber sprechen wir gleich. Äh, was ist das?

$ gem install -P HighSecurity your-gem-1.0.gem
ERROR:  While executing gem ... (Gem::Security::Exception)
    root cert /CN=you/DC=example is not trusted

Der Übeltäter hier ist die Sicherheitsrichtlinie. RubyGems hat mehrere verschiedene Sicherheitsrichtlinien. Machen wir eine kurze Pause und gehen die Sicherheitsrichtlinien durch. Hier ist eine Liste der verfügbaren Sicherheitsrichtlinien und eine kurze Beschreibung jeder einzelnen.

Der Grund, warum RubyGems Ihren glänzenden neuen signierten Gem nicht installieren wollte, war, dass er von einer nicht vertrauenswürdigen Quelle stammte. Nun, Ihr Code ist fehlerfrei (natürlich), also müssen Sie sich selbst als vertrauenswürdige Quelle hinzufügen.

# add trusted certificate
gem cert --add ~/.gem/gem-public_cert.pem

Sie haben jetzt Ihr öffentliches Zertifikat als vertrauenswürdige Quelle hinzugefügt. Jetzt können Sie Pakete, die von Ihrem privaten Schlüssel signiert wurden, ohne Probleme installieren. Versuchen wir den obigen Installationsbefehl erneut.

# install the gem with using the HighSecurity policy (and this time
# without any shenanigans)
$ gem install -P HighSecurity your-gem-1.0.gem
Successfully installed your-gem-1.0
1 gem installed

Diesmal wird RubyGems Ihr signiertes Paket akzeptieren und mit der Installation beginnen.

Während Sie darauf warten, dass RubyGems seine Magie wirkt, schauen Sie sich einige der anderen Sicherheitsbefehle an, indem Sie gem help cert ausführen.

Options:
  -a, --add CERT                   Add a trusted certificate.
  -l, --list [FILTER]              List trusted certificates where the
                                   subject contains FILTER
  -r, --remove FILTER              Remove trusted certificates where the
                                   subject contains FILTER
  -b, --build EMAIL_ADDR           Build private key and self-signed
                                   certificate for EMAIL_ADDR
  -C, --certificate CERT           Signing certificate for --sign
  -K, --private-key KEY            Key for --sign or --build
  -A, --key-algorithm ALGORITHM    Select key algorithm for --build from RSA, DSA, or EC. Defaults to RSA.
  -s, --sign CERT                  Signs CERT with the key from -K
                                   and the certificate from -C
  -d, --days NUMBER_OF_DAYS        Days before the certificate expires
  -R, --re-sign                    Re-signs the certificate from -C with the key from -K

Wir haben bereits die Option --build behandelt, und die Befehle --add, --list und --remove scheinen ziemlich selbsterklärend; sie erlauben Ihnen, die Zertifikate in Ihrer Liste vertrauenswürdiger Zertifikate hinzuzufügen, aufzulisten und zu entfernen. Aber was ist mit der Option --sign?

Zertifikatsketten

Um diese Frage zu beantworten, werfen wir einen Blick auf „Zertifikatsketten“, ein Konzept, das ich zuvor erwähnt habe. Selbstsignierte Zertifikate haben ein paar Probleme: Erstens bieten selbstsignierte Zertifikate keine große Sicherheit. Sicher, das Zertifikat besagt Yukihiro Matsumoto, aber woher weiß ich, dass es tatsächlich von matz selbst generiert und signiert wurde, es sei denn, er hat mir das Zertifikat persönlich gegeben?

Das zweite Problem ist die Skalierbarkeit. Sicher, wenn es 50 Gem-Autoren gibt, dann habe ich 50 vertrauenswürdige Zertifikate, kein Problem. Was ist, wenn es 500 Gem-Autoren gibt? 1000? Ständig neue vertrauenswürdige Zertifikate hinzufügen zu müssen, ist eine Plage und macht das Vertrauenssystem tatsächlich unsicherer, indem es RubyGems-Benutzer dazu ermutigt, neuen Zertifikaten blind zu vertrauen.

Hier kommen Zertifikatsketten ins Spiel. Eine Zertifikatkette etabliert eine beliebig lange Vertrauenskette zwischen einem ausstellenden Zertifikat und einem Kind-Zertifikat. Anstatt also Zertifikaten auf einer pro-Entwickler-Basis zu vertrauen, verwenden wir das PKI-Konzept der Zertifikatsketten, um eine logische Hierarchie des Vertrauens aufzubauen. Hier ist ein hypothetisches Beispiel für eine Vertrauenshierarchie, die (ungefähr) auf Geografie basiert:

                    --------------------------
                    | rubygems@rubygems.org |
                    --------------------------
                                |
              -----------------------------------
              |                                 |
  ----------------------------    -----------------------------
  |  seattlerb@seattlerb.org |    | dcrubyists@richkilmer.com |
  ----------------------------    -----------------------------
       |                |                 |             |
---------------   ----------------   -----------   --------------
|   drbrain   |   |   zenspider  |   | pabs@dc |   | tomcope@dc |
---------------   ----------------   -----------   --------------

Anstatt jetzt 4 vertrauenswürdige Zertifikate zu haben (eines für drbrain, zenspider, pabs@dc und tomecope@dc), könnte ein Benutzer tatsächlich mit einem Zertifikat auskommen, dem „rubygems@rubygems.org“-Zertifikat.

So funktioniert es:

Ich installiere „rdoc-3.12.gem“, ein Paket, das von „drbrain“ signiert wurde. Ich habe noch nie von „drbrain“ gehört, aber sein Zertifikat hat eine gültige Signatur vom Zertifikat „seattle.rb@seattlerb.org“, welches wiederum eine gültige Signatur vom Zertifikat „rubygems@rubygems.org“ hat. Voilà! An diesem Punkt ist es für mich viel vernünftiger, einem von „drbrain“ signierten Paket zu vertrauen, da ich eine Kette zu „rubygems@rubygems.org“ herstellen kann, dem ich vertraue.

Zertifikate signieren

Die Option --sign ermöglicht all dies. Ein Entwickler erstellt sein Build-Zertifikat mit der Option --build, lässt es dann signieren, indem er es zu seinem nächsten regionalen Ruby-Treffen (in unserem hypothetischen Beispiel) mitbringt, und es wird dort vom Inhaber des regionalen RubyGems-Signaturzertifikats signiert, welches auf der nächsten RubyConf vom Inhaber des Top-Level-RubyGems-Zertifikats signiert wird. An jedem Punkt führt der Aussteller denselben Befehl aus.

# sign a certificate with the specified key and certificate
# (note that this modifies client_cert.pem!)
$ gem cert -K /mnt/floppy/issuer-priv_key.pem -C issuer-pub_cert.pem
   --sign client_cert.pem

Dann kann der Inhaber des ausgestellten Zertifikats (in diesem Fall Ihr Kumpel „drbrain“) dieses signierte Zertifikat verwenden, um RubyGems zu signieren. Übrigens, um allen anderen sein neues schickes signiertes Zertifikat bekannt zu machen, würde „drbrain“ sein neu signiertes Zertifikat als ~/.gem/gem-public_cert.pem speichern.

Offensichtlich existiert diese RubyGems-Vertrauensinfrastruktur noch nicht. Auch im „echten Leben“ generieren Aussteller das Kind-Zertifikat tatsächlich aus einer Zertifikatsanforderung, anstatt ein bestehendes Zertifikat zu signieren. Und unserer hypothetischen Infrastruktur fehlt ein Zertifikats-Widerrufs-System. Dies sind Dinge, die in Zukunft behoben werden können...

An diesem Punkt sollten Sie wissen, wie Sie all diese neuen und interessanten Dinge tun können:

Signaturen manuell überprüfen

Falls Sie RubyGems nicht vertrauen, können Sie Gem-Signaturen manuell überprüfen.

  1. Den Gem abrufen und entpacken

    gem fetch some_signed_gem
    tar -xf some_signed_gem-1.0.gem
  2. Die öffentliche Schlüsseldatei aus dem Gemspec abrufen

    gem spec some_signed_gem-1.0.gem cert_chain | \
      ruby -rpsych -e 'puts Psych.load($stdin)' > public_key.crt
  3. Einen SHA1-Hash der data.tar.gz-Datei generieren

    openssl dgst -sha1 < data.tar.gz > my.hash
    
  4. Die Signatur überprüfen

    openssl rsautl -verify -inkey public_key.crt -certin \
      -in data.tar.gz.sig > verified.hash
  5. Ihren Hash mit dem verifizierten Hash vergleichen

    diff -s verified.hash my.hash
  6. Wiederholen Sie 5 und 6 mit metadata.gz

OpenSSL Referenz

Die von --build und --sign generierten .pem-Dateien sind PEM-Dateien. Hier sind ein paar nützliche OpenSSL-Befehle zur Manipulation dieser Dateien:

# convert a PEM format X509 certificate into DER format:
# (note: Windows .cer files are X509 certificates in DER format)
$ openssl x509 -in input.pem -outform der -out output.der

# print out the certificate in a human-readable format:
$ openssl x509 -in input.pem -noout -text

Und dasselbe können Sie auch mit der privaten Schlüsseldatei tun.

# convert a PEM format RSA key into DER format:
$ openssl rsa -in input_key.pem -outform der -out output_key.der

# print out the key in a human readable format:
$ openssl rsa -in input_key.pem -noout -text

Fehler/TODO

Originalautor

Paul Duncan <pabs@pablotron.org> pablotron.org/