HTTP CSP

本文介绍了 Content Security Policy (CSP),包括其体现的限制资源获取、报告资源获取越权功能,以及限制方式。通过实验展示了如何限制 inline js 代码执行、外部链接加载的 js 文件域名、form 表单提交方向等,还说明了 CSP 出现异常时向服务器汇报的方法。

Content Security Policy (CSP)

https://developer.mozilla.org/zh-CN/docs/Web/Security/CSP

它体现在:

 - 限制资源获取

 - 报告资源获取越权

限制方式

 - default-src 限制全局

 - 制定资源类型(资源类型如,connect-src, img-src, manifest-src, img-src, style-src, media-src, font-src, script-src, frame-src, ...)

下面我们来实验一下。

先是server.js 如下

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    const html = fs.readFileSync('test.html')
    response.writeHead(200, {
        'Content-Type': 'text/html'
    })
    response.end(html)
    
}).listen(8888)

console.log('serve listening on 8888')

然后test.html 如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MyHtml</title>
</head>
<body>
    <div>hello world</div>
    <div>don't speak</div>

    <script>
        console.log('inline js')
    </script>
</body>
</html>

test.html 中有inline js 代码,我们不希望执行inline js 代码。因为XSS 攻击就是执行的inline js 代码。那么我们可以这样做,如下。

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    const html = fs.readFileSync('test.html')
    response.writeHead(200, {
        'Content-Type': 'text/html',
        'Content-Security-Policy': 'default-src http: https:'
    })
    response.end(html)
    
}).listen(8888)

console.log('serve listening on 8888')

重启服务,刷新页面,就会发现不会执行inline js 并会在控制条打印出报错信息。

下面,我们将html 中加入一个src 它的内容请求自后台。如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MyHtml</title>
</head>
<body>
    <div>hello world</div>
    <div>don't speak</div>

    <script>
        console.log('inline js')
    </script>

    <script src="/test.js"></script>
</body>
</html>

然后去改一下后台代码,如下。

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    if (request.url === '/') {
        const html = fs.readFileSync('test.html')
        response.writeHead(200, {
            'Content-Type': 'text/html',
            'Content-Security-Policy': 'default-src http: https:'
        })
        response.end(html)
    } else {
        response.writeHead(200, {
            'Content-Type': 'application/javascript'
        })
        response.end('console.log("loaded script")')
    }
    
    
}).listen(8888)

console.log('serve listening on 8888')

我们重启服务,刷新页面就可以看到:

 

不仅如此,我们还可以限制,通过外部链接加载的js 文件,它可以通过哪些域名进行加载。比如限制,只能根据本域名下的js 进行加载,如下。

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    if (request.url === '/') {
        const html = fs.readFileSync('test.html')
        response.writeHead(200, {
            'Content-Type': 'text/html',
            'Content-Security-Policy': 'default-src \'self\''
            // cannot inline js
            // 'Content-Security-Policy': 'default-src http: https:'
        })
        response.end(html)
    } else {
        response.writeHead(200, {
            'Content-Type': 'application/javascript'
        })
        response.end('console.log("loaded script")')
    }
    
    
}).listen(8888)

console.log('serve listening on 8888')

然后,我们在test.html  中加入一个外链script (不同域),如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MyHtml</title>
</head>
<body>
    <div>hello world</div>
    <div>don't speak</div>

    <script>
        console.log('inline js')
    </script>

    <script src="/test.js"></script>

    <script src="https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>
</body>
</html>

重启,刷新后,发现:

当然,也可以设置有些域名的js 外链可访问,如下。

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    if (request.url === '/') {
        const html = fs.readFileSync('test.html')
        response.writeHead(200, {
            'Content-Type': 'text/html',
            'Content-Security-Policy': 'default-src \'self\' https://cdn.bootcss.com/'
            // cannot inline js
            // 'Content-Security-Policy': 'default-src http: https:'
        })
        response.end(html)
    } else {
        response.writeHead(200, {
            'Content-Type': 'application/javascript'
        })
        response.end('console.log("loaded script")')
    }
    
    
}).listen(8888)

console.log('serve listening on 8888')

我们还可以限制form 表单的提交方向。form 表单不接受default-src 的限制.设置如下。

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    if (request.url === '/') {
        const html = fs.readFileSync('test.html')
        response.writeHead(200, {
            'Content-Type': 'text/html',
            'Content-Security-Policy': 'default-src \'self\'; form-action \'self\''
            // 'Content-Security-Policy': 'default-src \'self\' https://cdn.bootcss.com/'
            // cannot inline js
            // 'Content-Security-Policy': 'default-src http: https:'
        })
        response.end(html)
    } else {
        response.writeHead(200, {
            'Content-Type': 'application/javascript'
        })
        response.end('console.log("loaded script")')
    }
    
    
}).listen(8888)

