Azure Storage账户:带SAS Url的文件上传仍遭防火墙拦截
问题:Azure Blob Storage SAS URL在受限网络下的403权限问题
我开发了一个文件共享Web应用,上传流程如下:
- 用户选择本地文件并点击上传按钮
- 向Azure VM上的后端服务发送请求,后端生成带SAS密钥的URL并返回
- 前端通过该SAS URL发送PUT请求完成文件上传
生成SAS URL的代码如下:
export function generate_AzureBlob_signed_url(CONTAINER_NAME, BLOB_NAME) { const ACCOUNT_NAME = process.env.AZURE_BLOB_STORAGE_ACCOUNT_NAME as string; const ACCOUNT_KEY = process.env.AZURE_BLOB_STORAGE_ACCOUNT_KEY as string; // TODO: 按安全最佳实践,需改用用户委托SAS而非访问密钥 const sharedKeyCredential = new StorageSharedKeyCredential(ACCOUNT_NAME, ACCOUNT_KEY) // URL有效期1小时 const expDate = new Date(new Date().valueOf() + 3600 * 1000); // 生成Blob级别的服务SAS const blobSAS = generateBlobSASQueryParameters({ containerName: CONTAINER_NAME, // 必填 blobName: BLOB_NAME, // 必填 permissions: BlobSASPermissions.parse("racwd"), // 必填 // TODO: 可进一步细化权限,比如限定HTTP方法 startsOn: new Date(), // 可选 expiresOn: expDate, // 必填,日期类型 cacheControl: "cache-control-override", // 可选 contentDisposition: "content-disposition-override", // 可选 contentEncoding: "content-encoding-override", // 可选 contentLanguage: "content-language-override", // 可选 contentType: "content-type-override", // 可选 ipRange: { start: "0.0.0.0", end: "255.255.255.255" }, // 可选 protocol: SASProtocol.HttpsAndHttp, // 可选 version: "2019-12-12" // 可选 }, 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; }
生成的SAS URL示例(解析后):
https://myapp.blob.core.windows.net/issues/issues/src-HosE/dst-HosX/2023-08-31-18-44/b01e7dd3cb2ea333/raw/BJFC00272274T500_006.dcm?sv=2019-12-12 &spr=https%2Chttp &st=2023-08-31T09%3A44%3A28Z &se=2023-08-31T10%3A44%3A28Z &sip=0.0.0.0-255.255.255.255 &sr=b &sp=racwd &sig=JIAZVvddUI5FQ1DGNIMhJQUajxOxHduzfLNq%2F4OhlIA%3D &rscc=cache-control-override &rscd=content-disposition-override &rsce=content-encoding-override &rscl=content-language-override &rsct=content-type-override
但前端发送PUT请求时返回403错误:
PUT https://myapp.blob.core.windows.net/issues/issues/src-HosE/dst-HosX/2023-08-31-18-44/b01e7dd3cb2ea333/raw/BJFC00272274T500_006.dcm?sv=2019-12-12&spr=https%2Chttp&st=2023-08-31T09%3A44%3A28Z&se=2023-08-31T10%3A44%3A28Z&sip=0.0.0.0-255.255.255.255&sr=b&sp=racwd&sig=JIAZVvddUI5FQ1DGNIMhJQUajxOxHduzfLNq%2F4OhlIA%3D&rscc=cache-control-override&rscd=content-disposition-override&rsce=content-encoding-override&rscl=content-language-override&rsct=content-type-override 403 (This request is not authorized to perform this operation.)
当前存储账户网络设置为仅允许选定虚拟网络和IP地址访问,将用户IP添加到防火墙规则后上传成功,说明SAS URL请求仍被网络防火墙拦截。需要实现:无需改为“允许全部网络访问”,让公网任意用户通过SAS URL上传文件,达到类似AWS S3预签名URL的效果。
解决方案
方案1:启用“允许受信任的Microsoft服务访问”例外
在存储账户的防火墙和虚拟网络设置中,找到“例外”区域,勾选允许受信任的Microsoft服务访问此存储账户。该选项允许通过SAS授权的请求绕过网络防火墙限制,即使请求来源IP不在允许列表内。
方案2:改用用户委托SAS(User Delegation SAS)
当前代码使用的是基于存储账户密钥的服务SAS,而用户委托SAS通过Azure AD令牌生成,安全性更高,且在网络受限场景下,用户委托SAS的请求默认可绕过存储账户防火墙限制(需确保SAS权限、有效期配置正确)。
修改后的用户委托SAS生成代码示例:
import { BlobServiceClient, generateBlobSASQueryParameters, BlobSASPermissions, SASProtocol } from "@azure/storage-blob"; import { DefaultAzureCredential } from "@azure/identity"; export async function generate_AzureBlob_user_delegation_sas(CONTAINER_NAME, BLOB_NAME) { const ACCOUNT_NAME = process.env.AZURE_BLOB_STORAGE_ACCOUNT_NAME as string; const blobServiceClient = new BlobServiceClient( `https://${ACCOUNT_NAME}.blob.core.windows.net`, new DefaultAzureCredential() ); // 获取用户委托密钥,有效期最长7天 const userDelegationKey = await blobServiceClient.getUserDelegationKey( new Date(), new Date(new Date().valueOf() + 7 * 24 * 3600 * 1000) ); // SAS有效期1小时 const expDate = new Date(new Date().valueOf() + 3600 * 1000); const blobSAS = generateBlobSASQueryParameters({ containerName: CONTAINER_NAME, blobName: BLOB_NAME, permissions: BlobSASPermissions.parse("cwd"), // 上传仅需创建、写入、删除权限,按需调整 startsOn: new Date(), expiresOn: expDate, protocol: SASProtocol.Https, // 建议仅允许HTTPS version: "2021-06-08" // 使用支持用户委托SAS的最新版本 }, userDelegationKey, ACCOUNT_NAME ).toString(); const sasUrl = `https://${ACCOUNT_NAME}.blob.core.windows.net/${CONTAINER_NAME}/${BLOB_NAME}?${blobSAS}`; console.log(`User Delegation SAS URL: ${sasUrl}`); return sasUrl; }
方案3:配置VNet服务端点或专用链接(适合后端在Azure VNet内的场景)
如果后端服务部署在Azure虚拟网络中,可为存储账户配置VNet服务端点或Azure专用链接。后端可在VNet内生成SAS URL,公网用户通过SAS URL上传时,请求会被正确路由,同时存储账户的网络限制依然生效。
内容的提问来源于stack exchange,提问作者Kid_Learning_C
相关产品推荐
相关产品推荐

