同源和跨域
同源
什么是同源?请求的“协议+ip(域名)+端口”被称之为“源”。
如果当前页面的url和在当前页面中要访问的url具有相同的源,那么我们称这两个请求“同源”,即它们来自或者去往同一个服务器。
跨域
什么是跨域?如果当前页面的url和请求的url非同源,那么我们就可以称之为跨域,也称之为跨源!或者说,当请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域!非同源和跨域,可以说是一种事务的不同说法!
跨域例子
假设某个页面的URL为http://mall.pplus.com/goods/detail.html,下面是常见的跨域(非同源)请求的类型:
请求的URL | 是否跨域(非同源) | 原因 |
---|---|---|
http://mall.pplus.com/other.html | 否 | 只有访问的资源路径不同 |
http://mall.pplus.com/dir/inner/another.html | 否 | 只有访问的资源路径不同 |
https://mall.pplus.com/dir/page.html | 是 | 协议不同 |
http://mall.pplus.com:81/dir/page.html | 是 | 端口不同 ( http:// 默认端口是80) |
http://news.pplus.com/dir/page.html | 是 | 主机(域名)不同 |
同源策略
什么是同源策略?同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,它的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,很容易受到 XSS、 CSFR 等攻击。
同源策略对跨域(非同源)请求进行了如下限制:
- 无法读取非同源网页的 Cookie、LocalStorage 和IndexedDB;当然如果两个网页一级域名相同,只是次级域名不同,那么Cookie可以通过设置相同的domainName来实现共享Cookie。
- 无法获取非同源网页的 DOM树和Js对象。
- 无法获取非同源地址返回的响应,例如通过
XMLHttpRequest
发送的AJAX请求,以及标签请求的非同源图片资源,获取想要加载的各种网络字体、样式资源。
一级域名:
一级域名就是顶级域名,是同一种域名的两种叫法。
例如pplus.com是顶级域名,也是一级域名。二级域名:
例如www.pplus.com、mall.pplus.com为二级域名。
同源策略的限制通常是浏览器自己来实现的,当同源策略触发时:
- 对于某些浏览器,可能已经正常发送了跨域请求,甚至服务器已经正常处理了请求并且返回了响应,只不过在处理结果时浏览器做了限制,导致无法获取服务器响应的结果!
- 对于另一些浏览器以及某些请求方式,它会首先发送一个OPTIONS 预检请求到服务器,以获知服务器是否允许该实际请求,当服务器响应允许此次跨域时,才会发送真正的请求并且正确的处理响应结果。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
解决跨域
同源策略虽然带来一定的安全性,但是这往往给一些正规的跨域需求带来不便,特别是前后端分离以及服务化的项目,通常不同的服务之间是不同源但,但是它们之间需要互相调用那个。为此,我们可以采取一些方式来绕过同源策略!
解决的方案:
JSONP
:一种只需要前端处理的比较简单的方法,但是只能支持get请求,不支持其他类型的请求,因此用的不多。WebSocket
:一种基于HTTP升级而来的协议,它不实行同源策略,只要服务器支持,就可以通过它进行跨域通信。但是WebSocket协议的前后端代码开发和普通HTTP开发有很大区别,通常是被用于即时通信或者要求服务器实时推送数据的项目中,普通web项目为了解决跨域问题而使用WebSocket是得不偿失的!Nginx
:反向代理,也是一种非常有效的解决跨域问题的方法,用的也非常多!CORS
:CORS(Cross-origin resource sharing),通俗地译为跨域资源共享。CORS是一个W3C标准,是一种基于HTTP头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(源,包括域、协议和端口),这样浏览器可以访问加载这些资源。
CORS解决跨域
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的OPTIONS“预检”请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。
CORS可以看作是跨源AJAX请求的一种根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。
CORS标准
W3C为CORS标准新增了一组 HTTP 首部字段,以允许服务器在响应头中通过这些字段声明和控制哪些源站通过浏览器有权限访问哪些资源。
另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
header
Access-Control-Allow-Origin
:必须的字段,服务器允许的域,允许所有域设置为 *。- Access-Control-Allow-Methods: 服务器允许的请求方法,允许所有方法设置为*。
Access-Control-Allow-Headers
:如果请求包括Access-Control-Request-Headers
字段,则响应的Access-Control-Allow-Headers
字段是必需的。表明服务器允许添加的请求头字段,采用“,”分隔,不限于浏览器在预检请求中传递的字段。Access-Control-Allow-Credentials
:处理方式与简单请求时一致。Access-Control-Max-Age
: 该预检请求响应的有效时间,单位秒时。在有效时间内,浏览器无须为同一请求再次发起预检请求。浏览器自身同样维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。
Spring MVC 对CORS支持
Spring MVC的HandlerMapping
的实现为CORS提供了内置支持。在成功将request映射到handler处理器后,HandlerMaping
实现将检查给定request和handler的 CORS 配置信息,随后,将会直接处理预检请求,同时拦截、验证简单和实际 CORS 请求,并设置所需的(配置的)CORS响应头信息。
为了启用跨源请求,需要具有一些显式声明的 CORS 配置。如果未找到匹配的 CORS 配置,则预检请求将直接被拒绝。
每个HandlerMapping
都可以使用基于URL模式的 CorsConfiguration
映射单独配置。在大多数情况下,web程序使用 MVC JavaConfig或 XML文件来声明这些映射规则,这会导致单个全局映射传递将给所有 HandlerMaping
实例。
可以将HandlerMapping
级别的全局CORS配置与更细粒度的handler级别的CORS配置相结合使用。例如,基于注解的Controller控制器可以使用类或方法级的@CrossOrigin
注解来为某些或者某个接口单独定义CORS配置。
@CrossOrigin注解配置
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
@AliasFor("origins")
String[] value() default {};
@AliasFor("value")
String[] origins() default {};
/**实际请求中允许的请求标头列表,可能使用“*”来允许所有标头。*/
String[] allowedHeaders() default {};
/**用户代理将允许客户端在实际响应中访问的响应标头列表,而不是“简单”标头,即Cache Control、Content Language、Content Type、Expires、Last Modified或Pragma, 暴露的标头列在实际CORS请求的访问控制暴露标头响应标头中。 默认情况下,不会将任何标头列为已公开标头。*/
String[] exposedHeaders() default {};
/**支持的HTTP请求方法的列表。 默认情况下,支持的方法与控制器方法映射到的方法相同。*/
RequestMethod[] methods() default {};
/**浏览器是否应向带注释的端点发送凭据,如Cookie以及跨域请求。配置的值是在飞行前请求的访问控制允许凭据响应标头上设置的。*/
String allowCredentials() default "";
/**缓存持续时间的最大期限(以秒为单位)。*/
long maxAge() default -1;
}
@CrossOrigin
使用:
@RestController
public class AccessControlController {
@CrossOrigin
@GetMapping("/accessControl/{id}")
public User accessControl(@PathVariable Long id, String name) {
return new User(id, name);
}
}
全局配置
Controller方法级别配置外,我们更多的可能还希望定义一些全局CORS配置。我们可以在任何处HandlerMapping
上单独设置基于URL的CorsConfiguration映射。但是,大多数应用程序使用 MVC JavaConfig或 MVC XML配置来做到这一点。
Java配置:
采用JavaConfig的配置方式非常简单,我们只需要实现WebMvcConfigurer
接口并且,实现addCorsMappings
回调方法即可,在方法中即可通过参数配置CORS信息!
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
/**
* 配置跨源请求处理。
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
//为指定的路径(模式)启用跨源请求处理。
//默认情况下,此映射的CORS配置采用CorsConfiguration.applyPermitDefaultValue()的配置
//即允许所有的origins(源),所有的headers(请求头字段),所有的GET、HEAD和POST方法。
registry.addMapping("/api/**")
//设置允许访问该资源的外域源,这里是所有
.allowedOrigins("*")
//设置实际请求所允许使用的 HTTP 方法。这里是所有
.allowedMethods("*")
//设置实际请求所允许携带的自定义首部字段。这里是所有
.allowedHeaders("*")
//设置是否允许请求中携带身份认证信息,比如Cookie。这里是允许
.allowCredentials(true);
}
}
XML配置:
若要在Spring的XML配置文件中启用CORS,可以使用mvc:cors标签:
<mvc:cors>
<!--配置第一个CORS映射处理规则-->
<mvc:mapping path="/api/**"
allow-credentials="true"
allowed-headers="*"
allowed-methods="*"
allowed-origins="*"
max-age="1800"/>
<!--配置第二个CORS映射处理规则-->
<mvc:mapping path="/service/**"/>
</mvc:cors>
XSS
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。
跨站脚本攻击(XSS),是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。
CSFR
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。
CSRF原理
CSRF 攻击流程:
其实这个流程很简单:
- 假设用户打开了招商银行网上银行网站,并且登录。
- 登录成功后,网上银行会返回 Cookie 给前端,浏览器将 Cookie 保存下来。
- 用户在没有登出网上银行的情况下,在浏览器里边打开了一个新的选项卡,然后又去访问了一个危险网站。
- 这个危险网站上有一个超链接,超链接的地址指向了招商银行网上银行。
- 用户点击了这个超链接,由于这个超链接会自动携带上浏览器中保存的 Cookie,所以用户不知不觉中就访问了网上银行,进而可能给自己造成了损失。