Web内容安全策略浅析

简介: Web安全问题一直是前端领域一个绕不开的话题,但很多前端人员对Web的相关安全策略都只停留在面试过程中,本文主要是对上线过程中遇到的安全问题踩坑进行了一个总结,旨在对Web安全相关问题能有一个更为立体切身的体会,也希望能给大家提供一些踩坑时候的参考。

前端 | Web内容安全策略浅析.png

前言

Web安全问题一直是前端领域一个绕不开的话题,但很多前端人员对Web的相关安全策略都只停留在面试过程中,本文主要是对上线过程中遇到的安全问题踩坑进行了一个总结,旨在对Web安全相关问题能有一个更为立体切身的体会,也希望能给大家提供一些踩坑时候的参考。

背景

XSS vs CSRF

XSS

XSS是Cross-site scripting的缩写,为了和Cascading Style Sheets进行区分,因而将其简写为XSS:

Cross-site scripting (XSS) is a security exploit which allows an attacker to inject into a website malicious client-side code. This code is executed by the victims and lets the attackers bypass access controls and impersonate users.

从MDN给出的定义可以看出,XSS是通过inject(注入)有害代码来实现攻击方案的,也就是说XSS的攻击方案是注入,这里一般是通过注入脚本,由于浏览器中dom、bom的为js提供了接口,因而js可以操作html和css,其也可以插入html片段等。

CSRF

很多人分不清CSRF,其实他们还是有很大区别的,只不过通常会使用XSS拿到一些权限后才进行CSRF。CSRF是Cross-Site Request Forgery的简写:

CSRF (Cross-Site Request Forgery) is an attack that impersonates a trusted user and sends a website unwanted commands.

从MDN定义可以看出来,CSRF是通过伪造来进行攻击,也就是其本质目的需要用一切手段来伪装自己获取本不属于它的权限资源

区别

名称 目的 备注
XSS 篡改内容 不关心权限
CSRF 获取资源 只关心权限

XSS分类

名称 存放 侵入方式 场景
存储型(持久型) 后端数据库 HTML 带有用户保存数据的网站,如:论坛发帖、商品评论、用户私信
反射型(非持久型) URL HTML 网站搜索、跳转
DOM 型 后端数据库/前端存储/URL js 前端js执行,如写了eval等

XSS防御方案

XSS预防主要分为两大部分:

  1. 防止攻击者提交恶意代码
  2. 浏览器执行恶意代码

从这两大部分可以整理出不同的防范思路:

方法 步骤 类型
利用模板引擎 1 存储型、反射型
限制及转义输入 1 存储型、反射型
限制js执行方法 2 DOM型
Content Security Policy 1、2 存储型、反射型、DOM型

从上述列表可以看出,Content Security Policy对XSS的防范是比较好的,那么接下来就到了本文的重点内容,在下一part会重点介绍CSP相关的内容

CSP

内容安全策略(Content Security Policy)是现代浏览器安全机制中的一项重要内容,从图上可以看出除了老IE仅支持了csp的sandbox外,其他主流浏览器都已支持了csp,并且w3c也对整体做了统一的要求,具体可以参看w3c的官方文档Content Security Policy Level 2

简介

Content-Security-Policy 是现代浏览器用来增强document安全性的一个响应头字段,其用来限制诸如js、css以及其他浏览器所需资源的加载。也就是说,csp的本质是一个限制资源加载的策略,其通过解析csp的指令及值进行具体资源的限制,具体的实现可以看最后一part源码中chromium的相关实现。另外除了在header添加csp外,在html的dom中也可以通过meta标签来进行限制,只是当二者所限制的资源指令及值都一样的时候,header中的优先级较高。

指令

指令 版本 注释
default-src 1 大部分资源未定义时会读取这个配置,少数不会,比如:frame-ancestors,优先级低
script-src 1 定义有效的js资源
style-src 1 定义有效的css资源
img-src 1 定义有效的图片资源
connect-src 1 加载XMLHttpRequest、WebSocket、fetch、<a ping>
以及 EventSource资源,如果不被允许则返回400状态码
font-src 1 定义有效的字体资源,通过@font-face加载
object-src 1 定义有效的插件资源,比如:<object>

