XSS攻击

目录

  • 什么是XSS脚本攻击
  • XSS攻击的本质
  • XSS 攻击分类
    • 存储型 XSS 的攻击步骤:
    • 反射型 XSS 的攻击步骤:
    • DOM 型 XSS 的攻击步骤:
  • 前端处理
  • 后端处理
  • 参考资料

随着互联网的高速发展

信息安全问题已经成为企业最为关注的焦点之一,而前端又是引发企业安全问题的高危据点。

在移动互联网时代,前端人员除了传统的 XSS、CSRF 等安全问题之外,又时常遭遇网络劫持、非法调用 Hybrid API 等新型安全问题。
当然,浏览器自身也在不断在进化和发展,不断引入 CSP、Same-Site Cookies 等新技术来增强安全性,但是仍存在很多潜在的威胁,这需要前端技术人员不断进行“查漏补缺”。
近几年,随着业务高速发展,前端随之面临很多安全挑战,因此积累了大量的实践经验。
我们梳理了常见的前端安全问题以及对应的解决方案,将会做成一个系列,希望可以帮助前端人员在日常开发中不断预防和修复安全漏洞

什么是XSS脚本攻击

  • Cross-Site Scripting(跨站脚本攻击)简称 XSS,为了和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS,是一种代码注入攻击。
  • 攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。
  • 利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。

XSS攻击的本质

  • 恶意代码未经过滤,与网站正常的代码混在一起;
  • 浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
  • 而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求

XSS 攻击分类

据攻击的来源,XSS 攻击可分为存储型、反射型和 DOM 型三种

类型存储区插入点
存储型后端数据库HTML
反射型URLHTML
DOM 型后端数据库/前端存储/URL前端 JavaScript

存储型 XSS 的攻击步骤:

① 攻击者将恶意代码提交到目标网站的数据库中。
② 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
③ 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
④ 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。

反射型 XSS 的攻击步骤:

① 攻击者构造出特殊的 URL,其中包含恶意代码。
② 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
③ 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
④ 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 与 存储型 XSS 的区别是:

存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。
POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。

DOM 型 XSS 的攻击步骤:

① 攻击者构造出特殊的 URL,其中包含恶意代码。
② 用户打开带有恶意代码的 URL。
③ 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
④ 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 与 前两种 XSS 的区别:

DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。

前端处理

后端处理

在微服务架构中,在网关处增加一个全局过滤器,该过滤器会将请求中提交的文本中与xss攻击相关的敏感字符进行删除

  • 工具类
