Azure VM中无需存储账户访问密钥生成Blob SAS令牌的替代方案
问题描述
我的Web应用由ReactJS SPA前端和部署在Azure VM中的Web API后端构成。用户上传文件时,后端会生成带SAS令牌的URL,前端通过该URL直接从浏览器上传文件到Azure存储Blob容器,无需经过后端VM中转。
目前后端用以下代码生成带SAS令牌的URL:
function generate_signed_url(BLOB_NAME, AZURE_PRIMARY_KEY) { const ACCOUNT_NAME = process.env.AZURE_BLOB_STORAGE_ACCOUNT_NAME; const CONTAINER_NAME = process.env.AZURE_BLOB_STORAGE_CONTAINER_NAME; const ACCESS_KEY = process.env.AZURE_BLOB_STORAGE_ACCESS_KEY; const sharedKeyCredential = new StorageSharedKeyCredential(accountName=ACCOUNT_NAME, accountKey=ACCESS_KEY) //This URL will be valid for 1 hour const expDate = new Date(new Date().valueOf() + 3600 * 1000); // Generate service level SAS for a blob const blobSAS = generateBlobSASQueryParameters({ containerName: CONTAINER_NAME, // Required blobName: BLOB_NAME, // Required permissions: BlobSASPermissions.parse("racwd"), // Required startsOn: new Date(), // Optional expiresOn: expDate, // Required. Date type cacheControl: "cache-control-override", // Optional contentDisposition: "content-disposition-override", // Optional contentEncoding: "content-encoding-override", // Optional contentLanguage: "content-language-override", // Optional contentType: "content-type-override", // Optional ipRange: { start: "0.0.0.0", end: "255.255.255.255" }, // Optional protocol: SASProtocol.HttpsAndHttp, // Optional version: "2016-05-31" // Optional }, sharedKeyCredential ).toString(); SaSURL = `https://${ACCOUNT_NAME}.blob.core.windows.net/${AZURE_CONTAINER}/${AZURE_BLOB}?${containerSAS}`; console.log(`SAS URL for blob is: ${SaSURL}`); return SaSURL; }
但这种方式需要把Azure存储账户访问密钥存在VM的环境变量里,属于不安全的实践:
const ACCESS_KEY = process.env.AZURE_BLOB_STORAGE_ACCESS_KEY
请问有没有更专业的替代方案?
替代方案
以下是几种更安全合规的替代方案,避免直接存储访问密钥:
1. 使用Azure托管标识(Managed Identity)
这是Azure推荐的首选方案,完全不需要存储任何密钥:
- 给Azure VM启用系统分配或用户分配的托管标识
- 给该托管标识授予Azure存储账户的Storage Blob Data Contributor(或更细粒度的权限,比如仅允许生成SAS的权限)角色
- 后端代码中使用Azure Identity SDK获取令牌,替代共享密钥生成SAS:
const { DefaultAzureCredential } = require("@azure/identity"); const { BlobServiceClient, BlobSASPermissions } = require("@azure/storage-blob"); async function generate_signed_url(BLOB_NAME) { const ACCOUNT_NAME = process.env.AZURE_BLOB_STORAGE_ACCOUNT_NAME; const CONTAINER_NAME = process.env.AZURE_BLOB_STORAGE_CONTAINER_NAME; // 使用托管标识获取凭证 const credential = new DefaultAzureCredential(); const blobServiceClient = new BlobServiceClient( `https://${ACCOUNT_NAME}.blob.core.windows.net`, credential ); const expDate = new Date(Date.now() + 3600 * 1000); const containerClient = blobServiceClient.getContainerClient(CONTAINER_NAME); const blobClient = containerClient.getBlobClient(BLOB_NAME); // 生成SAS令牌 const sasToken = await blobClient.generateSasUrl({ permissions: BlobSASPermissions.parse("racwd"), expiresOn: expDate, // 其他可选参数按需设置 }); console.log(`SAS URL for blob is: ${sasToken}`); return sasToken; } - 优势:无需管理密钥,Azure自动处理身份验证,权限可通过IAM精细控制,泄露风险为零。
2. 使用Azure Key Vault存储访问密钥
如果暂时无法使用托管标识,可将存储账户访问密钥存入Azure Key Vault:
- 创建Azure Key Vault实例,将存储账户的访问密钥作为机密存入Vault
- 给VM的托管标识(或服务主体)授予Key Vault的Secret User权限,允许读取该机密
- 后端代码从Key Vault中获取访问密钥,而非从环境变量读取:
const { SecretClient } = require("@azure/keyvault-secrets"); const { DefaultAzureCredential } = require("@azure/identity"); const { StorageSharedKeyCredential, generateBlobSASQueryParameters, BlobSASPermissions } = require("@azure/storage-blob"); async function generate_signed_url(BLOB_NAME) { const ACCOUNT_NAME = process.env.AZURE_BLOB_STORAGE_ACCOUNT_NAME; const CONTAINER_NAME = process.env.AZURE_BLOB_STORAGE_CONTAINER_NAME; const KEY_VAULT_URL = process.env.AZURE_KEY_VAULT_URL; const SECRET_NAME = "AzureBlobStorageAccessKey"; // Key Vault中存储密钥的机密名称 // 从Key Vault获取访问密钥 const credential = new DefaultAzureCredential(); const secretClient = new SecretClient(KEY_VAULT_URL, credential); const secret = await secretClient.getSecret(SECRET_NAME); const ACCESS_KEY = secret.value; // 生成SAS逻辑 const sharedKeyCredential = new StorageSharedKeyCredential(ACCOUNT_NAME, ACCESS_KEY); const expDate = new Date(Date.now() + 3600 * 1000); const blobSAS = generateBlobSASQueryParameters({ containerName: CONTAINER_NAME, blobName: BLOB_NAME, permissions: BlobSASPermissions.parse("racwd"), expiresOn: expDate, // 其他参数按需设置 }, sharedKeyCredential).toString(); const sasUrl = `https://${ACCOUNT_NAME}.blob.core.windows.net/${CONTAINER_NAME}/${BLOB_NAME}?${blobSAS}`; console.log(`SAS URL for blob is: ${sasUrl}`); return sasUrl; } - 优势:密钥集中存储在安全的Vault中,可设置自动轮换,权限受控,比直接存在VM环境变量安全得多。
3. 使用存储账户的用户委派SAS(User Delegation SAS)
如果需要更高的安全性和审计能力,可使用用户委派SAS:
- 基于Azure AD身份生成SAS,而非存储账户密钥
- 给身份授予Storage Blob Data Contributor或Storage Blob Data Owner角色
- 代码中通过Azure AD令牌获取用户委派密钥,再生成SAS:
const { DefaultAzureCredential } = require("@azure/identity"); const { BlobServiceClient, generateBlobSASQueryParameters, BlobSASPermissions } = require("@azure/storage-blob"); async function generate_signed_url(BLOB_NAME) { const ACCOUNT_NAME = process.env.AZURE_BLOB_STORAGE_ACCOUNT_NAME; const CONTAINER_NAME = process.env.AZURE_BLOB_STORAGE_CONTAINER_NAME; const credential = new DefaultAzureCredential(); const blobServiceClient = new BlobServiceClient( `https://${ACCOUNT_NAME}.blob.core.windows.net`, credential ); // 获取用户委派密钥 const userDelegationKey = await blobServiceClient.getUserDelegationKey( new Date(), new Date(Date.now() + 3600 * 1000) ); const expDate = new Date(Date.now() + 3600 * 1000); const blobSAS = generateBlobSASQueryParameters({ containerName: CONTAINER_NAME, blobName: BLOB_NAME, permissions: BlobSASPermissions.parse("racwd"), expiresOn: expDate, // 其他参数按需设置 }, userDelegationKey, ACCOUNT_NAME).toString(); const sasUrl = `https://${ACCOUNT_NAME}.blob.core.windows.net/${CONTAINER_NAME}/${BLOB_NAME}?${blobSAS}`; console.log(`SAS URL for blob is: ${sasUrl}`); return sasUrl; } - 优势:SAS的权限与Azure AD身份绑定,可通过Azure AD审计日志追踪操作,比服务SAS更安全,适合敏感数据场景。
内容的提问来源于stack exchange,提问作者Kid_Learning_C
相关产品推荐
相关产品推荐