<embed>
或者<applet> |
| media-src | 1 | 定义有效的音频及视频资源,比如:<audio>
<video>
等元素 |
| frame-src | 1 | 定义有效的frame加载资源,在csp2中,frame-src被废弃,使用child-src;在csp3中,又被启用,与child-src同时存在,如果child-src不存在,frame-src也会起作用 |
| sandbox | 1 | 允许iframe的sandbox属性,sandbox会采用同源策略,阻止弹窗、插件及脚本执行,可以通过不设置sandbox属性,而是通过allow-scripts、allow-popups、allow-modals、allow-orientation-lock、allow-pointer-lock、allow-presentation、allow-popups-to-escape-sandbox、allow-top-navigation来透给sandbox字段限制 |
| report-uri | 1 | 向这个uri发送失败报告,也可以使用Content-Security-Policy-Report-Only来作为http header进行发送但不阻塞网络资源的解析,在csp3中report-uri被废弃,改用report-to指令 |
| child-src | 2 | 定义web workers以及包含嵌套上下文的资源加载,比如<frame>
以及<iframe> |
| form-action | 2 | 定义有效的html标签<form>
的action资源加载 |
| frame-ancestors | 2 | 定义有效内嵌标签诸如<frame>
<iframe>
<object>
<embd>
<applet>
资源加载,当值为'none'时,大致可以和X-Frame-Options: DENY相当 |
| plugin-types | 2 | 定义通过<object>
<embd>
的MIME资源类型加载,对于<applet>
则必须明确MIME为application/x-java-applet |
| base-uri | 2 | 定义通过<base>
的html标签中的src属性引用的url资源加载 |
| report-to | 3 | 定义Report-To的http响应头字段 |
| worker-src | 3 | 限制通过Worker、SharedWorker以及ServiceWorker的url资源加载 |
| manifest-src | 3 | 限制manifests的url资源加载 |
| prefetch-src | 3 | 定义预渲染及预加载请求的资源加载,比如通过<link>
标签的rel="prefetch"或rel="prerender"属性 |
| navigate-to | 3 | 限制document的任何方式跳转url,比如通过link跳转,或者window.location被执行,如果form-action被设置,则本规则会被替换,即对于form而言,form-action优先级更高 |

版本 描述 样例
* 1 未知,允许除了data、blob、filesystem、schemes之外的任何url资源加载 img-src *
'none' 1 不允许加载任何的资源 object-src 'none'
'self' 1 只允许同源资源加载 script-src 'self'
'data:' 1 允许data格式的资源加载,比如:Base64图片编码 img-src 'self' data:
xx.xxx.com 1 允许明确域名的资源加载 img-src domain.example.com
*.xxx.com 1 允许加载任何例如:example.com子域下的资源 img-src *.example.com
https://xxx.com 1 仅允许https协议下域名的资源加载 img-src https://cdn.com
https: 1 允许加载https下任何域名的资源 img-src https:
'unsafe-inline' 1 允许使用内联元素加载资源,诸如:级联样式、句柄方法、script内容标签以及javascript:URIs等 script-src 'unsafe-inline'
'unsafe-eval' 1 允许不安全的动态js代码执行 script-src 'unsafe-eval'
'sha256-' 2 允许内联script及css匹配hash值后的执行 script-src 'sha256-xyz...'
'nonce-' 2 允许使用包含nonce属性的内联script及css执行,nonce应该是一个安全的任意值,并且不能被重复使用 script-src 'nonce-r@nd0m'
'strict-dynamic' 3 允许加载非解析型脚本资源,比如:document.createElement('script') script-src 'strict-dynamic'
'unsafe-hashes' 3 允许使用事件句柄的方式进行资源加载,但是不允许使用内联脚本及javascript:执行的方式 script-src 'unsafe-hashes' 'sha256-abc...'

案例

通过上一part的介绍,我们大致了解了CSP相关一些用法,下面具体看一下在前端业务中的具体实践

nginx

server {
    listen 9080;
    server_name localhost;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; frame-ancestors 'self'; object-src 'none'";
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
}

我们先来看一下nginx中的所有都严格控制的情况,我们再console中的v8实例下,使用创建一个标签来测试一下

这时我们发现,浏览器中报了

