33 _ 跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
通过上篇文章的介绍,我们知道了同源策略可以隔离各个站点之间的DOM交互、页面数据和网络通信,虽然严格的同源策略会带来更多的安全,但是也束缚了Web。这就需要在安全和自由之间找到一个平衡点,所以我们默认页面中可以引用任意第三方资源,然后又引入CSP策略来加以限制;默认XMLHttpRequest和Fetch不能跨站请求资源,然后又通过CORS策略来支持其跨域。
不过支持页面中的第三方资源引用和CORS也带来了很多安全问题,其中最典型的就是XSS攻击。
什么是XSS攻击
XSS全称是Cross Site Scripting,为了与“CSS”区分开来,故简称XSS,翻译过来就是“跨站脚本”。XSS攻击是指黑客往HTML文件中或者DOM中注入恶意脚本,从而在用户浏览页面时利用注入的恶意脚本对用户实施攻击的一种手段。
最开始的时候,这种攻击是通过跨域来实现的,所以叫“跨域脚本”。但是发展到现在,往HTML文件中注入恶意代码的方式越来越多了,所以是否跨域注入脚本已经不是唯一的注入手段了,但是XSS这个名字却一直保留至今。
当页面被注入了恶意JavaScript脚本时,浏览器无法区分这些脚本是被恶意注入的还是正常的页面内容,所以恶意注入JavaScript脚本也拥有所有的脚本权限。下面我们就来看看,如果页面被注入了恶意JavaScript脚本,恶意脚本都能做哪些事情。
- 可以窃取Cookie信息。恶意JavaScript可以通过“document.cookie”获取Cookie信息,然后通过XMLHttpRequest或者Fetch加上CORS功能将数据发送给恶意服务器;恶意服务器拿到用户的Cookie信息之后,就可以在其他电脑上模拟用户的登录,然后进行转账等操作。
- 可以监听用户行为。恶意JavaScript可以使用“addEventListener”接口来监听键盘事件,比如可以获取用户输入的信用卡等信息,将其发送到恶意服务器。黑客掌握了这些信息之后,又可以做很多违法的事情。
- 可以通过修改DOM伪造假的登录窗口,用来欺骗用户输入用户名和密码等信息。
- 还可以在页面内生成浮窗广告,这些广告会严重地影响用户体验。
除了以上几种情况外,恶意脚本还能做很多其他的事情,这里就不一一介绍了。总之,如果让页面插入了恶意脚本,那么就相当于把我们页面的隐私数据和行为完全暴露给黑客了。
恶意脚本是怎么注入的
现在我们知道了页面中被注入恶意的JavaScript脚本是一件非常危险的事情,所以网站开发者会尽可能地避免页面中被注入恶意脚本。要想避免站点被注入恶意脚本,就要知道有哪些常见的注入方式。通常情况下,主要有存储型XSS攻击、反射型XSS攻击和基于DOM的XSS攻击三种方式来注入恶意脚本。
1. 存储型XSS攻击
我们先来看看存储型XSS攻击是怎么向HTML文件中注入恶意脚本的,你可以参考下图:

通过上图,我们可以看出存储型XSS攻击大致需要经过如下步骤:
- 首先黑客利用站点漏洞将一段恶意JavaScript代码提交到网站的数据库中;
- 然后用户向网站请求包含了恶意JavaScript脚本的页面;
- 当用户浏览该页面的时候,恶意脚本就会将用户的Cookie信息等数据上传到服务器。
下面我们来看个例子,2015年喜马拉雅就被曝出了存储型XSS漏洞。起因是在用户设置专辑名称时,服务器对关键字过滤不严格,比如可以将专辑名称设置为一段JavaScript,如下图所示:

当黑客将专辑名称设置为一段JavaScript代码并提交时,喜马拉雅的服务器会保存该段JavaScript代码到数据库中。然后当用户打开黑客设置的专辑时,这段代码就会在用户的页面里执行(如下图),这样就可以获取用户的Cookie等数据信息。

当用户打开黑客设置的专辑页面时,服务器也会将这段恶意JavaScript代码返回给用户,因此这段恶意脚本就在用户的页面中执行了。
恶意脚本可以通过XMLHttpRequest或者Fetch将用户的Cookie数据上传到黑客的服务器,如下图所示:

黑客拿到了用户Cookie信息之后,就可以利用Cookie信息在其他机器上登录该用户的账号(如下图),并利用用户账号进行一些恶意操作。

