hmac-auth プラグインは、ハッシュベースのメッセージ認証コード(HMAC)アルゴリズムに基づいて HTTP リクエストの改ざん不可能な署名を生成するために使用されます。署名は ID 認証に使用されます。このトピックでは、hmac-auth プラグインを設定する方法について説明します。
プラグインの種類
認証プラグイン。
フィールド
認証設定
名前 | データ型 | 必須 | デフォルト値 | 説明 |
consumers | オブジェクトの配列 | はい | - | サービスの呼び出し元。このフィールドは、リクエストを認証するために使用されます。 |
date_offset | 数値 | いいえ | - | クライアントの最大時間オフセット(秒単位)。システムはこのフィールドを使用して、リクエストの |
global_auth | 文字列の配列 | いいえ(インスタンスレベルの設定にのみ必要) | - | global_auth が true に設定されている場合、認証メカニズムはグローバルに有効になります。 global_auth が false に設定されている場合、認証メカニズムは設定したドメイン名とルートに対してのみ有効になります。 global_auth が設定されていない場合、認証メカニズムはドメイン名とルートが設定されていない場合にのみグローバルに有効になります。 |
次の表は、consumers
フィールドの設定項目について説明しています。
名前 | データ型 | 必須 | デフォルト値 | 説明 |
key | 文字列 | はい | - | コンシューマーのアクセスキー。 |
secret | 文字列 | はい | - | 署名の生成に使用されるシークレット。 |
name | 文字列 | はい | - | コンシューマーの名前。 |
承認設定(オプション)
名前 | データ型 | 必須 | デフォルト値 | 説明 |
allow | 文字列の配列 | いいえ(インスタンスレベル以外の設定に必要) | - | このフィールドは、ルートやドメイン名などの細かい粒度でのみ設定できます。一致条件を満たすリクエストに対して、アクセスを許可するコンシューマーを設定できます。これにより、きめ細かい権限制御が実装されます。 |
承認設定と認証設定は、1 つのルールで共存できません。
認証されたリクエストの場合、
X-Mse-Consumer
フィールドがリクエストヘッダーに追加され、呼び出し元の名前が識別されます。
設定例
グローバルに認証を設定し、ルートの承認を設定する
このセクションでは、ゲートウェイの特定のルートまたはドメインに対して hmac-auth プラグインに基づく認証を有効にする方法の例を示します。 key
フィールドは一意である必要があります。
インスタンスレベルで次のプラグイン設定を適用します:
global_auth: false
consumers:
- key: appKey-example-1 // コンシューマー 1 のアクセスキー
secret: appSecret-example-1 // コンシューマー 1 のシークレット
name: consumer-1 // コンシューマー 1 の名前
- key: appKey-example-2 // コンシューマー 2 のアクセスキー
secret: appSecret-example-2 // コンシューマー 2 のシークレット
name: consumer-2 // コンシューマー 2 の名前
route-a
ルートと route-b
ルートに次のプラグイン設定を適用します:
allow:
- consumer1
*.example.com
ドメイン名と test.com
ドメイン名に次のプラグイン設定を適用します:
allow:
- consumer2
この例では、
route-a
ルートとroute-b
ルートは、ゲートウェイルートの作成時に指定されたルートです。クライアントリクエストがいずれかのルートと一致する場合、name
がconsumer1
である呼び出し元はゲートウェイへのアクセスを許可されます。他の呼び出し元はゲートウェイへのアクセスを許可されません。この例では、
*.example.com
ドメイン名とtest.com
ドメイン名は、リクエスト内のドメイン名と照合するために使用されます。クライアントリクエストがいずれかのドメイン名と一致する場合、name
がconsumer2
である呼び出し元はゲートウェイへのアクセスを許可されます。他の呼び出し元はゲートウェイへのアクセスを許可されません。
ゲートウェイの認証を有効にする
global_auth: true
consumers:
- key: appKey-example-1 // コンシューマー 1 のアクセスキー
secret: appSecret-example-1 // コンシューマー 1 のシークレット
name: consumer-1 // コンシューマー 1 の名前
- key: appKey-example-2 // コンシューマー 2 のアクセスキー
secret: appSecret-example-2 // コンシューマー 2 のシークレット
name: consumer-2 // コンシューマー 2 の名前
署名メカニズム
設定の準備
署名の生成と検証に必要な次の資格情報設定を行う必要があります。
key:リクエストヘッダー
x-ca-key
で使用されます。secret:リクエスト署名の生成に使用されます。
クライアントで署名を生成する
概要
署名を生成するために、クライアントは次の手順を実行します。
元のリクエストからキーデータを抽出し、署名文字列を生成します。
暗号化アルゴリズムと設定された
secret
を使用して署名文字列を暗号化し、署名を生成します。署名に関連するすべてのヘッダーを元の HTTP リクエストに追加して、最終的な HTTP リクエストを取得します。
署名文字列を抽出する
署名を生成するために、クライアントは HTTP リクエストからキーデータを抽出し、キーデータを署名文字列に結合する必要があります。署名文字列の形式は次のとおりです。
HTTPMethod
Accept
Content-MD5
Content-Type
Date
Headers
PathAndParameters
上記の 7 つのフィールドは署名文字列を構成し、\n で区切られます。 Headers フィールドを空のままにする場合、\n は不要です。他のフィールドを空のままにする場合、\n は保持する必要があります。署名は大文字と小文字が区別されます。
各フィールドを抽出するためのルール:
HTTPMethod:リクエストの送信に使用される HTTP メソッド(例:POST)。このフィールドの値は大文字です。
Accept:リクエストの Accept ヘッダーの値。 Accept ヘッダーを空のままにすることができます。リクエストで Accept ヘッダーを明示的に指定することをお勧めします。 Accept ヘッダーが空の場合、一部の HTTP クライアントは Accept のデフォルト値として
*/*
を使用します。その結果、署名検証は失敗します。Content-MD5:Content-MD5 ヘッダーの値。空のままにすることができます。値は、リクエストに非フォームタイプの本文が含まれている場合にのみ計算されます。次の例は、Java 形式で Content-MD5 ヘッダーの値を計算する方法を示しています。
String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));
Content-Type:Content-Type ヘッダーの値。空のままにすることができます。
Date:Date ヘッダーの値。時間オフセット検証に使用されます。
date_offset
が指定されていない場合、このフィールドを空のままにすることができます。Headers:署名の計算に使用するヘッダーを指定できます。署名文字列を連結する場合、次のルールが適用されます。
ヘッダーキーは最初にアルファベット順にソートされ、次に次のルールに基づいて連結されます。
HeaderKey1 + ":" + HeaderValue1 + "\n"\+ HeaderKey2 + ":" + HeaderValue2 + "\n"\+ ... HeaderKeyN + ":" + HeaderValueN + "\n"
ヘッダーの値が空の場合、署名には HeaderKey + ":" + "\n" を使用します。ヘッダーキーとコロン(:)は保持する必要があります。
署名に使用されるヘッダーキーはコンマ(,)で区切られ、キーが
X-Ca-Signature-Headers
であるヘッダーに配置されます。次のヘッダーを署名の計算に使用することはできません:X-Ca-Signature、X-Ca-Signature-Headers、Accept、Content-MD5、Content-Type、および Date。
PathAndParameters フィールドには、すべてのパス、クエリ、およびフォームパラメーターが含まれています。
Path + "?" + Key1 + "=" + Value1 + "&" + Key2 + "=" + Value2 + ... "&" + KeyN + "=" + ValueN
クエリパラメーターとフォームパラメーターのキーは最初にアルファベット順にソートされ、次に上記の方法を使用して連結されます。
クエリパラメーターとフォームパラメーターを空のままにする場合、署名情報を追加せずにパスパラメーターを使用できます。
一部のクエリパラメーターとフォームパラメーターが、同じキーで異なる値を持つ配列パラメーターである場合、最初の値が署名計算に使用されます。
署名文字列の抽出例
初期 HTTP リクエスト:
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
accept:application/json; charset=utf-8
ca_version:1
content-type:application/x-www-form-urlencoded; charset=utf-8
x-ca-timestamp:1525872629832
date:Wed, 09 May 2018 13:30:29 GMT+00:00
user-agent:ALIYUN-ANDROID-DEMO
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
content-length:33
username=xiaoming&password=123456789
生成された署名文字列:
POST
application/json; charset=utf-8
application/x-www-form-urlencoded; charset=utf-8
Wed, 09 May 2018 13:30:29 GMT+00:00
x-ca-key:203753385
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
x-ca-signature-method:HmacSHA256
x-ca-timestamp:1525872629832
/http2test/test?param1=test&password=123456789&username=xiaoming
署名の計算
クライアントが HTTP リクエストからキーデータを抽出し、署名文字列を生成した後、クライアントは署名文字列を暗号化およびエンコードして最終的な署名を形成する必要があります。
暗号化プロセスでは、stringToSign
は抽出された署名文字列を指定し、secret
はプラグイン設定で指定され、sign
は最終的な署名を指定します。
Mac hmacSha256 = Mac.getInstance("HmacSHA256"); // HmacSHA256 アルゴリズムを使用して MAC オブジェクトを初期化します。
byte[] secretBytes = secret.getBytes("UTF-8"); // シークレットを UTF-8 バイト配列に変換します。
hmacSha256.init(new SecretKeySpec(secretBytes, 0, secretBytes.length, "HmacSHA256")); // シークレットバイト配列を使用して HmacSHA256 アルゴリズムを初期化します。
byte[] result = hmacSha256.doFinal(stringToSign.getBytes("UTF-8")); // 署名文字列を UTF-8 バイト配列に変換し、HmacSHA256 アルゴリズムを使用して暗号化します。
String sign = Base64.encodeBase64String(result); // 暗号化された結果を Base64 文字列にエンコードします。
要約すると、stringToSign
は UTF-8 を使用してバイト配列にデコードされます。バイト配列は暗号化アルゴリズムを使用して暗号化され、次に Base64 アルゴリズムを使用してエンコードされて署名が生成されます。
署名の追加
クライアントは、署名検証のために API Gateway に送信されるリクエストに次のヘッダーを含める必要があります。
x-ca-key:AppKey。必須です。
x-ca-signature-method:署名アルゴリズム。オプションです。有効な値:HmacSHA256 および HmacSHA1。デフォルト値:HmacSHA256。
x-ca-signature-headers:すべての署名ヘッダーのキーのコレクション。オプションです。キーはコンマ(,)で区切られます。
x-ca-signature:署名。必須です。
次の例は、署名付きの HTTP リクエストを示しています。
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
accept:application/json; charset=utf-8
ca_version:1
content-type:application/x-www-form-urlencoded; charset=utf-8
x-ca-timestamp:1525872629832
date:Wed, 09 May 2018 13:30:29 GMT+00:00
user-agent:ALIYUN-ANDROID-DEMO
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
x-ca-key:203753385
x-ca-signature-method:HmacSHA256
x-ca-signature-headers:x-ca-timestamp,x-ca-key,x-ca-nonce,x-ca-signature-method
x-ca-signature:xfX+bZxY2yl7EB/qdoDy9v/uscw3Nnj1pgoU+Bm6xdM=
content-length:33
username=xiaoming&password=123456789
サーバー側で署名を検証する
手順
クライアントから受信した署名を検証するために、サーバーは次の手順を実行します。
リクエストからキーデータを抽出して署名文字列を取得します。
リクエストから
key
を読み取り、key
を使用してsecret
を取得します。暗号化アルゴリズムと
secret
を使用して署名文字列を暗号化し、署名を取得します。リクエストからクライアント側の署名を読み取り、サーバー側の署名とクライアント側の署名を比較します。
署名エラーのトラブルシューティング
署名検証が失敗した場合、システムはサーバー側の署名文字列(StringToSign)をクライアントに返される HTTP レスポンスのヘッダーに追加します。キーは X-Ca-Error-Message です。クライアント側の署名文字列(StringToSign)とサーバー側の署名文字列を比較すると、エラーを特定できます。
2 つの値が同じ場合は、署名計算に使用される AppSecret を確認してください。
HTTP ヘッダーは改行をサポートしていません。署名文字列(StringToSign)の改行は番号記号(#
)に置き換えられます。
X-Ca-Error-Message: Server StringToSign:`GET#application/json##application/json##X-Ca-Key:200000#X-Ca-Timestamp:1589458000000#/app/v1/config/keys?keys=TEST`
HTTP ステータスコード
HTTP ステータスコード | エラーメッセージ | 理由 |
400 | 無効な署名です。 | x-ca-signature リクエストヘッダーに、サーバーで計算された署名と一致しない署名が含まれています。 |
400 | 無効な Content-MD5 です。 | content-md5 リクエストヘッダーが無効です。 |
400 | 無効な日付です。 | date リクエストヘッダーに基づいて計算された時間オフセットが、設定された date_offset を超えています。 |
401 | 無効なキーです。 | x-ca-key リクエストヘッダーが指定されていないか、無効です。 |
401 | 空の署名です。 | x-ca-signature リクエストヘッダーに署名が含まれていません。 |
403 | 承認されていないコンシューマーです。 | リクエストの呼び出し元にアクセス許可がありません。 |
413 | リクエスト本文が大きすぎます。 | リクエスト本文のサイズが 32 MB を超えています。 |
413 | ペイロードが大きすぎます。 | リクエスト本文が、ゲートウェイに設定されている DownstreamConnectionBufferLimits の値を超えています。パラメーター設定ページで DownstreamConnectionBufferLimits の値を増やすことができます。 |
DownstreamConnectionBufferLimits
の値を増やすと、ゲートウェイのメモリ使用量が大幅に増加します。この操作を実行する際は注意してください。