モバイルアプリは、長期的な認証情報を埋め込むことなく、RAM ロールの STS トークンを使用して Alibaba Cloud リソースに安全にアクセスできます。
背景情報
ある企業が Object Storage Service (OSS) を使用するモバイルアプリを開発しています。このアプリは OSS に直接接続してデータをアップロードまたはダウンロードする必要がありますが、企業が管理していないエンドユーザーのモバイルデバイス上で動作します。
この企業には以下の要件があります。
-
直接データ転送:企業は、バックエンドアプリケーションサーバ経由でデータをプロキシするのではなく、アプリが OSS に直接接続してアップロードおよびダウンロードを行うことを希望しています。
-
安全な認証情報管理:企業は、AccessKey などの長期的な認証情報をモバイルアプリに埋め込むことを避ける必要があります。エンドユーザーデバイスは信頼できない実行環境です。
-
リスクコントロール:最小権限の原則に従い、各アプリが OSS に接続する際には最小限の権限と短期間のセッションを持つ必要があります。
ソリューション
OSS からデータをアップロードまたはダウンロードする必要がある場合、モバイルアプリはアプリケーションサーバから認証情報をリクエストします。アプリケーションサーバは RAM ユーザーとして、STS AssumeRole 操作を呼び出して RAM ロールを引き受け、返された STS トークンをアプリに渡します。アプリはこのトークンを使用して OSS にアクセスします。