以上就是存储型XSS攻击的一个典型案例,这是乌云网在2015年曝出来的,虽然乌云网由于某些原因被关停了,但是你依然可以通过这个站点来查看乌云网的一些备份信息。
2. 反射型XSS攻击
在一个反射型XSS攻击过程中,恶意JavaScript脚本属于用户发送给网站请求中的一部分,随后网站又把恶意JavaScript脚本返回给用户。当恶意JavaScript脚本在用户页面中被执行时,黑客就可以利用该脚本做一些恶意操作。
这样讲有点抽象,下面我们结合一个简单的Node服务程序来看看什么是反射型XSS。首先我们使用Node来搭建一个简单的页面环境,搭建好的服务代码如下所示:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get(‘/’, function(req, res, next) {
res.render(‘index’, { title: ‘Express’,xss:req.query.xss });
});
module.exports = router;
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel=‘stylesheet’ href=‘/stylesheets/style.css’ />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<div>
<%- xss %>
</div>
</body>
</html>
上面这两段代码,第一段是路由,第二段是视图,作用是将URL中xss参数的内容显示在页面。我们可以在本地演示下,比如打开http://localhost:3000/?xss=123这个链接,这样在页面中展示就是“123”了(如下图),是正常的,没有问题的。

但当打开http://localhost:3000/?xss=<script>alert(‘你被xss攻击了’)</script>这段URL时,其结果如下图所示:

通过这个操作,我们会发现用户将一段含有恶意代码的请求提交给Web服务器,Web服务器接收到请求时,又将恶意代码反射给了浏览器端,这就是反射型XSS攻击。在现实生活中,黑客经常会通过QQ群或者邮件等渠道诱导用户去点击这些恶意链接,所以对于一些链接我们一定要慎之又慎。
另外需要注意的是,Web服务器不会存储反射型XSS攻击的恶意脚本,这是和存储型XSS攻击不同的地方。
3. 基于DOM的XSS攻击
基于DOM的XSS攻击是不牵涉到页面Web服务器的。具体来讲,黑客通过各种手段将恶意脚本注入用户的页面中,比如通过网络劫持在页面传输过程中修改HTML页面的内容,这种劫持类型很多,有通过WiFi路由器劫持的,有通过本地恶意软件来劫持的,它们的共同点是在Web资源传输过程或者在用户使用页面的过程中修改Web页面的数据。
如何阻止XSS攻击
我们知道存储型XSS攻击和反射型XSS攻击都是需要经过Web服务器来处理的,因此可以认为这两种类型的漏洞是服务端的安全漏洞。而基于DOM的XSS攻击全部都是在浏览器端完成的,因此基于DOM的XSS攻击是属于前端的安全漏洞。
但无论是何种类型的XSS攻击,它们都有一个共同点,那就是首先往浏览器中注入恶意脚本,然后再通过恶意脚本将用户信息发送至黑客部署的恶意服务器上。
所以要阻止XSS攻击,我们可以通过阻止恶意JavaScript脚本的注入和恶意消息的发送来实现。
接下来我们就来看看一些常用的阻止XSS攻击的策略。
1. 服务器对输入脚本进行过滤或转码
不管是反射型还是存储型XSS攻击,我们都可以在服务器端将一些关键的字符进行转码,比如最典型的:
code:<script>alert(‘你被xss攻击了’)</script>
这段代码过滤后,只留下了:
code:
这样,当用户再次请求该页面时,由于<script>标签的内容都被过滤了,所以这段脚本在客户端是不可能被执行的。
除了过滤之外,服务器还可以对这些内容进行转码,还是上面那段代码,经过转码之后,效果如下所示:
code:<script>alert('你被xss攻击了')</script>
经过转码之后的内容,如<script>标签被转换为<script>,因此即使这段脚本返回给页面,页面也不会执行这段脚本。
2. 充分利用CSP
虽然在服务器端执行过滤或者转码可以阻止 XSS 攻击的发生,但完全依靠服务器端依然是不够的,我们还需要把CSP等策略充分地利用起来,以降低 XSS攻击带来的风险和后果。
实施严格的CSP可以有效地防范XSS攻击,具体来讲CSP有如下几个功能:
- 限制加载其他域下的资源文件,这样即使黑客插入了一个JavaScript文件,这个JavaScript文件也是无法被加载的;
- 禁止向第三方域提交数据,这样用户数据也不会外泄;
- 禁止执行内联脚本和未授权的脚本;
- 还提供了上报机制,这样可以帮助我们尽快发现有哪些XSS攻击,以便尽快修复问题。
因此,利用好CSP能够有效降低XSS攻击的概率。
3. 使用HttpOnly属性
由于很多XSS攻击都是来盗用Cookie的,因此还可以通过使用HttpOnly属性来保护我们Cookie的安全。
通常服务器可以将某些Cookie设置为HttpOnly标志,HttpOnly是服务器通过HTTP响应头来设置的,下面是打开Google时,HTTP响应头中的一段:
set-cookie: NID=189=M8q2FtWbsR8RlcldPVt7qkrqR38LmFY9jUxkKo3-4Bi6Qu_ocNOat7nkYZUTzolHjFnwBw0izgsATSI7TZyiiiaV94qGh-BzEYsNVa7TZmjAYTxYTOM9L_-0CN9ipL6cXi8l6-z41asXtm2uEwcOC5oh9djkffOMhWqQrlnCtOI; expires=Sat, 18-Apr-2020 06:52:22 GMT; path=/; domain=.google.com; HttpOnly我们可以看到,set-cookie属性值最后使用了HttpOnly来标记该Cookie。顾名思义,使用HttpOnly标记的Cookie只能使用在HTTP请求过程中,所以无法通过JavaScript来读取这段Cookie。我们还可以通过Chrome开发者工具来查看哪些Cookie被标记了HttpOnly,如下图:

