このブログ投稿は、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はどのような暗号化方式をとっているのか確認してみよう。
認証のフローは、チャレンジレスポンス認証と同じである。
チャレンジとして、サーバーからは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種類のメッセージがある。細かなメッセージの仕様については以下の通りである。
- [MS-NLMP]: NEGOTIATE_MESSAGE | Microsoft Learn
- クライアントからサーバーに対して認証を開始する際に送られるメッセージ
- [MS-NLMP]: CHALLENGE_MESSAGE | Microsoft Learn
- チャレンジレスポンス認証のチャレンジに相当するメッセージ
- [MS-NLMP]: AUTHENTICATE_MESSAGE | Microsoft Learn
- チャレンジレスポンス認証のレスポンスに相当するメッセージ バージョンはどこで見分けるか NTLMにはv1、v2と2つのバージョンが存在するが、どこで見分ければ良いだろうか?
バージョンを判別する方法は無さそうだ。v1とv2ではレスポンスの長さや生成方法が異なるため、長さで判別するなどの方法はとれそうだ。バージョンが異なる場合、レスポンスが異なるため、認証失敗として終了する。
紛らわしいのだが、NegotiateFlags という構造体のNTLMSSP_NEGOTIATE_VERSIONフラグはデバッグ用途でOSのバージョン情報の提供を要求するもので、これはNTLMのバージョンとは無関係である。
同様に、NegotiateFlags に含まれているNTLMSSP_NEGOTIATE_NTLMフラグはNTLM v1の拡張ではないセッションセキュリティを利用せよというフラグであり、v1を指定するものではない。
NTLM over HTTP
HTTPを利用したNTLM認証の話に移ろう。
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はそれなりに存在する。
- GitHub - macks/ruby-ntlm: NTLM authentication client for Ruby.
- GitHub - WinRb/rubyntlm
- GitHub - pyu10055/ntlm-http: ntlm authentication for http
- GitHub - at-point/net-http-ntlm: Adds NTLM authentication to HTTP requests using the rubyntlm gem. A drop-in replacement for ruby-ntlm with NTLMv2 support.
内部実装を見てみよう
それぞれの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-ntlmはruby-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!
いかがだっただろうか。
弊社の相手はエンタープライズ故に、クライアントが持つ古い技術と弊社が持つ新しい技術をブレンドして解決を試みるようなこともままある。 こういった課題はネット上に情報がそもそも無いことも多く、自身で調査して実装する面白さがある。 弊社はとことん試行錯誤しながら課題を解決していくエンジニアを募集中である。
右上の「採用ページへ」のリンクから職種を探してみて欲しい。
参考文献
- NTLM認証とは - 意味をわかりやすく - e-words
- NTLM 認証と LDAP 認証の違いは何ですか。 - Cisco
- NTLM認証 - 通信用語の基礎知識
- NTLMとは?複合機のエラー発生時の対処法を紹介!|複合機リースの格安NO1|株式会社じむや
- チャレンジ/レスポンス認証とは - 意味をわかりやすく - e-words
- @IT:@ITハイブックス連携企画 > Windows サーバー セキュリティ徹底解説
- NT LAN Manager - Wikipedia
- Microsoft NTLM - Win32 apps | Microsoft Learn
- LMハッシュ - Wikipedia
- NTLM Overview | Microsoft Learn
- パスワードWindowsローカル SAM データベースに LAN Manager (LM) ハッシュを格納AD防止する - Windows Server | Microsoft Learn
- NTLM ユーザー認証 - Windows Server | Microsoft Learn
- チャレンジレスポンス認証とは - 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
- [MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol | Microsoft Learn
- 【図解】わかりやすいNTLM 認証の仕組みとシーケンス, pass-the-hash について | SEの道標
- Windows NTLM認証とマン・イン・ザ・ミドル攻撃 - Cyber Security Management 2004年5月号
- Data Encryption Standard - Wikipedia
- MD4 - Wikipedia
- [MS-NTHT]: NTLM Over HTTP Protocol | Microsoft Learn
- The NTLM Authentication Protocol and Security Support Provider
この記事を書いた人

Yuki Nishimura
雑食系エンジニア