はじめに
このドキュメントでは、RADIUSオーセンティケータヘッダーとメッセージオーセンティケータ属性、それらの使用方法、および検証エラーが発生する状況について説明します。
オーセンティケータヘッダー
RFC 2865によると、オーセンティケータヘッダーの長さは16バイトです。Access-Requestで使用される場合は、Request Authenticatorと呼ばれます。任意の種類の応答で使用される場合は、応答認証者と呼ばれます。これは次の目的で使用されます。
応答の認証
サーバが正しい応答オーセンティケータで応答すると、クライアントは、その応答が有効な要求に関連しているかどうかを計算できます。
クライアントは、ランダムなオーセンティケータヘッダーを含む要求を送信します。次に、応答を送信したサーバは、共有シークレットとともに要求パケットを使用して応答オーセンティケータを計算します。
ResponseAuth = MD5(Code + ID + Length + RequestAuth + Attributes + Secret)
応答を受信したクライアントは、同じ操作を実行します。結果が同じ場合、パケットは正しいです。
注:秘密鍵を知っている攻撃者は、要求を傍受できない限り、応答をスプーフィングすることはできません。
検証の失敗が予想される状況:
スイッチが要求をキャッシュしなくなった場合(タイムアウトなど)、検証エラーが発生します。 共有秘密が無効な場合にも発生します(はい。Access-Rejectにもこのヘッダーが含まれます)。 このようにして、ネットワークアクセスデバイス(NAD)は共有秘密の不一致を検出できます。通常は、認証、認可、アカウンティング(AAA)クライアント/サーバから共有キーの不一致として報告されますが、詳細は明らかになりません。
パスワードの非表示
オーセンティケータヘッダーは、プレーンテキストでUser-Password属性を送信しないようにするためにも使用されます。まず、Message Digest 5(MD5 - secret, authenticator)が計算されます。その後、パスワードのチャンクを使用して複数のXOR操作が実行されます。MD5は強力な一方向アルゴリズムとして認識されなくなったため、この方法はオフライン攻撃(レインボーテーブル)に対して脆弱です。
ユーザパスワードを計算するPythonスクリプトを次に示します。
def Encrypt_Pass(password, authenticator, secret):
m = md5()
m.update(secret+authenticator)
return "".join(chr(ord(x) ^ ord(y)) for x, y in zip(password.ljust
(16,'\0')[:16], m.digest()[:16]))
再転送
RADIUS Access-Requestのいずれかの属性(RADIUS ID、User-Nameなど)が変更された場合、新しいAuthenticatorフィールドを生成し、それに依存する他のすべてのフィールドを再計算する必要があります。再送信の場合は、何も変更されません。
アカウンティング
オーセンティケータヘッダーの意味は、アクセス要求とアカウンティング要求では異なります。
Access-Requestの場合、オーセンティケータはランダムに生成され、正しく計算されたResponseAuthenticatorによる応答を受信することが想定されます。これにより、応答がその特定の要求に関連していることが証明されます。
Accounting-Requestの場合、オーセンティケータはランダムではありませんが、計算されます(RFC 2866準拠)。
RequestAuth = MD5(Code + ID + Length + 16 zero octets + Attributes + Secret)
このようにして、サーバはアカウンティングメッセージをすぐに確認し、再計算された値がオーセンティケータの値と一致しない場合はパケットをドロップできます。Identity Services Engine(ISE)は次の情報を返します。
11038 RADIUS Accounting-Request header contains invalid Authenticator field
この一般的な理由は、共有秘密キーが正しくないことです。
メッセージ認証属性
Message-Authenticator属性は、RFC 3579で定義されているRADIUS属性です。署名と検証という同様の目的で使用されます。しかし今回は、応答ではなく要求を検証するために使用されます。
Access-Requestを送信するクライアント(Access-Challengeで応答するサーバにすることもできます)は、自身のパケットからHash-Based Message Authentication Code(HMAC)-MD5を計算し、Message-Authenticator属性を署名として追加します。その後、サーバは同じ動作を実行していることを確認できます。
この式は、オーセンティケータヘッダーに似ています。
Message-Authenticator = HMAC-MD5 (Type, Identifier, Length, Request Authenticator,
Attributes)
HMAC-MD5関数は、次の2つの引数をとります。
- パケットのペイロード。ゼロで埋められた16バイトのMessage-Authenticatorフィールドが含まれます。
- 共有秘密鍵
いつメッセージ認証を使用するか:
メッセージオーセンティケータは、拡張認証プロトコル(EAP)メッセージ(RFC 3579)を含むすべてのパケットに使用する必要があります。 これには、Access-Requestを送信するクライアントと、Access-Challengeで応答するサーバの両方が含まれます。検証が失敗すると、相手側は通知せずにパケットを廃棄します。
検証の失敗が予想される状況:
共有シークレットが無効な場合、検証エラーが発生します。その後、AAAサーバは要求を検証できません。
ISEは次のように報告します。
11036 The Message-Authenticator Radius Attribute is invalid.
これは通常、EAPメッセージが添付された後の段階で発生します。802.1xセッションの最初のRADIUSパケットにはEAPメッセージが含まれていません。Message-Authenticatorフィールドがないため要求を確認できませんが、その段階で、クライアントはAuthenticatorフィールドを使用して応答を検証できます。
メッセージ認証属性の検証
正しく計算されることを確認するために、手動で値をカウントする方法を説明する例を示します。
パケット番号30(Access-Request)が選択されています。これはEAPセッションの途中であり、パケットにはMessage-Authenticatorフィールドが含まれています。目的は、メッセージオーセンティケータが正しいことを確認することです。
- Radius Protocolを右クリックし、Export selected packet bytesを選択します。
- そのRADIUSペイロードをファイル(バイナリデータ)に書き込みます。
- Message-Authenticatorフィールドを計算するには、このフィールドにゼロを入力し、HMAC-MD5を計算する必要があります。
たとえば、vimなどの16進数/バイナリエディタを使用する場合、「:%!xxd」と入力すると、16進数モードに切り替わり、「5012」の後から16バイトがゼロになります(12進数はメッセージオーセンティケータのタイプであるdecで80、12はAttribute Value Pairs(AVP)ヘッダーを含む18のサイズ)。
vimやその他のテキストエディタは、保存時にファイルの最後に余分なラインフィード文字(16進数表記では'0a')を追加できることに注意してください。テキストエディタ(vim -bファイル)のバイナリモードを使用して、LineFeed文字が表示されないようにします。
その変更後、ペイロードの準備が整います。16進/バイナリモード(タイプ: ":%!xxd -r")に戻り、ファイル(":wq")を保存する必要があります。 「hexdump -C file」を使用すると、余分なラインフィード文字が追加されていないかどうかを再確認できます。
- OpenSSLを使用してHMAC-MD5を計算します。
pluton # cat packet30-clear-msgauth.bin | openssl dgst -md5 -hmac 'cisco'
(stdin)= 01418d3b1865556918269d3cf73608b0
HMAD-MD5関数は2つの引数をとります。標準入力(stdin)の最初の引数はメッセージ自体で、2番目の引数は共有秘密です(この例ではCisco)。 結果は、RADIUS Access-Requestパケットに付加されたMessage-Authenticatorとまったく同じ値になります。
Pythonスクリプトを使用して同じ計算を行うことができます。
Python 2:
pluton # cat hmac.py
#!/usr/bin/env python
import base64
import hmac
import hashlib
f = open('packet30-clear-msgauth.bin', 'rb')
try:
body = f.read()
finally:
f.close()
digest = hmac.new('cisco', body, hashlib.md5)
d=digest.hexdigest()
print d
pluton # python hmac.py
01418d3b1865556918269d3cf73608b0
Python 3:
import hmac
import hashlib
with open('packet30-clear-msgauth.bin', 'rb') as f:
body = f.read()
digest = hmac.new(b'cisco', body, hashlib.md5)
print(digest.hexdigest())
前の例は、Access-RequestからMessage-Authenticatorフィールドを計算する方法を示しています。Access-Challenge、Access-Accept、およびAccess-Rejectの場合、ロジックはまったく同じですが、前のAccess-Requestパケットで提供されるRequest Authenticatorを使用する必要があることを覚えておくことが重要です。
関連情報