首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >前端:浏览器Content Security Policy 安全策略介绍和用法

前端:浏览器Content Security Policy 安全策略介绍和用法

原创
作者头像
小明互联网技术分享社区
发布2025-09-29 08:39:54
发布2025-09-29 08:39:54
29400
代码可运行
举报
文章被收录于专栏:前端前端
运行总次数:0
代码可运行

Content Security Policy(CSP)是现代浏览器提供的一种重要的安全机制,它能有效防止XSS攻击、数据注入等安全威胁。今天我们就来详细了解一下CSP,并通过几个可以直接运行的示例来掌握它的用法。

什么是CSP?

Content Security Policy(内容安全策略,简称 CSP)是现代浏览器提供的一项重要安全机制,主要作用是帮助网站防范多种常见的客户端攻击,尤其是跨站脚本攻击(XSS)和数据注入攻击。

除了 Internet Explorer 之外,目前所有主流现代浏览器均已原生支持 CSP。所以在绝大多数场景下,开发者可以放心地在项目中启用并使用这一安全功能。

CSP 的核心机制是通过设置 HTTP 响应头 Content-Security-Policy(或通过 HTML 的 <meta> 标签),定义哪些外部资源可以被加载和执行。当浏览器解析页面时,会先检查该策略配置,再决定是否允许加载指定的资源。

例如,当页面需要加载 JavaScript 文件、图片、样式表、字体或 iframe 等资源时,浏览器会依据 CSP 策略进行校验:只有符合策略规定的资源才会被加载;不符合的则会被阻止,并在开发者控制台中记录相关警告或错误信息。

为什么需要CSP?

想象一下,如果你的网站允许用户输入评论,而恶意用户输入了这样的脚本:

代码语言:javascript
代码运行次数:0
运行
复制
<script>alert('你的数据被我偷走了!')</script>

传统的防御手段虽然有效,但CSP提供了更深层的保护——它通过白名单机制,告诉浏览器只加载和执行来自特定来源的资源。

CSP两种设置CSP的方式

通过HTTP响应头设置(推荐)

代码语言:javascript
代码运行次数:0
运行
复制
Content-Security-Policy: default-src 'self'

通过Meta标签设置

代码语言:javascript
代码运行次数:0
运行
复制
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">

常用指令说明

default-src:默认策略

script-src:控制JavaScript

style-src:控制CSS

img-src:控制图片

connect-src:控制Ajax请求

实战示例

下面我们通过两个个可以直接运行的示例来理解CSP的工作原理。

示例1:没有CSP保护的基础HTML页面