Refused to apply xxx because it violates the following Content Security Policy directive...

的错误;同时,我们打开任意一个network中的请求连接,我们发现,在响应头中,出现了

如上图所示的Content-Security-Policy: xxx的字段

接着,我们根据返回的错误,进行适度的csp限制放开

server {
    listen 9080;
    server_name localhost;
    add_header Content-Security-Policy "default-src *; script-src * 'unsafe-inline' 'unsafe-eval'; img-src * data:;";
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
}

修改过后,发现之前的报错消失了,测试成功

html

最后,我们来看一下html中meta标签的一个使用情况

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Security-Policy" content="default-src *; script-src 'unsafe-inline' 'unsafe-eval'; img-src *;">
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
    <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="0" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without
        JavaScript enabled. Please enable it to continue.</strong
      >
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

在不使用nginx配置csp的情况下,在html中配置meta也是可以起到同样的作用,但是当二者同时存在且字段一致时,会优先解析header中的响应

源码

这一part我们通过chromium源码来看一下chrome中是如何实现CSP的

content_security_policy_parsers

bool IsCSPDirectiveNameCharacter(UChar c) {
    return IsASCIIAlpanumeric(c) || c == '-';
}

bool IsCSPDirectiveValueCharacter(UChar c) {
    return IsASCIISpace(c) || (IsASCIIPrintable(c) && c != ',' && c != ';');
}

在network中通过解析key-value值对Content-Security-Policy中的指令及值进行获取

resource_fetcher

bool ResourceFetcher::ResourceNeedsLoad(Resource* resource, const FetchParameters& params, RevalidationPolicy policy) {
    if(archive_) 
        return false;
    
    if(resource->GetType() == ResourceType::kFont && !params.IsLinkPreload())
        return false;
    
    if(resource->GetType() == ResourceType::kImage && (ShouldDeferImageLoad(resource->Url()) || params.GetImageRequestBehavior() == FetchParameters::kDeferImageLoad)) {
        return false;
    }

    return policy != RevalidationPolicy::kUse || resource->StillNeedsLoad();
}

对是否进行资源加载进行判断

http_equiv

void HttpEquiv::ProcessHttpEquivContentSecurityPolicy(LocalDOMWindow* window, const AtomicString& equiv, const AtomicString& content) {
    if(!window || !window->GetFrame())
        return;
    if(window->GetFrame()->GetSettings()->GetBypassCSP())
        return;
    if(EqualIgnoringASCIICase(equiv, "content-security-policy")) {
        Vector<network::mojom::blink::ContentSecurityPolicyPtr> parsed = ParseContentSecurityPolicies(content, network::mojom::blink::ContentSecurityPolicyType::kEnforce, network::mojom::blink::ContentSecurityPolicySource::kMeta, *(window->GetSecurityOrigin()));
        window->GetContentSecurityPolicy()->AddPolicies(mojo::Clone(parsed));
        window->GetPolicyContainer()->AddContentSecurityPolicies(std::move(parsed));
    } else if (EqualIgnoringASCIICase(equiv, "content-security-policy-report-only")) {
        window->GetContentSecurityPolicy()->ReportReportOnlyInMeta(content);
    } else {
        NOTREACHED();
    }
}

我们看到在Document中执行相关csp的一些逻辑

worker_content_settings_client

bool WorkerContentSettingsClient::AllowScriptFromSource(bool enabled_per_settings, const blink::WebURL& script_url) {
    bool allow = enabled_per_settings;
    if(allow && content_setting_rules_) {
        GURL top_frame_origin_url = top_frame_origin_.GetURL();
        for(const auto& rule: content_setting_rules_->script_rules) {
            if(rule.primary_pattern.Matches(top_frame_origin_url) && rule.secondary_pattern.Matches(script_url)) {
                allow = rule.GetContentSetting() != CONTENT_SETTING_BLOCK;
                break;
            }
        }
    }

    if(!allow) {
        EnsureContentSettingsManager();
        content_settings_manager_->OnContentBlocked(render_frame_id_, ContentSettingsType::JAVASCRIPT);
        return false;
    }

    return true;
}

最后,我们可以简单看一下是否允许加载content的一些逻辑,以script加载为例

总结

