Yubikey5にもう少し重要な秘密鍵を置く
以前のブログ記事ではYubikeyでそのまま鍵を生成したが、これだとバックアップを取ることもできないため重要な用途の鍵には使えない。 別のマシンでマスターキーを作り、サブキーをYubikeyに置くことで運用できるようにする。
AWS LambdaでRustを使う
AWS Lambda with Rust
公式のサポートがある環境ではないのでCustom Runtimeを使う必要がある。 2020年10月11日現在まだcrates.ioにpublishされていないが、lambda-runtimeの0.3あるいは名前を変えてlambdaとなる(?) GitHub - awslabs/aws-lambda-rust-runtime: A Rust runtime for AWS Lambda が使える。 どうも使用例がまだ少なくネット上にサンプルがなかったのでここに書いて残しておく。
aws-lambda-runtime 0.3 or lambda example
おそらく正式にpublishされた際には#[lambda]マクロが導入されていて0から書く場合はもうちょっとシンプルにも書けるようになるのだろうが、既存のバージョンのコードから変更するとこの例のような感じになると思われる。 tokio-0.2が使え、async/awaitが使え、?オペレータを使えるのが既存のバージョンと大きく違う。
ただしこのバージョンのRustはAWSが提供するカスタム環境のイメージのglibcでは使えないため、必ずmuslターゲットでコンパイルする必要がある。
既にAWS提供の環境でビルドしていた場合は別のhttps://hub.docker.com/r/ekidd/rust-musl-builder/のようなビルダーイメージでビルドする手もあるが、musl libcとglibcで性能が変わってくるような場合もあるだろう。
WebAuthn FIDO2について
WebAuthnを最低限知っておこう
社内勉強会で出したものの和訳です。社内レポジトリの実験環境用コードとそれを参照した部分は割愛しています。
対象読者: モダンブラウザの認証用APIについて興味があるレベルの人
tl;dr; WebAuthn API は将来的に使われる可能性がある良いAPIですが、(2019年1月)現在のブラウザでは限定的にしか使えません。
用語
WebAuthn: サーバーからブラウザを通して認証器(Authenticator)と通信するための javascript API
Fido2: WebAuthn + CTAP2(新しいブラウザ-認証器間のプロトコル) + 暗号アルゴリズムのホワイトリスト + (オプショナル) 登録済認証器の検証機能
市場で'Fido2対応'と謳ってマーケティングしている商品はCTAP2に対応していて、認証器の検証に対応している。
現状の認証
1. ユーザ名とパスワードを入力する。 (What You Know; 当人しか知りえない事 をサーバーが確かめる)
2. ワンタイムパスワード(OTP)を認証機器やスマホの認証アプリで表示する、あるいは受け取る。
3. OTPを入力してログイン完了 (What You Have; 当人しか持ちえない物 を持っていたとサーバーが確かめる)
現状のWebAuthnを利用した認証
1. ユーザ名とパスワードを入力する。 (What You Know; 当人しか知りえない事 をサーバーが確かめる)
2. FIDO2対応の認証器で認証するよう促される。
3. 認証され、ログイン完了 (What You Have; 当人しか持ちえない物 を持っていたとサーバーが確かめる)
WebAuthnを利用した次世代の認証
1. FIDO2対応でかつユーザーが認証できる認証器で認証するように促される
2. 認証され、ログイン完了。
認証自体は認証機器に任され、それが不正なものではないかのみサーバーで確かめる。
(ただし、2019年1月時点でこれを可能なのはHELLOによる認証が有効なWindows10 Edgeのみ。)
上記現状と次世代の例の差異
(この節は社内レポジトリ内のコードを利用しているため大幅割愛。実例でAttest部分以外をサーバー側で可読なように出力するコード。)
どの程度の認証が行えるかは認証器によって様々であり、UVに対応した認証器であれば次世代の例として示した認証が可能。サーバー側で登録時にこのフラグを検証して確認しなければいけない。*1
UVの確認ができない場合はWhat You Have; 当人しか持ちえない物を持っていると確認する一要素として利用できる。*2 2020年現在、Yubikey5+Githubのような例ではこちらの利用方法。
サーバーとの通信路での改竄やなりすましに対する防御のため、認証はRelying Party、ブラウザでの実運用上はドメイン名と強く結びついている。認証用ドメインや複数ドメインで同一IDを利用するのは難しい。*3
*1: Web Authentication: An API for accessing Public Key Credentials Level 1
*2: 勉強会ではYubikey5で実際にUVフラグがないことをしめす。
*3: 不可能ではないが認証連携システムの構築が必要。
家庭内トラストレス化へむけてクライアント証明書の発行
オレオレCAにクライアント証明書を発行してもらう。
まずはopenssl.cnfを編集する。
# x509_extensions = usr_cert とデフォルトCAのフィールドで指定しているので、[ usr_cert ] extension 部分を探す。
# このCAが署名した時にどのような目的の鍵として使えると示すか。このCA自体の制約ではない。
[ usr_cert ]
nsCertType = client,email
keyUsage = digitalSignature, keyEncipherment
# keyUsageはnonRepudationとdigitalSignatureが排他の方が何を目的とした署名や証明なのか明確になるだろうが、実態としては目的のkeyUsageだけ確認して他のkeyUsageが有効でも別に良しとされるみたい。
extendedKeyUsage = clientAuth
クライアント用に新しい鍵と署名要求を作る。
# pwd
/usr/local/share/pki
# SSLEAY_CONFIG="-config ./openssl.cnf" ./CA.pl -newreq-nodes
(略)
Please enter the following 'extra' attributes
to be sent with your certificate request
# デフォルトの [ req_attributes ]をそのままにしていたためattributeが要求される。opensslが署名する時には基本的に無視されているフィールド。別の仕様のCAでは要求されることもある。
A challenge password []:
An optional company name []:
(略)
そしてできたCSRを使ってオレオレCAが署名する。CA.plに余計な環境変数を与えていない限り、秘密鍵やCSRのファイル名が変わらず自動的にnewreq.pemが使われる。
# SSLEAY_CONFIG="-config ./openssl.cnf" ./CA.pl -sign
Using configuration from ./openssl.cnf
# CAの秘密鍵のパスフレーズが要求される。
Enter pass phrase for ./CA/private/cakey.pem:
(略)
これでこのCAにより署名されたnewcert.pemができあがる。
newcert.pemとnewkey.pemをクライアントのブラウザに配布しやすくするために、p12ファイルにする。こちらもファイル名がCA.plが知っている通りのままなので、細々とした環境変数やオプションが不要となる。
# ./CA.pl -pkcs12
Enter Export Password:
Verifying - Enter Export Password:
PKCS #12 file is in newcert.p12
newcert.p12をユーザ側に配布してブラウザに読み込ませれば完了となる。
家庭内トラストレス化へ向けての認証局
域内だけに公開しているhttpサービスを網全体を信用するのではなく、クライアント毎にアクセスを認可したい。ユーザ管理があるわけでもないのでIdPを使うのは難しいと判断し、クライアント認証を行う。
略語・専門用語
CA
Certificate authority.
証明書が信頼できると検証できる形で認証してくれるもの。CAが信頼できると言ってるなら自分も信頼できるな、と言えるような権威。
実態としてはbasicContrtaints
でCA:True
を持つ証明書の秘密鍵で、あなたが信頼できる手段で公開鍵を入手しているもの。「CAが信頼できると言ってる奴が信頼できると言ってる奴が信頼できると言ってるなら自分も信頼できるな」という具合で最大でPathLen
の回数だけ多段に認証を信用できる。
csr
Certificate Signing Request (Certification Req.).
自分が何者であるかと保持する鍵をまとめて署名してもらう要求。csrと公開鍵を渡して証明書(crtファイル)を返してもらう。
現代のWeb業界だと多分PKCS#10(RFC2986)形式以外見る機会がないが必要な項目を適切に書いてあればcsrではあるので他でも用語としては使う。HTMLの
crl
Certificate Revocation List
期限内だけれど信頼しちゃ駄目な証明書のリスト。なんかの事故とかで信頼性を担保できないのに署名しちゃった証明書が生まれた時にそれを排除するためにある仕組みだが、ブラウザくらい目が届いているもの以外実態としてクライアント側がきちんとチェックしきれていないと思う。家庭内トラストレス化でも事故が起きたらAuthorityを変えて再配布でこと足りるため、無視される。
crt
Certificate.
証明書に一般的に使われる拡張子。web業界だとX.509形式以外見ないと思われる。この証明書を持っている者が何であり、その事をどのAuthorityが署名したかが書いてあり、署名を検証する事で信頼性を保証する。署名済み公開鍵。 エンコードとしてはpemが多いが、windowsだとバイナリのder形式も一般的らしい。
key
一般的には秘密鍵の拡張子によく用いられるが、多分定められた標準はない。
pem
base64フォーマットされた証明書関連のあれこれのコンテナフォーマット。RFC1421,1422,1423,1424あたりで定義されたPrivacy Enhanced Mail用に作られた。PEMのメールは使われていないがコンテナとして便利なのでこれだけは現在でも使われている。 X.509形式の証明書、あるいは証明書の署名を検証していってルート証明書まで検証した結果、あるいはPKCS#10形式もPEM形式に変換できるのでCSRかもしれない。コンテナなのでこれだけでは中身が何かはわからない。
pkcs#12
RFC7292形式。拡張子としては.pfx
.p12
あたりが使われる。
証明書や、公開鍵と秘密鍵のセットといった複数の鍵をまとめて暗号化するフォーマット。
家庭内では物理的手段で信頼できる通信路が確保できる事が多く、必要性よりもブラウザ等で手軽に読み込むための利便性のために使う。
認証局作成手順
実態はちょっと珍しい制約を持つ秘密鍵と公開鍵である。有効なCAは公開鍵を信頼できる手段で多くの環境に配布済みであるが、オレオレ認証局はなんらかの方法で公開鍵を渡さない限り検証できない。 家庭内での使用ということでスクラップアンドビルドや再配布が容易であり、ルートとイシュアー認証局を分けずに使う。 環境はUbuntu 18.04(多分)。家庭用なので。
# apt install openssl
# cd /usr/local/share
# mkdir -p pki/CA
# cd pki
# cp /usr/lib/ssl/misc/CA.pl ./
# cp /etc/ssl/openssl.cnf ./
コピーしてきたCA.plを修正する。
$CATOP="./CA";
コピーしてきたopenssl.cnfを修正する。
[ CA_default ]
dir = ./CA
# これで続く行がCA.plが適当に作るディレクトリ類を適切に指しているはず。
[ req_distinguished_name ]
countryName_default = JA
# その他distinguished_nameのデフォルトも適当に書いておくと便利。
[ v3_ca ]
# CA.pl --newcaが読む部分。`-extensions v3_ca`と書いてあるはず。CA自体の証明書の設定。
basicConstraints = critical,CA:true
keyUsage = critical, cRLSign, keyCertSign, digitalSignature
# extendedKeyUsage はルートCAなので指定しない。中間CAを複数分ける時などには指定するだろうが、大規模システムのみ。
# nsCertType は古いシステムでは参照するかもしれないが、新たなものでは指定しない。(extended)keyUsageとbasicConstraintsで管理されるべきである。
CAを作る。CA.plがopensslコマンドの面倒なところをやってくれる。
# SSLEAY_CONFIG="-config ./openssl.cnf" ./CA.pl -newca
CA certificate filename (or enter to create) # 既存の証明書で作る場合は指定するが、今回は空でEnter
(中略)
# CA.plが勝手に新しい秘密鍵と公開鍵を作ってくれるので、その鍵のパスフレーズを入力する
writing new private key to './CA/private/cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
# 次に自己署名するためのCSRの情報を聞かれる。defaultを書いていたらEnterだけで良い。どうせ家庭内用なので適当に入力しても良いが、commonNameやemail等は存在するかどうかのチェックがあるため空欄は不可。
Country Name (2 letter code) [JA]:
State or Province Name (full name) [Tokyo]:
(中略)
# 最初に作ってもらった鍵で署名するため、ついさきほどのパスフレーズを要求される
Enter pass phrase for ./CA/private/cakey.pem:
(後略)
これで自己署名済みのCAができた。
Yubikey5にssh用秘密鍵を置く
貸与されたPC等自分のgithubアカウントにアクセスできると便利だが絶対に秘密鍵を置きたくない環境で使うための鍵ペアを準備した
自己同一性の証明用鍵のサブキーではない場合、意外と簡単だった
注
下記脆弱性のため、2017年6月以降に出荷されたYubikeyであることを確認してから実行すること Security Advisory 2017-10-16 | Yubico
環境
手順
- 必要な、あるいは便利なプログラムをインストールしていく
- Yubikey as OpenPGP card の PINを設定する
- YubiKey上で鍵生成する
- ssh公開鍵を確認し、登録する
- 生成した鍵をssh鍵として使う
プログラムのインストール
apt install gnupg2 gnupg-agent scdaemon pinentry-gnome3 (pinentry-guiならなんでもいい)
PINを初期設定から変更する
gpg2 --change-pin
初期設定はPINが123456、Admin PINは12345678なので変更する。
鍵設定する
gpg2 --card-edit
gpg cardとのCLIに入る
gpg/card> help
このカードでgenerateできる事を確認して、 *1
gpg/card> admin
gpg/card> generate
適切に答えていって4096bit RSA鍵を作る。今回は単にgithubと自分の管理するサーバのssh鍵の代わりに用い、pgp上のアイデンティティを想定していないのでオンラインのパソコンでそのまま作り、秘密鍵のバックアップをドライブ上に一切作らない。失くしたらauthorized_keysから消すのみである。
また、sshで使うためにはauthentication機能だけで良いのだがgpg cardに詳しくないためsign等用の鍵も生成してしまった。
エントロピー生成のためにいろいろしながら過ごして待つ。一所懸命に何か入力して過ごしてもそこそこ時間がかかるので、Yubikeyを反射的に抜いてしまわない限り他の事をやって鍵の事を忘れるくらいでいい。(1敗。運良く特に壊れなかった。)
gpg: key <key id> marked as ultimately trusted
これでgnupgのキーリングで対応する公開鍵が信頼された状態となる。
ssh公開鍵を確認する
今回生成した鍵は普段の環境では使うつもりがない*2ので、gpg-agentの導入を一時的にしかしない。
SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)" ssh-add -L
sshがgpg-agentのssh-agentモードに繋ぐようにソケットを変え、agentに登録されている公開鍵のリストを出す。
他にgpg-agent内に鍵がある場合はcardnoでどの鍵かを識別し、これをgithubのssh鍵の設定なり必要なマシンのauthorized_keysなりに追加する。
githubにはcommit verification用のgpg鍵の設定もあるので混同しないようにする。
生成した鍵をssh鍵として使う
鍵を使う側のPCの方ではsshからgpg-agentを常用するように設定する。こちらは何が必要で何が必要でなかったのかあまりわからないが、そこまできつく縛る環境でもないので適当に入れている。
sudo apt install scdaemon gpg2 gpg-agent pcscd
環境変数の設定
# In .bashrc etc
export GPG_TTY=$(tty)
gpg-connect-agent updatestartuptty /bye
なお、こちらでもpinentry-gnome3 のたぐいを入れておいた方が多数のttyでPINを入れ直したりせずに済む。
# ~/.gnupg/gpg-agent.conf
enable-ssh-support
pinentry-program /usr/bin/pinentry-gnome3
あとはYubikeyを刺してssh-add -lしてみたり、試しにgit clone等してみて動作を確認する。
補遺
日本のマイナンバーカードの鍵なども一切利用が進まず、他のSmart Cardを使う事がないのでscdaemonのみ使用する環境を想定しているが、IT先進国ではopenscと共存させるための設定が追加で必要となるはず。
なぜYubico OTPは専門家から避けられているのか
一般の人々はそこまで深く気にしないかもしれないが、プライバシーを気にする人、例えばスノーデン氏などは絶対にYubikeyのYubico OTP(One Time Password)を使わないだろう。たとえOTPによって本人認証がよりセキュアになるとしてもだ。
Yubico OTPとはYubikeyを使って44桁のOTPを入力させることができる機能だ。YubikeyはUSBキーボードとしてOTPを入力する。ウェブサイトなどのサービスはYubicoのクラウドでOTPが有効なものかどうかを確かめる事ができる。Yubicoの言い分としては6桁のよくあるRFC 6238 TOTPよりもセキュアだそうだ。
だが、このOTPには重大な瑕疵がある。始めの数桁が固定のIDなのだ。デフォルトではYubikeyのシリアルナンバーとなっている。そしてYubikeyがキーボードを模して入力するため、信用できないサイトや通信の時でも間違えてYubikeyに触れでもしたらそのIDが漏れる。せっかく仕事のアカウントと公益通報のアカウントを分けていたとしても、Yubikeyでセキュアにしたつもりが逆に個人特定をされてしまう。
このため、Yubikeyを入手したら、うっかり触れてIDを様々な場所にばら撒かないようにYubikey Personalization ToolでOTP機能を無効化するべきだとされる。
# 参考
## yubico OTPの機能
https://www.yubico.com/products/services-software/personalization-tools/yubikey-otp/
## Yubikey Personalization Tool