代码语言:javascript
代码运行次数:0
运行
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>示例1:没有CSP保护的页面</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        .container { border: 1px solid #ddd; padding: 20px; margin: 20px 0; border-radius: 5px; }
        button { padding: 8px 15px; margin: 5px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background: #45a049; }
        input { padding: 8px; width: 300px; margin-right: 10px; }
        .warning { background: #ffebee; color: #c62828; padding: 10px; border-radius: 4px; }
        .safe { background: #e8f5e9; color: #2e7d32; padding: 10px; border-radius: 4px; }
    </style>
</head>
<body>
    <h1>示例1:没有CSP保护的页面</h1>
    
    <div class="container">
        <h2>XSS攻击演示</h2>
        <p>这个页面没有CSP保护,尝试输入恶意脚本:</p>
        <input type="text" id="userInput" placeholder="尝试输入: &lt;script&gt;alert('XSS')&lt;/script&gt;">
        <button id="displayButton">显示输入内容</button>
        <div id="output" style="margin-top: 15px; min-height: 50px; padding: 10px; border: 1px dashed #ccc;"></div>
        
        <div class="warning">
            <p><strong>警告:</strong>这个页面没有CSP保护,恶意脚本可以执行!</p>
        </div>
    </div>
    
    <div class="container">
        <h2>外部资源加载</h2>
        <button id="loadScriptButton">加载外部脚本</button>
        <button id="loadImageButton">加载外部图片</button>
        <div id="external-results" style="margin-top: 15px;"></div>
    </div>

    <script>
        // 显示用户输入(不安全的方式)
        document.getElementById('displayButton').addEventListener('click', function() {
            const userInput = document.getElementById('userInput').value;
            // 危险:直接插入HTML,可能导致XSS攻击
            document.getElementById('output').innerHTML = 
                "你输入的是: " + userInput;
        });
        
        // 加载外部脚本
        document.getElementById('loadScriptButton').addEventListener('click', function() {
            const resultDiv = document.getElementById('external-results');
            try {
                // 动态创建脚本标签
                const script = document.createElement('script');
                script.src = 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js';
                script.onload = function() {
                    resultDiv.innerHTML = '<p style="color:green">外部脚本加载成功!</p>';
                };
                script.onerror = function() {
                    resultDiv.innerHTML = '<p style="color:red">外部脚本加载失败</p>';
                };
                document.head.appendChild(script);
            } catch(e) {
                resultDiv.innerHTML = '<p style="color:red">错误: ' + e.message + '</p>';
            }
        });
        
        // 加载外部图片
        document.getElementById('loadImageButton').addEventListener('click', function() {
            const resultDiv = document.getElementById('external-results');
            try {
                const img = document.createElement('img');
                img.src = 'https://via.placeholder.com/150';
                img.onload = function() {
                    resultDiv.innerHTML = '<p style="color:green">外部图片加载成功!</p><br>';
                    resultDiv.appendChild(img);
                };
                img.onerror = function() {
                    resultDiv.innerHTML = '<p style="color:red">外部图片加载失败</p>';
                };
            } catch(e) {
                resultDiv.innerHTML = '<p style="color:red">错误: ' + e.message + '</p>';
            }
        });
        
        // 模拟恶意脚本(这个脚本会执行,因为没有CSP)
        setTimeout(() => {
            console.log("恶意脚本已执行 - 因为没有CSP保护");
            // 尝试执行alert
            try {
                eval('console.log("eval函数可以执行")');
            } catch(e) {
                console.log("eval错误:", e);
            }
        }, 2000);
    </script>
</body>
</html>

具体效果如下图:

加载外部图片失败

示例2:启用严格CSP保护的页面

代码语言:javascript
代码运行次数:0
运行
复制
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>示例2:启用CSP保护的页面</title>
    <!-- 启用CSP保护 - 使用nonce解决内联脚本问题 -->
    <meta http-equiv="Content-Security-Policy" 
          content="default-src 'self'; 
                   script-src 'self' 'nonce-random123'; 
                   style-src 'self' 'unsafe-inline';
                   img-src 'self' data: https:;
                   object-src 'none';
                   base-uri 'self'">
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        .container { border: 1px solid #ddd; padding: 20px; margin: 20px 0; border-radius: 5px; }
        button { padding: 8px 15px; margin: 5px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background: #45a049; }
        input { padding: 8px; width: 300px; margin-right: 10px; }
        .warning { background: #ffebee; color: #c62828; padding: 10px; border-radius: 4px; }
        .safe { background: #e8f5e9; color: #2e7d32; padding: 10px; border-radius: 4px; }
    </style>
</head>
<body>
    <h1>示例2:启用CSP保护的页面</h1>
    
    <div class="container">
        <h2>XSS攻击防护演示</h2>
        <p>这个页面有CSP保护,尝试输入恶意脚本:</p>
        <input type="text" id="userInput" placeholder="尝试输入: &lt;script&gt;alert('XSS')&lt;/script&gt;">
        <button id="displayButton">显示输入内容</button>
        <div id="output" style="margin-top: 15px; min-height: 50px; padding: 10px; border: 1px dashed #ccc;"></div>
        
        <div class="safe">
            <p><strong>安全:</strong>这个页面有CSP保护,恶意脚本会被阻止!</p>
        </div>
    </div>
    
    <div class="container">
        <h2>外部资源加载限制</h2>
        <button id="loadScriptButton">加载外部脚本</button>
        <button id="loadImageButton">加载外部图片</button>
        <button id="tryEvalButton">尝试使用eval()</button>
        <div id="external-results" style="margin-top: 15px;"></div>
    </div>

    <!-- 使用nonce允许特定脚本执行 -->
    <script nonce="random123">
        // 显示用户输入(安全的方式)
        document.getElementById('displayButton').addEventListener('click', function() {
            const userInput = document.getElementById('userInput').value;
            // 安全:使用textContent而不是innerHTML
            document.getElementById('output').textContent = 
                "你输入的是: " + userInput;
        });
        
        // 尝试加载外部脚本(会被CSP阻止)
        document.getElementById('loadScriptButton').addEventListener('click', function() {
            const resultDiv = document.getElementById('external-results');
            try {
                // 动态创建脚本标签
                const script = document.createElement('script');
                script.src = 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js';
                script.onload = function() {
                    resultDiv.innerHTML = '<p style="color:green">外部脚本加载成功!</p>';
                };
                script.onerror = function() {
                    resultDiv.innerHTML = '<p style="color:red">外部脚本被CSP阻止</p>';
                };
                document.head.appendChild(script);
            } catch(e) {
                resultDiv.innerHTML = '<p style="color:red">错误: ' + e.message + '</p>';
            }
        });
        
        // 尝试加载外部图片(会被CSP允许,因为我们设置了img-src https:)
        document.getElementById('loadImageButton').addEventListener('click', function() {
            const resultDiv = document.getElementById('external-results');
            try {
                const img = document.createElement('img');
                img.src = 'https://via.placeholder.com/150';
                img.onload = function() {
                    resultDiv.innerHTML = '<p style="color:green">外部图片加载成功!</p><br>';
                    resultDiv.appendChild(img);
                };
                img.onerror = function() {
                    resultDiv.innerHTML = '<p style="color:red">外部图片加载失败</p>';
                };
            } catch(e) {
                resultDiv.innerHTML = '<p style="color:red">错误: ' + e.message + '</p>';
            }
        });
        
        // 尝试使用eval(会被CSP阻止)
        document.getElementById('tryEvalButton').addEventListener('click', function() {
            const resultDiv = document.getElementById('external-results');
            try {
                eval('resultDiv.innerHTML = "<p style=\"color:green\">eval执行成功!</p>"');
            } catch(e) {
                resultDiv.innerHTML = '<p style="color:red">CSP阻止了eval执行: ' + e.message + '</p>';
            }
        });
        
        // 尝试执行内联事件处理程序(会被CSP阻止)
        // 注意:这个脚本不会执行,因为CSP阻止了内联事件处理程序
        console.log("这个脚本可以执行,因为它有nonce属性");
    </script>
</body>
</html>
  • 页面启用了CSP保护
  • 使用了nonce属性允许特定脚本执行
  • 阻止了外部脚本加载(只允许同源脚本)
  • 允许加载HTTPS图片
  • 阻止了eval()等危险函数
  • 使用了安全的textContent而不是innerHTML
  • 展示了CSP如何防止XSS攻击

具体如下图:

加载外部脚本

加载外部图片

启动eval

CSP配置详解

常用指令值

'self':只允许同源资源

'none':禁止所有资源

'unsafe-inline':允许内联脚本/样式(不推荐)

'unsafe-eval':允许eval等动态代码执行(不推荐)

https::允许所有HTTPS资源

总结

CSP是一项强大的安全技术,实际应用过程中应该记住以下原则:

渐进实施:从报告模式开始,逐步转向强制执行

最小权限原则:只授予必要的权限

持续监控:定期检查违规报告,优化策略

平衡安全与功能:在安全性和开发便利性之间找到平衡点

大家如果使用过程中有问题的话欢迎评论区沟通交流!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是CSP?
  • CSP两种设置CSP的方式
    • 常用指令说明
  • 实战示例
    • 示例1:没有CSP保护的基础HTML页面
    • 示例2:启用严格CSP保护的页面
  • CSP配置详解
    • 常用指令值
    • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档