托管在Azure App Service的Angular应用如何添加Nonce支持实现合规CSP配置
纯Angular 12在Azure App Service实现带Nonce的严格CSP方案
方案一:基于IIS原生URL Rewrite模块实现(无需额外后端,适合Windows宿主)
Azure App Service Windows宿主默认预装IIS URL Rewrite模块,无需引入.NET Core技术栈即可实现Nonce全流程配置:
- 第一步:修改Angular项目
index.html
删除原有CSP meta标签,所有需要授权的inline script(包括第三方脚本)统一添加nonce占位符,示例:<!-- 第三方inline script --> <script nonce="{{CSP_NONCE}}" src="第三方脚本地址"></script> <!-- 自有inline script如有需要也添加相同的nonce属性 --> - 第二步:在Angular项目根目录添加
web.config文件,部署时同步到Azure App Service
配置逻辑:每次请求生成随机Nonce,自动替换index.html中的占位符,同时注入标准CSP和仅报告模式CSP响应头,配置示例:<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <allowedServerVariables> <add name="CSP_NONCE" /> </allowedServerVariables> <rules> <!-- 生成随机Nonce存入服务器变量 --> <rule name="生成CSP Nonce" stopProcessing="false"> <match url=".*" /> <serverVariables> <set name="CSP_NONCE" value="{RANDOM}{RANDOM}{RANDOM}" /> </serverVariables> <action type="None" /> </rule> <!-- 保留Angular SPA默认路由重定向规则 --> <rule name="Angular SPA 路由" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="/" /> </rule> </rules> <outboundRules> <!-- 替换HTML页面中的Nonce占位符 --> <rule name="替换页面Nonce占位符" preCondition="是HTML页面"> <match filterByTags="None" pattern="{{CSP_NONCE}}" /> <action type="Rewrite" value="{CSP_NONCE}" /> </rule> <!-- 注入正式CSP响应头 --> <rule name="注入CSP响应头" preCondition="是HTML页面"> <match serverVariable="RESPONSE_Content-Security-Policy" pattern=".*" /> <action type="Rewrite" value="default-src 'self'; script-src 'nonce-{CSP_NONCE}' 'strict-dynamic' 替换为实际第三方域名; style-src 'self' 'unsafe-inline'; img-src 'self' data: 替换为实际第三方域名; font-src 'self'; connect-src 'self' 替换为实际接口域名; frame-src 'none'; object-src 'none'; base-uri 'self';" /> </rule> <!-- 注入仅报告模式CSP响应头 --> <rule name="注入仅报告模式CSP响应头" preCondition="是HTML页面"> <match serverVariable="RESPONSE_Content-Security-Policy-Report-Only" pattern=".*" /> <action type="Rewrite" value="default-src 'self'; script-src 'nonce-{CSP_NONCE}' 'strict-dynamic' 替换为实际第三方域名; report-uri 替换为实际上报地址;" /> </rule> </outboundRules> <preConditions> <preCondition name="是HTML页面"> <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" /> </preCondition> </preConditions> </rewrite> <staticContent> <mimeMap fileExtension=".json" mimeType="application/json" /> </staticContent> </system.webServer> </configuration>
方案二:基于轻量Node.js伺服实现(适合Linux宿主)
如果使用Azure App Service Linux宿主,可以加入极简Express伺服层实现Nonce能力,无需修改Angular业务代码:
- 第一步:项目根目录新增
server.js文件const express = require('express'); const crypto = require('crypto'); const fs = require('fs'); const path = require('path'); const app = express(); const PORT = process.env.PORT || 8080; // 静态资源直接返回 app.use(express.static(path.join(__dirname, 'dist/替换为实际Angular构建产物目录名'))); // 所有页面请求生成Nonce并注入 app.get('*', (req, res) => { const nonce = crypto.randomBytes(16).toString('hex'); const indexPath = path.join(__dirname, 'dist/替换为实际Angular构建产物目录名/index.html'); let indexContent = fs.readFileSync(indexPath, 'utf8'); // 替换页面中的Nonce占位符 indexContent = indexContent.replace(/{{CSP_NONCE}}/g, nonce); // 注入正式CSP头 res.setHeader( 'Content-Security-Policy', `default-src 'self'; script-src 'nonce-${nonce}' 'strict-dynamic' 替换为实际第三方域名; style-src 'self' 'unsafe-inline'; img-src 'self' data: 替换为实际第三方域名;` ); // 注入仅报告模式CSP头 res.setHeader( 'Content-Security-Policy-Report-Only', `default-src 'self'; script-src 'nonce-${nonce}' 'strict-dynamic' 替换为实际第三方域名; report-uri 替换为实际上报地址;` ); res.send(indexContent); }); app.listen(PORT, () => { console.log(`伺服启动,端口${PORT}`); }); - 第二步:修改
package.json的启动命令为node server.js,部署到Azure App Service Linux Node.js环境即可。
验证注意事项
- 部署后可通过浏览器开发者工具「网络」面板查看页面响应头,确认两个CSP头都已正确注入,且nonce值和页面script标签的nonce属性完全一致
- 不要使用固定Nonce,每次请求生成的Nonce唯一才具备XSS防护效果
- 严格模式下无需添加
unsafe-inline到script-src指令,符合安全要求
内容的提问来源于stack exchange,提问作者ickybyte
相关产品推荐
相关产品推荐