HttpOnly演示 从图中可以看出,NID这个Cookie的HttpOlny属性是被勾选上的,所以NID的内容是无法通过document.cookie是来读取的。
由于JavaScript无法读取设置了HttpOnly的Cookie数据,所以即使页面被注入了恶意JavaScript脚本,也是无法获取到设置了HttpOnly的数据。因此一些比较重要的数据我们建议设置HttpOnly标志。
总结
好了,今天我们就介绍到这里,下面我来总结下本文的主要内容。
XSS攻击就是黑客往页面中注入恶意脚本,然后将页面的一些重要数据上传到恶意服务器。常见的三种XSS攻击模式是存储型XSS攻击、反射型XSS攻击和基于DOM的XSS攻击。
这三种攻击方式的共同点是都需要往用户的页面中注入恶意脚本,然后再通过恶意脚本将用户数据上传到黑客的恶意服务器上。而三者的不同点在于注入的方式不一样,有通过服务器漏洞来进行注入的,还有在客户端直接注入的。
针对这些XSS攻击,主要有三种防范策略,第一种是通过服务器对输入的内容进行过滤或者转码,第二种是充分利用好CSP,第三种是使用HttpOnly来保护重要的Cookie信息。
当然除了以上策略之外,我们还可以通过添加验证码防止脚本冒充用户提交危险操作。而对于一些不受信任的输入,还可以限制其输入长度,这样可以增大XSS攻击的难度。
思考时间
今天留给你的思考题是:你认为前端开发者对XSS攻击应该负多大责任?
欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给更多的朋友。
相关文章:
33 _ 跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
通过上篇文章的介绍,我们知道了同源策略可以隔离各个站点之间的DOM交互、页面数据和网络通信,虽然严格的同源策略会带来更多的安全,但是也束缚了Web。这就需要在安全和自由之间找到一个平衡点,所以我们默认页面中可以引用任意第三…...
C++入门小结
C命名空间总结 C 中的命名空间(Namespace)是一种组织代码的方式,用于避免全局命名冲突。在同一个命名空间中,可以有相同名称的变量、函数和类,但它们彼此互不影响。下面是对 C 命名空间的一些总结: 定义命…...
Java 开发实例:Spring Boot+AOP+注解+Redis防重复提交(防抖)
文章目录 1. 环境准备2. 引入依赖3. 配置Redis4. 创建防重复提交注解5. 实现AOP切面6. 创建示例Controller7. 测试8. 进一步优化8.1 自定义异常处理8.2 提升Redis的健壮性 9. 总结 🎉欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨…...
使用difflib实现文件差异比较用html显示
1.默认方式,其中加入文本过长,需要换行,因此做 contenthtml_output.replace(</style>,table.diff td {word-wrap: break-word;white-space: pre-wrap;max-width: 100%;}</style>),添加换行操作 ps:当前te…...
【文末附gpt升级秘笈】AI热潮降温与AGI场景普及的局限性
AI热潮降温与AGI场景普及的局限性 摘要: 随着人工智能(AI)技术的迅猛发展,AI热一度席卷全球,引发了广泛的关注和讨论。然而,近期一些学者和行业专家对AI的发展前景提出了质疑,认为AI热潮将逐渐…...
Vue待学习
整个渲染过程了解 Vue实例?Vue模板?渲染函数render()?虚拟DOM VNode?模板编译器?diff算法 CSS相关 CSS高级学习?过渡? 待熟悉掌握 Vue-router?VueX?Vue-Cli、Webpack和…...
TOP150-LC88
/*给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。注意:最终,合并后数组不…...
使用Python和TCN进行时间序列预测:一个完整的实战示例
使用Python和TCN进行时间序列预测:一个完整的实战示例 时间卷积网络(TCN)已被证明在处理序列数据方面表现出色,尤其是在需要捕获长期依赖关系的任务中。在本文中,我们将通过一个简单的例子,展示如何使用Py…...
如何用R语言ggplot2画高水平期刊散点图
文章目录 前言一、数据集二、ggplot2画图1、全部代码2、细节拆分1)导包2)创建图形对象3)主题设置4)轴设置5)图例设置6)散点颜色7)保存图片 前言 一、数据集 数据下载链接见文章顶部 处理前的数据…...
Python基于 Jupyter Notebook 的图形可视化工具库之ipysigma使用详解
概要 在数据科学和网络分析中,图(Graph)结构是一种常用的数据结构,用于表示实体及其关系。为了方便图数据的可视化和交互操作,ipysigma 提供了一个基于 Jupyter Notebook 的图形可视化工具。通过 ipysigma,用户可以在 Jupyter Notebook 中创建、编辑和展示图结构,方便进…...
四叉树和KD树
1. 简介 四叉树和KD树都是用于空间数据索引和检索的树状数据结构。它们通过将空间递归地划分为更小的区域,并存储每个区域内的点,来实现快速搜索和范围查询。 2. 四叉树 2.1 定义 四叉树是一种树状数据结构,它将二维空间递归地划分为四个…...
C语言中结构体使用.与->访问成员变量的区别
文章目录 前言点运算符(.)箭头运算符(->)总结 前言 在C语言中,. 和 -> 都是用来访问结构体成员的运算符,但它们的使用场景和含义有所不同。 提示:以下是本篇文章正文内容,下面…...
计算机二级Access选择题考点
在Access中,若要使用一个字段保存多个图像、图表、文档等文件,应该设置的数据类型是附件。在“销售表"中有字段:单价、数量、折扣和金额。其中,金额单价x数量x折扣,在建表时应将字段"金额"的数据类型定义为计算。若…...
人工智能历史与现状
1 人工智能历史与现状 1.1 人工智能的概念和起源 1.1.1 人工智能的概念 人工智能 (Artificial Intelligence ,AI)是一门研究如何使计算机 能够模拟人类智能行为的科学和技术,目标在于开发能够感知、理解、 学习、推理、决策和解决问题的智能机器。人工智能的概念主要包含 以…...
【git使用一】windows下git下载、安装和卸载
目录 (1)下载安装包 (2)安装git (3)安装验证 (4)卸载git (1)下载安装包 官网下载地址:Git 国内镜像下载地址:CNPM Binaries Mir…...
JVM 类加载器的工作原理
JVM 类加载器的工作原理 类加载器(ClassLoader)是一个用于加载类文件的子系统,负责将字节码文件(.class 文件)加载到 JVM 中。Java 类加载器允许 Java 应用程序在运行时动态地加载、链接和初始化类。 2. 类加载器的工…...
ARM Cortex-M4 CPU指令大全:作用、原理与实例
引言 在计算机系统中,CPU(中央处理器)是执行各种指令的核心部件。ARM Cortex-M4是广泛应用于嵌入式系统中的一款处理器,其指令集架构(ISA)基于ARMv7-M。本文将介绍ARM Cortex-M4处理器中的常见指令&#x…...
Mysql学习(九)——存储引擎
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 七、存储引擎7.1 MySQL体系结构7.2 存储引擎简介7.3 存储引擎特点7.4 存储引擎选择7.5 总结 七、存储引擎 7.1 MySQL体系结构 连接层:最上层是一些客户…...
TFT屏幕波形显示
REVIEW 关于TFT显示屏,之前已经做过彩条显示: TFT显示屏驱动_tft驱动-CSDN博客 关于ROM IP核,以及coe文件生成: FPGA寄存器 Vivado IP核_fpga寄存器资源-CSDN博客 1. TFT屏幕ROM显示正弦波 ①生成coe文件 %% sin-cos wave dat…...
服务器无法远程桌面连接不上的问题排查与解决方案
一、问题概述 当尝试使用远程桌面协议(RDP)连接至服务器时,如果连接失败,这通常意味着存在一些配置问题、网络问题或服务器本身的问题。此类问题对于管理员而言,需要系统地进行排查和解决。 二、排查步骤 1. 检查网…...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