不论是否是it行业,只要是工程师,都需要对工程项目中的安全问题进行相关的重视,对于前端工程来说,常见的XSS、CSRF等相关知识也需要我们在实际工程项目中进行原理探索,以及对常见的解决方案能够做到了熟于心,这样在架构设计及工程实践过程中才能做到应用工程的稳定可持续,最后附上一张chrome中的xss信息的ER图,从中体会一下chrome对于安全问题的架构设计及信息链路把控的优秀与严谨

参考

相关文章
|
2月前
|
缓存 数据库 索引
如何优化Python Web应用的性能,包括静态资源加载、缓存策略等?
```markdown 提升Python Web应用性能的关键点:压缩合并静态资源,使用CDN,设置缓存头;应用和HTTP缓存,ETag配合If-None-Match;优化数据库索引和查询,利用数据库缓存;性能分析优化代码,避免冗余计算,使用异步处理;选择合适Web服务器并调整参数;部署负载均衡器进行横向扩展。每一步都影响整体性能,需按需调整。 ```
21 4
|
9天前
|
前端开发 JavaScript
深入理解与实践:Web异步请求中的返回值处理策略
深入理解与实践:Web异步请求中的返回值处理策略
13 0
|
10天前
|
缓存 前端开发 JavaScript
探索现代Web应用的性能优化策略移动应用开发的未来之路:跨平台与原生之争
【4月更文挑战第30天】随着互联网技术的迅猛发展,Web应用已成为信息交流和商业活动的重要平台。用户对Web应用的响应速度和稳定性有着极高的期望,这促使开发者不断寻求提升应用性能的有效途径。本文将深入探讨针对现代Web应用进行性能优化的关键策略,包括前端优化、后端优化以及数据库层面的调优技巧,旨在为开发者提供一套全面的优化工具箱,帮助他们构建更快速、更高效的Web应用。
|
14天前
|
存储 缓存 JSON
【Web开发】会话管理与无 Cookie 环境下的实现策略
【Web开发】会话管理与无 Cookie 环境下的实现策略
|
17天前
|
缓存 前端开发 数据库
探索现代Web应用中的性能优化策略
【4月更文挑战第24天】 在数字化时代,Web应用的性能直接影响用户体验和业务成效。本文深入探讨了提升Web应用性能的多个维度,包括前端资源优化、服务端响应速度改进以及数据库查询效率增强。我们将分析现代Web技术栈中常用的性能优化技巧,并通过实例说明如何有效实施这些策略以提升最终用户满意度。
|
25天前
|
缓存 负载均衡 数据库
优化后端性能:提升Web应用响应速度的关键策略
在当今数字化时代,Web应用的性能对于用户体验至关重要。本文探讨了如何通过优化后端架构和技术手段,提升Web应用的响应速度。从数据库优化、缓存机制到异步处理等多个方面进行了深入分析,并提出了一系列实用的优化策略,以帮助开发者更好地应对日益增长的用户访问量和复杂的业务需求。
18 1
|
2月前
|
监控 前端开发 JavaScript
构建高性能Web应用:前端性能优化的关键策略与实践
本文将深入探讨前端性能优化的关键策略与实践,从资源加载、渲染优化、代码压缩等多个方面提供实用的优化建议。通过对前端性能优化的深入剖析,帮助开发者全面提升Web应用的用户体验和性能表现。
|
2月前
|
缓存 前端开发 UED
构建高性能Web应用:前端资源优化与加载策略探讨
本文将深入剖析前端资源优化和加载策略,包括减少HTTP请求、使用CDN加速、异步加载、资源压缩等技术手段。通过合理的资源管理和加载策略,可以显著提升Web应用的性能和用户体验。
|
3月前
|
Java 测试技术 数据库连接
基于Java Web技术的跨平台应用开发策略探讨
基于Java Web技术的跨平台应用开发策略探讨
|
3月前
|
缓存 前端开发 JavaScript
探索Web前端性能优化策略
在Web应用开发中,前端性能优化是至关重要的一环。本文将介绍几种有效的Web前端性能优化策略,包括代码压缩与合并、资源缓存、异步加载和图片优化等方法,以帮助开发者提升网页加载速度和用户体验。
37 1

热门文章

最新文章