console.log('serve listening on 8888')

test.html 如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>MyHtml</title>
</head>
<body>
    <div>hello world</div>
    <div>don't speak</div>
    <form action="http://www.baidu.com">
        <input type="text" name="name" />
        <input type="submit" />
    </form>
    <script>
        console.log('inline js')
    </script>

    <script src="/test.js"></script>

    <script src="https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>
</body>
</html>

注意:default-src 是将所有的src 属性限制了,包括 img 的src 。如果只想限制js 的src ,可以将default-src 改为 script-src .

汇报

内容安全策略当出现了,我们不希望出现的情况的时候,我们可以申请它向服务器发送一个请求来进行汇报。如下。

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    if (request.url === '/') {
        const html = fs.readFileSync('test.html')
        response.writeHead(200, {
            'Content-Type': 'text/html',
            'Content-Security-Policy': 'default-src \'self\'; form-action \'self\'; report-uri /report'
            // 'Content-Security-Policy': 'default-src \'self\' https://cdn.bootcss.com/'
            // cannot inline js
            // 'Content-Security-Policy': 'default-src http: https:'
        })
        response.end(html)
    } else {
        response.writeHead(200, {
            'Content-Type': 'application/javascript'
        })
        response.end('console.log("loaded script")')
    }
    
    
}).listen(8888)

console.log('serve listening on 8888')

重启刷新,会发现,network 中会发送 report 请求。

我们同时,还可以不阻止src,只是发送report ,如下。

const http = require('http')
const fs = require('fs')
const zlib = require('zlib')

http.createServer(function (request, response) {
    console.log('request come', request.url)

    if (request.url === '/') {
        const html = fs.readFileSync('test.html')
        response.writeHead(200, {
            'Content-Type': 'text/html',
            'Content-Security-Policy-Report-Only': 'default-src \'self\'; form-action \'self\'; report-uri /report'
            // 'Content-Security-Policy': 'default-src \'self\'; form-action \'self\'; report-uri /report'
            // 'Content-Security-Policy': 'default-src \'self\' https://cdn.bootcss.com/'
            // cannot inline js
            // 'Content-Security-Policy': 'default-src http: https:'
        })
        response.end(html)
    } else {
        response.writeHead(200, {
            'Content-Type': 'application/javascript'
        })
        response.end('console.log("loaded script")')
    }
    
    
}).listen(8888)

console.log('serve listening on 8888')

 

08-26
### Content Security Policy 是什么? Content Security Policy(CSP)是一种增强网站安全性的机制,旨在检测并阻止网页加载非法资源,从而减轻跨站脚本攻击(XSS)和数据注入攻击的风险。通过定义哪些资源可以被加载和执行,CSP 可以有效防止恶意脚本的注入和运行,保护用户数据的安全性[^1]。 ### 如何配置 Content Security Policy? 配置 CSP 的核心方法是通过在 HTTP 响应头中添加 `Content-Security-Policy` 字段,以定义资源加载的规则。以下是一个典型的 CSP 配置示例: ```http Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' https://trusted-styles.com; ``` - `default-src 'self'`:表示默认情况下,所有资源只能从当前域名加载。 - `script-src 'self' https://trusted-cdn.com`:表示脚本可以从当前域名和指定的 CDN 加载。 - `style-src 'self' https://trusted-styles.com`:表示样式文件可以从当前域名和指定的样式服务器加载。 除了基本的资源加载规则,CSP 还支持更复杂的配置,例如限制内联脚本的执行、指定报告违规行为的端点等。以下是一个更复杂的配置示例: ```http Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com 'unsafe-inline'; report-uri /csp-violation-report-endpoint/; ``` 在这个示例中: - `'unsafe-inline'` 允许执行内联脚本(虽然不推荐,但在某些情况下可能需要)。 - `report-uri` 指定了一个端点,用于接收 CSP 违规行为的报告,便于进一步分析和调整策略。 ### 业务接入 CSP 的流程 1. **定义策略**:根据业务需求和安全要求,定义资源加载的策略,包括脚本、样式、图片等资源的来源限制。 2. **测试模式**:启用 CSP 的 `Content-Security-Policy-Report-Only` 模式,以便在不阻止资源加载的情况下收集违规报告,逐步调整策略。 3. **部署策略**:将最终的 CSP 策略部署到生产环境中,通过 `Content-Security-Policy` 响应头生效。 4. **监控与优化**:持续监控 CSP 违规报告,优化策略以确保安全性和功能性之间的平衡。 ### 相关问题 1. CSP 如何防止 XSS 攻击? 2. CSP 中的 `report-uri` 和 `report-to` 有什么区别? 3. 如何在开发环境中测试 CSP 策略? 4. CSP 支持哪些类型的资源限制? 5. CSP 与传统的 XSS 防护机制有何不同?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值