-
アプリがアプリケーションサーバから認証情報をリクエストします。
-
RAM ロールを作成し、必要な権限を付与します。
詳細については、「RAM ロールの作成と権限の付与」をご参照ください。
-
アプリケーションサーバ用の RAM ユーザーを作成し、その RAM ユーザーが RAM ロールを引き受けられるようにします。
詳細については、「RAM ユーザーの作成とロール引き受けの許可」をご参照ください。
-
アプリケーションサーバが STS AssumeRole 操作を呼び出して、RAM ロール用の STS トークンを取得します。
詳細については、「アプリケーションサーバでの STS トークンの取得」をご参照ください。
-
アプリケーションサーバは、各アプリクライアントに対するより詳細な制御のために、STS トークンの権限をさらに制限できます。
詳細については、「STS トークンの権限制限」をご参照ください。
-
アプリがデータを直接アップロードまたはダウンロードする必要がある場合、STS トークンを使用して OSS にアクセスします。
詳細については、「アプリから STS トークンを使用して OSS にアクセスする」をご参照ください。
RAM ロールの作成と権限の付与
ご利用の Alibaba Cloud アカウント ID が 123456789012**** であると仮定します。
-
Alibaba Cloud アカウントを使用して、
oss-objectmanagerという名前の RAM ロールを作成し、プリンシパルタイプとして アカウント を選択します。説明RAM ロールを作成する際は、信頼されたプリンシパルとして 現在のアカウント を選択してください。これにより、同じ Alibaba Cloud アカウントに属する RAM ユーザーのみがこの RAM ロールを引き受けられるようになります。
詳細については、「Alibaba Cloud アカウント用の RAM ロールの作成」をご参照ください。
RAM ロールの作成後、ロール詳細ページで ARN と信頼ポリシーを確認できます。
-
RAM ロールの ARN は
acs:ram::123456789012****:role/oss-objectmanagerです。 -
RAM ロールの信頼ポリシーは次のとおりです。
説明以下の信頼ポリシーにより、Alibaba Cloud アカウント
123456789012****内の任意の認証済みプリンシパルがロールを引き受けられるようになります。{ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "RAM": [ "acs:ram::123456789012****:root" ] } } ], "Version": "1" }
-
-
oss-objectmanagerRAM ロールに、AliyunOSSFullAccessシステムポリシーをアタッチします。詳細については、「RAM ロールへの権限付与」をご参照ください。
RAM ユーザーの作成とロール引き受けの許可
-
アプリケーションサーバ用に
appserverという名前の RAM ユーザーを作成します。詳細については、「RAM ユーザーの作成」をご参照ください。
-
appserverRAM ユーザーにAliyunSTSAssumeRoleAccess権限を付与して、ロールの引き受けを許可します。詳細については、「RAM ユーザーへの権限付与」をご参照ください。
STS トークンの取得
-
アプリケーションサーバが RAM ユーザーの AccessKey を使用して、STS AssumeRole 操作を呼び出します。
説明-
Alibaba Cloud Command Line Interface (CLI) を使用する前に、アプリケーションサーバにインストールし、RAM ユーザーの認証情報を設定する必要があります。詳細については、「STS CLI リファレンス」をご参照ください。
-
この例では、
Policyパラメーターを指定していません。そのため、返された STS トークンはoss-objectmanagerRAM ロールからすべての権限を継承します。STS トークンの権限をさらに制限するには、「STS トークンの権限制限」をご参照ください。
次の例は、Alibaba Cloud CLI を使用して AssumeRole 操作を呼び出す方法を示しています。
aliyun sts AssumeRole --RoleArn 'acs:ram::123456789012****:role/oss-objectmanager' --RoleSessionName 'client-001' -
-
STS がアプリケーションサーバに STS トークンを返します。STS トークンには
AccessKeyId、AccessKeySecret、およびSecurityTokenが含まれます。次のコードはサンプル応答です。
{ "AssumedRoleUser": { "AssumedRoleId": "391578752573****:client-001", "Arn": "acs:ram::123456789012****:role/oss-objectmanager/client-001" }, "Credentials": { "AccessKeySecret": "yourAccessKeySecret", "SecurityToken": "yourSecurityToken", "Expiration": "2016-01-13T15:02:37Z", "AccessKeyId": "yourAccessKeyId" }, "RequestId": "E1779AAB-E7AF-47D6-A9A4-53128708B6CE" }説明SecurityTokenの有効期限は短くなっています。より長いセッションが必要な場合は、アプリケーションサーバが定期的に(たとえば 1800 秒ごとに)STS トークンを再発行する必要があります。
STS トークンの権限制限
AssumeRole 呼び出しの Policy パラメーターを使用して、ユーザーまたはデバイスに基づいて STS トークンの権限を制限し、権限昇格のリスクを低減できます。
次の例では、sample-bucket/2015/01/01/*.jpg パスに一致するオブジェクトをダウンロードする権限を付与します。
-
サンプルリクエスト
aliyun sts AssumeRole --RoleArn 'acs:ram::123456789012****:role/oss-objectmanager' --RoleSessionName 'client-002' --Policy '{"Version":"1", "Statement": [{"Effect":"Allow", "Action":"oss:GetObject", "Resource":"acs:oss:*:*:sample-bucket/2015/01/01/*.jpg"}]}'説明STS トークンのデフォルトの有効期間は 3,600 秒です。
DurationSecondsパラメーターを使用して、別の有効期間を設定できます。詳細については、「AssumeRole」をご参照ください。 -
サンプル応答
{ "AssumedRoleUser": { "AssumedRoleId": "391578752573****:client-002", "Arn": "acs:ram::123456789012****:role/oss-objectmanager/client-002" }, "Credentials": { "AccessKeySecret": "yourAccessKeySecret", "SecurityToken": "yourSecurityToken", "Expiration": "2016-01-13T15:03:39Z", "AccessKeyId": "yourAccessKeyId" }, "RequestId": "98835D9B-86E5-4BB5-A6DF-9D3156ABA567" }
アプリから STS トークンを使用して OSS にアクセスする
-
アプリケーションサーバが STS トークンをモバイルアプリに渡します。
-
モバイルアプリが STS トークンを使用して OSS にアクセスします。
次の例では、OSS Android SDK を使用して、STS トークンで OSS からファイルをダウンロードします。
// yourEndpoint をバケットが配置されているリージョンのエンドポイントに置き換えます。たとえば、中国 (杭州) リージョンの場合、エンドポイントは https://oss-cn-hangzhou.aliyuncs.com です。 String endpoint = "yourEndpoint"; // yourRegion をバケットが配置されているリージョンの ID に置き換えます。たとえば、中国 (杭州) リージョンの場合、リージョン ID は cn-hangzhou です。 String region = "yourRegion"; // STS から取得した一時的な AccessKey ID および AccessKey Secret。 String accessKeyId = "yourAccessKeyId"; String accessKeySecret = "yourAccessKeySecret"; // STS から取得したセキュリティトークン。 String securityToken = "yourSecurityToken"; OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(accessKeyId, accessKeySecret, securityToken); ClientConfiguration config = new ClientConfiguration(); config.setSignVersion(SignVersion.V4); // OSSClient インスタンスを作成します。 OSSClient oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider); oss.setRegion(region); // ダウンロードリクエストを構築します。 GetObjectRequest get = new GetObjectRequest("sample-bucket", "2015/01/01/grass.jpg"); OSSAsyncTask task = oss.asyncGetObject(get, new OSSCompletedCallback<GetObjectRequest, GetObjectResult>() { @Override // 成功すると、GetObject リクエストは入力ストリームを含む GetObjectResult を返します。ストリームを処理する必要があります。 public void onSuccess(GetObjectRequest request, GetObjectResult result) { // リクエストが成功しました。 Log.d("asyncGetObject", "DownloadSuccess"); Log.d("Content-Length", "" + result.getContentLength()); try (InputStream inputStream = result.getObjectContent()) { byte[] buffer = new byte[2048]; int len; while ((len = inputStream.read(buffer)) != -1) { // データを処理します。 } } catch (IOException e) { e.printStackTrace(); } } @Override // リクエストが失敗すると onFailure コールバックがトリガーされます。例外を処理してください。 public void onFailure(GetObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { if (clientExcepion != null) { // ネットワークエラーなどのクライアント側の例外を処理します。 clientExcepion.printStackTrace(); } if (serviceException != null) { // サービス側の例外を処理します。 Log.e("ErrorCode", serviceException.getErrorCode()); Log.e("RequestId", serviceException.getRequestId()); Log.e("HostId", serviceException.getHostId()); Log.e("RawMessage", serviceException.getRawMessage()); } } }); // タスクをキャンセルします。 // task.cancel(); // タスクの完了を待ちます。 // task.waitUntilFinished();