MNTSQ Techブログ

リーガルテック・カンパニー「MNTSQ(モンテスキュー)」のTechブログです。

Ruby on RailsでNTLM over HTTPする

MNTSQ Tech Blog TOP > 記事一覧 > Ruby on RailsでNTLM over HTTPする

認証

このブログ投稿は、Ruby on RailsでNTLM認証を実装する必要が出たので、その対応と追加調査の記録である。

NTLMにはv1とv2が存在するが、このブログで扱うのは主にv1である。

プログラマも歩けばNTLMにあたるとはよく言ったもので、この記事を見ているあなたもおそらくうっかりNTLM対応をすることになったITエンジニアの一人だろう。そんなあなたの一助になれば幸いである。

NTLMとは

NTLM(NT LAN Manager)とは、チャレンジ/レスポンス方式を使う認証方式の1つである。前世代にLM(LAN Manager)認証という認証プロトコルがあり、その後継にあたる。SMBを介して認証を行うNTLM over SMBや、HTTPを介して認証を行うNTLM over HTTPなど、各種プロトコルを介した認証も提供されている。

DESやMD4など現在では安全でない技術を含むなどいくつかの理由で、利用は推奨されていない。ただ実態としては、社内ネットワーク複合機など一部では現役のようだ。

幸い、詳しい仕様はMicrosoftのサイトに掲載されているので、仕様の理解で困ることは無い。

チャレンジレスポンス認証とは

チャレンジレスポンス認証とは、以下のような流れで生パスワードをネットワークに流さずに認証する方式である。 以下のような流れである:

チャレンジレスポンス認証
チャレンジレスポンス認証

  • クライアントからサーバーに認証を要求
  • サーバーからランダムな値を生成してクライアントに送付(チャレンジ)
  • クライアントはパスワードとチャレンジを利用してアルゴリズムに沿って値を生成してサーバー側に送付(レスポンス)
  • サーバー側でレスポンスを検証し、問題なければ認証成功の結果を返す

NTLM v1

本題のNTLMのv1はどのような暗号化方式をとっているのか確認してみよう。

認証のフローは、チャレンジレスポンス認証と同じである。

NTLMの流れ
引用: [MS-NLMP]: NTLM Connection-Oriented Call Flow | Microsoft Learn

チャレンジとして、サーバーからは8バイトのランダムな乱数を1つクライアントに送信する。

受け取ったクライアントは、以下の流れでレスポンス(「応答鍵」とも呼ばれている)を生成する:

  • NTハッシュを生成する
    • パスワードをUNICODEとして評価する
    • MD4で暗号化する
  • LMハッシュを生成する
    • パスワードをすべて大文字に変換する
    • 14文字に満たない部分をゼロフィルする
    • それらを前半、後半で分離する
    • それぞれを KGS!@#$%[b] という文字列を用いてDESで暗号化する
    • 分離したバイト列を結合する
  • NTハッシュとLMハッシュからそれぞれレスポンスを生成する
    • それらを3分割する
    • 3つそれぞれをキーとしてチャレンジをDESで暗号化する
    • 生成されたバイト列を順番通り結合する
  • 上記の処理でできた2つのハッシュを所定のフォーマットに充当する

上記ロジックの都合でパスワードは14文字以内に制限されている。また、上記ではクライアントチャレンジを利用する拡張セッションセキュリティについては触れていない。 参考: [MS-NLMP]: NTLM v1 Authentication | Microsoft Learn 参考2: DESLなど各関数についてはappendixに記述がある

DESとは

DES(Data Encryption Standard)とは、共通鍵暗号方式の1つである。

現在は鍵長が短く安全でないと言われており、AESという別の暗号化方式に取って代わられた。

MD4とは

MD4(Message Digest 4)とは、一方向の(復元不可能な)ハッシュを生成する関数の1つである。

脆弱であることが判明したため、後継のMD5が開発された。ただ、MD5も現在は脆弱であるとされており、更に後継とされているSHA-1も非推奨となり、記事執筆現在ではSHA-2が比較的広範に利用されている。

メッセージの種類

前段の図で示された3種類のメッセージがある。細かなメッセージの仕様については以下の通りである。

