鍵認証は使う場面は多々あるかと思いますが、多くはローカルからリモート環境へ接続する場面での利用が多い印象です。今回はそれとは少し違い、運用している Web サービスからお客様のサーバへアクセスし、そこに置いてあるファイルを取得してくるというような要件への対応でした。
この接続を実装するにあたり、管理画面から情報を入力できるようにしました。入力された情報は DB に暗号化して保存すれば、お客様ごとに接続先を管理することができるようになります。Rails を使っていて、既に別のカラムで暗号化は導入済みだったので、ここの導入は難なく実装できました。
今回共有を受けた鍵の暗号化形式は Ed25519 です。これまで多くの場面で RSA を利用してきましたが、最近はこちらの方が主流のようですね。この鍵をフォームに入力し、保存した情報を利用する形で連携機能を実装しました。
すると大体予想通りではあるものの、接続に失敗しました。ただ失敗しただけでは何が原因かわからないので、ここからデバッグに取り掛かります。IP 制限が問題かとも思いましたが、鍵の認証のところまでは到達しているログが確認できました。ということは鍵に何かしら問題があるのかもしれません。念のためお客様に鍵の MD5 ハッシュ値を送って確認してみたところ、値が違うとの返信が!
そこで改めていただいた鍵を使用してみたところ、前回と同じ MD5 ハッシュ値が…これは何やら私がやらかしている予感しかありません。そこで今度はいただいた鍵と入力された値とを比較してみたところ、そこで MD5 ハッシュ値に差分がありました😱
差分を調査してみたところ、この件名にあるように改行コードが原因でした。調べてみたところどうも HTML Standard に記載があるようにフォーム送信時の挙動によるもので、入力が LF でも送信時に CRLF に変換されてしまうようです。
そしてこれはドキュメントを見つけられなかったのですが、OpenSSH の実装によって改行コードは LF しか許容していないようです。AI (Claude Opus 5) に聞いてみたところ OpenSSH の独自の openssh-key-v1 形式(-----BEGIN OPENSSH PRIVATE KEY-----)の判定箇所で改行コードまで含んだ判定を行っているのが原因とのことです。(参考コード)
#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
...
if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) ||
memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) {
r = SSH_ERR_INVALID_FORMAT;
つまり元々改行コードが LF で受け取っていたものを、ブラウザから入力する形で保存したために改行コードが意図せず CRLF に変換されてしまい、その結果 OpenSSH の実装によりエラーとなってしまっていた、ということです。解決方法はシンプルで、DB に保存する際にバックエンド側で CRLF → LF に変換するだけで解決しました😅
考えてみたらこれまで鍵はフォーム(DB)に保存せずにファイルのまま取り扱っていたので、改行コードで問題というのはなかなか思い当たりませんでした。またエラーメッセージでも認証情報に問題という内容(received packet nr 6 type 51)しか得ることが出来なかったので、そこも判明するまでに時間がかかった要因でした。
常に改行コードを意識した実装というのは難しいかもしれないですが、問題が起こった際に改行コードを意識できるようにしたいところです。しかし大昔にガラケー向けのサイトを作ってた頃は文字コードに苦戦してたわけですが、今では UTF-8 を使っておけばひとまず多くの問題は回避できます。それと同じように改行コードも統一されてほしいものだなと、主に Windows ユーザーの私も思ったり。