import lombok.extern.slf4j.Slf4j;import java.util.regex.Pattern;/*** @author samson bruce*/
@Slf4j
public class XssCleanRuleUtils {private static final Pattern[] scriptPatterns = {Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)};/*** GET请求参数过滤** @param value* @return String*/public static String xssGetClean(String value) {//过滤xss字符集return xssClean(value);}/*** post请求参数过滤** @param value value* @return String*/public static String xssPostClean(String value) {//过滤xss字符集return xssClean(value);}private static String xssClean(String value) {if (value != null) {value = value.replaceAll("\0|\n|\r", "");for (Pattern pattern : scriptPatterns) {value = pattern.matcher(value).replaceAll("");}value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");}return value;}}
  • 全局过滤器
import io.netty.buffer.ByteBufAllocator;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Optional;/*** @author samson bruce、yuqi li* @since 2023-07-01*/
@Slf4j
@Component
public class XssFilter implements GlobalFilter, Ordered {@SneakyThrows(Exception.class)@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// grab configuration from Config objectlog.info("----自定义防XSS攻击网关全局过滤器生效----");ServerHttpRequest serverHttpRequest = exchange.getRequest();HttpMethod method = serverHttpRequest.getMethod();String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);URI uri = exchange.getRequest().getURI();boolean postFlag = (method == HttpMethod.POST || method == HttpMethod.PUT) &&(MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_VALUE.equals(contentType));//过滤get请求if (method == HttpMethod.GET) {String rawQuery = uri.getRawQuery();if (StringUtils.isBlank(rawQuery)) {return chain.filter(exchange);}log.info("原请求参数为:{}", rawQuery);// 执行XSS清理rawQuery = XssCleanRuleUtils.xssGetClean(rawQuery);log.info("修改后参数为:{}", rawQuery);try {//重新构造get requestURI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(rawQuery).build(true).toUri();ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();return chain.filter(exchange.mutate().request(request).build());} catch (Exception e) {log.error("get请求清理xss攻击异常", e);throw new IllegalStateException("Invalid URI query: \"" + rawQuery + "\"");}}//post请求时,如果是文件上传之类的请求,不修改请求消息体else if (postFlag) {return DataBufferUtils.join(serverHttpRequest.getBody()).flatMap(d -> Mono.just(Optional.of(d))).defaultIfEmpty(Optional.empty()).flatMap(optional -> {// 取出body中的参数String bodyString = "";if (optional.isPresent()) {byte[] oldBytes = new byte[optional.get().readableByteCount()];optional.get().read(oldBytes);bodyString = new String(oldBytes, StandardCharsets.UTF_8);}HttpHeaders httpHeaders = serverHttpRequest.getHeaders();// 执行XSS清理log.info("{} - [URL:{}] XSS处理前参数:{}", method, uri.getPath(), bodyString);bodyString = XssCleanRuleUtils.xssPostClean(bodyString);log.info("{} - [URL:{}] XSS处理后参数:{}", method, uri.getPath(), bodyString);ServerHttpRequest newRequest = serverHttpRequest.mutate().uri(uri).build();// 重新构造bodybyte[] newBytes = bodyString.getBytes(StandardCharsets.UTF_8);DataBuffer bodyDataBuffer = toDataBuffer(newBytes);Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);// 重新构造headerHttpHeaders headers = new HttpHeaders();headers.putAll(httpHeaders);// 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度int length = newBytes.length;headers.remove(HttpHeaders.CONTENT_LENGTH);headers.setContentLength(length);headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf8");// 重写ServerHttpRequestDecorator,修改了body和header,重写getBody和getHeaders方法newRequest = new ServerHttpRequestDecorator(newRequest) {@Overridepublic Flux<DataBuffer> getBody() {return bodyFlux;}@Overridepublic HttpHeaders getHeaders() {return headers;}};return chain.filter(exchange.mutate().request(newRequest).build());});} else {return chain.filter(exchange);}}/*** 自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行*/@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE;}/*** 字节数组转DataBuffer** @param bytes 字节数组* @return DataBuffer*/private DataBuffer toDataBuffer(byte[] bytes) {NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);buffer.write(bytes);return buffer;}}

参考资料

https://tech.meituan.com/2018/09/27/fe-security.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/63154.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【electron】Puppeteer 和 Electron 共用同一个Chrome 或 Chromium浏览器二进制文件

将 Puppeteer 的可执行路径设置为 Electron 的可执行路径来实现这一点 以下是一个示例代码&#xff0c;展示了如何在 Puppeteer 中使用 Electron 的浏览器二进制文件&#xff1a; const puppeteer require(puppeteer-core);(async () > {// 设置 Electron 的可执行路径co…

visual studio编写DLL,python调用

选择第一个c DLL&#xff0c; 然后项目源文件下右击新建项&#xff0c;这里名字随便取&#xff0c;在代码中输入一下内容&#xff1a; #include <iostream>#define EXPORT extern "C" __declspec(dllexport)EXPORT int sub(int a, int b) {return a - b; } 在…

SpringBoot项目配置文件数据库用户名密码加密

1、需求 在使用SpringBoot开发过程中&#xff0c;会将一些敏感信息配置到SpringBoot项目的配置文件中(不考虑使用配置中心的情况 )&#xff0c;例如数据库的用户名和密码、Redis的密码等。为了保证敏感信息的安全&#xff0c;我们需要将此类数据进行加密配置。 2、操作步骤 …

Vue 纯 css 编写鱼骨图

Vue 纯 css 编写鱼骨图 参考文章1忘记 参考文章1会点 php 的前端小渣渣 &#xff08;我是在此基础上进行二改的&#xff09; 二改组件 粘贴下来到手直接用。 <template><div class"fishbone scroll"><div class"content"><el-row typ…

网络编程套接字(3): 简单的TCP网络程序

文章目录 网络编程套接字(3)4. 简单的TCP网络程序4.1 服务端创建(1) 创建套接字(2) 绑定端口(3) 监听(4) 获取新连接(5) 处理读取与写入 4.2 客户端创建(1)连接服务器 4.3 代码编写(1) v1__简单发送消息(2) v2_多进程版本(3) v3_多线程版本(4) v4_线程池版本 网络编程套接字(3)…

leetcode原题: 生存人数

题目&#xff1a; 给定 N 个人的出生年份和死亡年份&#xff0c;第 i 个人的出生年份为 birth[i]&#xff0c;死亡年份为 death[i]&#xff0c;实现一个方法以计算生存人数最多的年份。 你可以假设所有人都出生于 1900 年至 2000 年&#xff08;含 1900 和 2000 &#xff09;…

【位运算】leetcode面试题:消失的两个数字

一.题目描述 消失的两个数字 二.思路分析 本题难度标签是困难&#xff0c;但实际上有了只出现一次的数字iii这道题的铺垫&#xff0c;本题的思路还是很容易想到的。 温馨提示&#xff1a;阅读本文前可以先查看我的【位运算】专栏的第一篇文章&#xff0c;其中包含位运算这类…

如何用selenium或pyppeteer来启动多个AdsPower窗口

前言 本文是该专栏的第57篇,后面会持续分享python爬虫干货知识,记得关注。 关于selenium或pyppeteer来启动打开adspower浏览器的方法,笔者在本专栏前面有详细介绍过,感兴趣的同学可往前翻阅《如何用selenium或pyppeteer来链接并打开指纹浏览器AdsPower》,文章内容包含完整…

封装动态表单组件

技术栈&#xff1a;vue2 js webpack 需求&#xff1a; 利用数据渲染表单&#xff0c;实现代码的精简化及效率的提升。 效果图&#xff1a; 封装的组件&#xff1a; <div v-if"formConfig"><el-formv-bind"$attrs"ref"formDom":model…

Spring Cloud Kubernetes:在Kubernetes中部署和管理微服务

Spring Cloud Kubernetes&#xff1a;在Kubernetes中部署和管理微服务 一、简介1. Spring Cloud Kubernetes2. 在Kubernetes中部署和管理微服务 二、概述1 Kubernetes概述2 Spring Cloud概述3 Spring Cloud Kubernetes概述 三、搭建Spring Cloud Kubernetes环境1 Kubernetes搭建…

new/delete与malloc/free的区别

new/delete与malloc/free的区别 new、delete是C中的操作符&#xff0c;而malloc、free是标准库函数。 new 和 delete 是类型安全的&#xff0c;它们能够根据要分配的对象类型进行内存分配和释放&#xff0c;并调用相应的构造函数和析构函数。而 malloc 和 free 则是无类型的&am…

【LeetCode-中等题】240. 搜索二维矩阵 II

文章目录 题目方法一&#xff1a;暴力双for查找方法二&#xff1a;二分查找&#xff0c;对每二维数组进行拆分&#xff0c;一行一行的进行二分查找方法三&#xff1a;列倒序Z字形查找 题目 方法一&#xff1a;暴力双for查找 public boolean searchMatrix(int[][] matrix, int …

Android DataBinding 基础入门(学习记录)

目录 一、DataBinding简介二、findViewById 和 DataBinding 原理及优缺点1. findViewById的优缺点2. DataBinding的优缺点 三、Android mvvm 之 databinding 原理1. 简介和三个主要的实体DataViewViewDataBinding 2.三个功能2.1. rebind 行为2.2 observe data 行为2.3 observe …

探索隧道ip如何助力爬虫应用

在数据驱动的世界中&#xff0c;网络爬虫已成为获取大量信息的重要工具。然而&#xff0c;爬虫在抓取数据时可能会遇到一些挑战&#xff0c;如IP封禁、访问限制等。隧道ip&#xff08;TunnelingProxy&#xff09;作为一种强大的解决方案&#xff0c;可以帮助爬虫应用更高效地获…

MYSQL数据库恢复(误删操作)

1.安装依赖 pip3 install PyMySQL0.7.8 pip3 install wheel0.24.0 pip3 install mysql-replication0.9 mysql8的版本问题&#xff0c;安装对应的binlog2sql工具版本 pip3 uninstall PyMySQL pip3 install PyMySQL0.9.3 2.Binlog2sql的使用 2.1解压binlog2sql.zip到任意位…

Ceph IO流程及数据分布

1. Ceph IO流程及数据分布 1.1 正常IO流程图 步骤&#xff1a; client 创建cluster handler。client 读取配置文件。client 连接上monitor&#xff0c;获取集群map信息。client 读写io 根据crshmap 算法请求对应的主osd数据节点。主osd数据节点同时写入另外两个副本节点数据。…

Node.js 应用的御用品: Node.js 错误处理系统

开发中&#xff0c;有些开发者会积极寻求处理错误&#xff0c;力求减少开发时间&#xff0c;但也有些人完全忽略了错误的存在。正确处理错误不仅意味着能够轻松发现和纠正错误&#xff0c;而且还意味着能够为大型应用程序开发出稳健的代码库。 特别是对于 Node.js 开发人员&am…

由Android10适配到Android12时遇到相关编译失败问题

最近Android系统各大应用商店联合发出公告&#xff0c;处于个人隐私安全考虑&#xff0c;强制APP适配到Android 11及以上版本。下面是其中应用市场的公告&#xff08;顺带提醒没适配的同学&#xff09;&#xff1a; 适配前的开发环境 名称版本Android studioGiraffe | 2022.3…

【板栗糖GIS】——360浏览器的下载图标隐藏在内部不方便,怎么修改

目录 1. 设置前的本来样子 2. 登录360的皮肤中心 3. 使用se13的经典皮肤 最近edge浏览器最近使用bilibili和notion都非常卡&#xff0c;时不时崩溃&#xff0c;不得不换浏览器使用&#xff0c;试来试去360浏览器最得我心&#xff0c;只不过广告太多&#xff0c;调教也是花了…

gitlab升级

1.下载需要的版本 wget -c https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-15.7.6-ce.0.el7.x86_64.rpm --no-check-certificate gitlab-ce-15.4.6-ce.0.el7.x86_64.rpm gitlab-ce-15.7.6-ce.0.el7.x86_64.rpm gitlab-ce-15.9.7-ce.0.el7.x86_64.rpm g…