バージョンを判別する方法は無さそうだ。v1とv2ではレスポンスの長さや生成方法が異なるため、長さで判別するなどの方法はとれそうだ。バージョンが異なる場合、レスポンスが異なるため、認証失敗として終了する。

紛らわしいのだが、NegotiateFlags という構造体のNTLMSSP_NEGOTIATE_VERSIONフラグはデバッグ用途でOSのバージョン情報の提供を要求するもので、これはNTLMのバージョンとは無関係である。

同様に、NegotiateFlags に含まれているNTLMSSP_NEGOTIATE_NTLMフラグはNTLM v1の拡張ではないセッションセキュリティを利用せよというフラグであり、v1を指定するものではない。

NTLM over HTTP

HTTPを利用したNTLM認証の話に移ろう。 HTTPを経由して認証する場合、以下のような流れになる:

NTLM over HTTP
NTLM over HTTP

実装は比較的簡単で、最初のNEGOTIATE_MESSAGEはBase64エンコードしてAuthorization HTTPヘッダーに「NTLM (Base64エンコードしたNEGOTIATE_MESSAGE)」の形式で指定する。

サーバーはステータスコード401と共にWWW-Authorization HTTPヘッダーにチャレンジを返却してくるので、それに対してまたAuthorization HTTPヘッダーに「NTLM (Base64エンコードしたレスポンス)」を指定して応答する。

するとサーバー側から成功した場合は2xx系のステータスを、失敗した場合は4xx系のステータスを返却する。

これで認証は完了である。

Rubyのgem

NTLM自体はかなり古いが(1990年代)、おそらく今なお一部で利用されていることもあり、ありがたいことにNTLMのgemはそれなりに存在する。

内部実装を見てみよう

それぞれのgemの内部実装を眺めてみよう。

ruby-ntlm

ruby-ntlmはNTLM v1 over HTTP/IMAP/SMTPに対応したgemである。

各Net::HTTPなどのNet系モジュールを拡張するかたちとなっている。拡張部分は至ってシンプルに実装されている。NTLM実装部分はNTLMモジュールの中にまとまっており、各メッセージはMessageクラスの下に収まっている。

各所に2.2.2.1など仕様書のナンバリングが見受けられ、また、メソッドの分割単位やフラグの名称も仕様書に倣った内容となっており、仕様書をよく読み下していることが伺える。

サンプルのプログラムも同梱されており、好感が持てる。

rubyntlm

rubyntlmはNTLM v1/v2 over HTTP/IMAP/SMTPに対応したgemである。

インデントが揃っていない、Type1-3はそうなのだがわかりやすくはない、1文字や2文字の短い変数名が頻出する、メソッドがスネークケースで揃っていないなどあまりお行儀は良くない内容だ。

html-http

ntlm-httpはNTLM v1/v2 over HTTP/IMAP/SMTPに対応したgemである。

こちらもタブインデントとスペースインデントが混在していたり、前段のrubyntlmと同じものを踏襲したものと推測される。

ほとんどのメソッドが800行程度の1ファイルに入れられており、見通しも良くない。

net-html-http

net-http-ntlmruby-ntlm gemにNTLMv2サポートした代替gemである。サポートするNTLMv2 over HTTPをサポートする。

実装は確かにruby-ntlmと同様の実装となっている。ruby-ntlmをラッピングし、NTLMv2のサポートをする部分だけの薄いgemである。 実装してみよう この中でどれを利用するかというとruby-ntlmかなと判断した。

これで実装してみよう。と言いたいところだが、実装は至って簡単で、以下が全容である。

http = Net::HTTP.new(@host_name)

request = Net::HTTP::Get.new(@path)
request.ntlm_auth(@user, @domain, @password)
http.request(request)

We are hiring!

いかがだっただろうか。

弊社の相手はエンタープライズ故に、クライアントが持つ古い技術と弊社が持つ新しい技術をブレンドして解決を試みるようなこともままある。 こういった課題はネット上に情報がそもそも無いことも多く、自身で調査して実装する面白さがある。 弊社はとことん試行錯誤しながら課題を解決していくエンジニアを募集中である。

右上の「採用ページへ」のリンクから職種を探してみて欲しい。

参考文献

この記事を書いた人

Yuki Nishimura

雑食系エンジニア