你必须要懂的 Web 安全
Category(分类): Other Status: 持续更新
作为前端工程师,我们也逃不开 Web 安全问题。本文尽量保留原文关于 XSS、CSRF、点击劫持、SQL 注入和 DDoS 的介绍,同时把已经过时或容易误导的说法改成现在更准确的表述,并补充现代浏览器和服务端的防御方式。
本文中的域名、请求和代码均为教学示例。安全测试只能在自己拥有或得到明确授权的系统中进行,不要把攻击代码投放到第三方生产环境。
更多文章可戳: github.com/YvetteLau/Blog
阅读完本文,至少应该能够回答:
- 前端常见的攻击方式有哪些?
- 什么是 XSS?它有哪些类型,应该如何防范?
- 什么是 CSRF?为什么只改用 POST 仍然不够?
- 如何防御点击劫持?
- 如何在授权范围内检查网站的安全性?
原文还准备了一个用于本地演示的示例项目,可以参考 Security 示例目录。示例项目年代较早,运行前应检查依赖和代码,不要直接部署到线上。
一、先建立几个基础认识
1. 同源策略不是“禁止所有跨域请求”
浏览器把 协议(scheme)+ 主机名(host)+ 端口(port) 组成的三元组称为源(origin)。例如:
https://app.example.com:443和https://api.example.com:443不是同源;http://example.com和https://example.com不是同源;https://example.com:443和https://example.com:8443也不是同源。
同源策略主要限制一个源中的脚本读取另一个源的响应和 DOM,但并不等于“跨源请求完全不会发出”。<img>、<script>、表单、链接和部分简单请求仍可能跨源发送,这正是 CSRF、JSONP 和 XSSI 等问题的基础。
“同站(site)”和“同源(origin)”也不是一回事。SameSite Cookie 主要按站点判断,站点通常还会考虑可注册域名和协议;安全设计不能把“同站”简单当成“同源”。
2. 不要把安全责任放在前端
前端校验可以改善体验、减少无效请求,但用户可以修改 JavaScript、绕过表单校验并直接调用接口。认证、授权、CSRF 校验、输入约束和业务规则必须在服务端再次执行。
二、XSS:跨站脚本攻击
XSS(Cross-Site Scripting,跨站脚本攻击)是一种代码注入攻击。攻击者设法让不可信数据进入 HTML、JavaScript、CSS 或 URL 等会被浏览器解析的上下文,浏览器随后把数据当成代码执行。
XSS 不一定真的“跨站”,名称来自历史背景;它的核心是不可信数据被当成了可执行内容。成功后,攻击者可能:
- 以受害者当前页面的权限读取页面数据、修改页面或发起请求;
- 窃取可被 JavaScript 读取的 Cookie、
localStorage、令牌或表单内容; - 诱导输入密码、钓鱼,或把恶意内容传播给其他用户。
如果会话 Cookie 设置了 HttpOnly,脚本不能通过 document.cookie 读取它,但 XSS 仍可能借助浏览器自动携带 Cookie 发起已登录请求。因此 HttpOnly 是降低影响面的措施,不是 XSS 的根本解决方案。

1. XSS 的三种常见类型
按恶意数据到达页面的路径,通常分为存储型、反射型和 DOM 型。实际漏洞可能同时具有多种特征。
1.1 反射型 XSS
攻击者构造一个包含恶意参数的 URL,服务端把参数未经安全处理地反射到响应 HTML 中,用户打开链接后脚本执行。搜索页、错误页、跳转页和带查询参数的页面比较常见。
典型流程:
- 攻击者构造带有恶意参数的 URL;
- 受害者被诱导打开 URL;
- 服务端把参数拼进 HTML 返回;
- 浏览器把这段参数解析成代码并执行。
POST 数据也可能触发反射型 XSS,只是通常需要构造表单并诱导用户提交,因此比 URL 传播的场景少见。
**防范重点是根据输出上下文进行编码,而不是简单地“过滤几个字符”。**例如,下面的查询参数编码只适用于 URL 的一个参数值:
const url = `/search?q=${encodeURIComponent(keyword)}`
如果变量最终要放到 HTML 文本中,encodeURIComponent 并不等价于 HTML 编码;如果变量放在 JavaScript、CSS、HTML 属性或 URL 中,也各自需要不同的处理方式。
服务端模板应使用默认转义:
app.get('/welcome', (req, res) => {
const type = typeof req.query.type === 'string' ? req.query.type : ''
// 使用会自动进行 HTML 转义的模板输出 type
res.render('welcome', { type })
})
不要把用户输入直接拼到 HTML、事件属性或内联脚本中。过去常说的“Chrome 和 Safari 会自动拦截 XSS、Firefox 不会”已经不能作为安全依据;浏览器过滤器会变化,而且不能替代应用自身的防御。
1.2 DOM 型 XSS
DOM 型 XSS 的恶意数据可能只存在于浏览器端,例如来自 location、URL hash、document.referrer、postMessage、Web Storage 或接口响应。服务端不一定能看到这段数据,前端 JavaScript 将它写入危险 DOM 接收器后即可触发。
常见来源(source)包括:
location.href、location.search、location.hash;document.referrer、window.name、postMessage消息;localStorage、sessionStorage、接口返回值和用户可编辑的表单。
常见危险接收器(sink)包括:
innerHTML、outerHTML、insertAdjacentHTML、document.write();eval()、new Function()、字符串形式的setTimeout()/setInterval();- 事件属性(如
onclick、onerror)和未经校验的href、src; - 把不可信字符串交给会解析 HTML 或脚本的第三方组件。
只需要显示文本时,优先使用 textContent、表单的 value、createTextNode 或框架的默认插值:
const keyword = new URL(location.href).searchParams.get('q') ?? ''
document.querySelector('#result').textContent = keyword
setAttribute() 只有在属性名固定且属性本身安全时才适合作为安全接收器,例如 setAttribute('title', value)。不要让用户控制属性名,也不要把不可信内容写入 on* 事件属性。
如果业务确实需要用户提交富文本,不能简单删除 <script> 标签,因为事件属性、危险 URL 和 SVG 等也可能执行代码。应使用经过维护的 HTML Sanitizer(例如 DOMPurify),配置允许的标签和属性,并在清洗后不要再次拼接或修改成不安全的 HTML:
const cleanHtml = DOMPurify.sanitize(untrustedHtml)
container.innerHTML = cleanHtml
对 href、src 等 URL 还应先解析并限制协议,至少拒绝 javascript: 等脚本协议。URL 编码本身不能把一个危险协议变成安全 URL。
Vue、React、Angular 等框架通常会自动转义文本,但 v-html、dangerouslySetInnerHTML、bypassSecurityTrust... 等逃生口会绕过默认保护,使用前必须完成可靠的清洗和审查。
1.3 存储型 XSS
恶意内容被保存到数据库、缓存或其他持久化存储中,之后在评论、帖子、私信、昵称、后台工单等页面展示给其他用户。因为一次提交可能影响很多人,存储型 XSS 的影响通常比反射型更大。
典型流程:
- 攻击者提交包含恶意内容的数据;
- 服务端把数据保存到数据库;
- 页面读取数据并输出到 HTML;
- 访问页面的用户执行了恶意内容。
正确的防御链路是:
- 输入校验:对长度、类型、格式和业务范围使用白名单,减少垃圾数据;
- 安全存储:不要因为“看起来像 HTML”就把输入当作可信代码;
- 按输出上下文编码:普通文本在 HTML 文本节点输出时使用 HTML 转义;
- 富文本单独清洗:只在明确需要 HTML 时使用白名单 Sanitizer;
- 服务端负责最终防御:前端过滤不能防止抓包或直接构造请求。
“在入库前统一过滤所有内容”不是万能方案:它可能破坏原始数据,也无法知道数据未来会被放入什么输出上下文。输入校验、输出编码和必要的 HTML 清洗应各司其职。
2. XSS 的纵深防御
2.1 使用正确的输出编码和安全接收器
编码必须匹配上下文:
| 输出位置 | 优先做法 |
|---|---|
| HTML 文本节点 | 使用框架默认转义或 HTML 实体编码 |
| 普通 HTML 属性 | 使用引号包裹属性值并进行属性编码,属性名使用固定白名单 |
| URL 参数 | 使用 URL 百分号编码;完整 URL 还要校验协议和主机 |
| JavaScript 字符串 | 尽量不要拼接进脚本;必须使用经过验证的 JavaScript 编码器 |
| CSS | 不要拼接不可信选择器或样式代码,优先修改固定 CSS 属性 |
| 富文本 HTML | 使用成熟 Sanitizer,不能只做字符串替换 |
不要把同一种编码“全局套用”到所有地方,也不要依赖一个拦截器把所有请求统一编码。请求拦截器通常不知道变量最终会进入 HTML、JavaScript 还是 URL,容易造成漏防或双重编码。
2.2 Content Security Policy(CSP)
CSP 是浏览器的额外限制层,可以限制脚本、样式、图片、连接和页面被嵌入的来源。一个严格策略的示意如下:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-<BASE64_NONCE>'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
实际部署时应根据应用资源逐项配置,并使用每次响应独立的随机 nonce 或哈希;示例中的 <BASE64_NONCE> 只是占位符,必须由服务端替换成密码学安全的随机 Base64 值,不能原样复制。尽量不要使用 'unsafe-inline' 和 'unsafe-eval'。可以先使用 Content-Security-Policy-Report-Only 观察报告,再逐步执行策略。CSP 的 frame-ancestors、报告等功能应通过 HTTP 响应头配置;把 CSP 放进 <meta> 不能替代所有响应头能力。
CSP 是纵深防御,不是输出编码的替代品。旧浏览器、不正确的白名单以及被允许加载的第三方脚本都可能削弱它。
Chromium 系浏览器还可以评估 Trusted Types,例如在确认兼容性后使用:
Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default
它可以约束 innerHTML 等 DOM XSS 接收器必须通过经过审查的策略创建内容,但目前不能当作所有浏览器的通用替代方案。
2.3 Cookie 和会话保护
敏感会话 Cookie 至少应考虑:
Set-Cookie: __Host-session=随机会话值; Path=/; Secure; HttpOnly; SameSite=Lax
Secure:只通过 HTTPS 发送;HttpOnly:禁止 JavaScript 读取,降低 XSS 窃取 Cookie 的影响;SameSite:减少跨站请求自动携带 Cookie 的机会,主要用于 CSRF 防御;__Host-:要求 HTTPS、Path=/且不能设置Domain,把 Cookie 限制在当前主机。
Cookie 属性不能阻止恶意脚本执行,也不能阻止 XSS 代表用户发起请求。不要把长期会话令牌、密码等敏感信息放进 localStorage;一旦发生 XSS,Web Storage 通常可以被直接读取。
2.4 长度、格式和速率限制
对用户名、评论、文件名、URL 等字段设置合理长度和格式限制,可以减少攻击面和资源消耗;但它们只能是辅助措施,不能替代上下文编码、清洗和 CSP。验证码适合转账、改密等高风险操作的二次确认,不是 XSS 的通用修复方案。
3. 如何检测 XSS
只能在授权范围内进行检测,建议结合代码审查、自动化扫描和人工验证:
- 盘点来源和接收器,重点检查 URL、消息、存储和接口数据的流向;
- 在本地或测试环境使用无害的唯一标记验证是否被当成 HTML,而不是直接使用会执行脚本的字符串;
- 使用浏览器开发者工具查看 DOM、响应头和 CSP 报告;
- 使用 OWASP Web Security Testing Guide 设计测试用例;
- 使用 OWASP ZAP 或商业扫描器进行辅助扫描,人工复核结果。
原文中的“通用 XSS 攻击字符串”来自较早的过滤器绕过测试,长度很大、容易造成误报或破坏页面,不能当成所有浏览器和所有上下文都有效的检测方法,更不能直接投放生产环境。

Arachni、w3af 等工具可以作为历史资料或特定环境的补充,但使用前应确认项目维护状态和运行环境。Mozilla HTTP Observatory 主要检查 HTTP 安全响应头,不能替代 XSS、SQL 注入等应用漏洞测试。
三、CSRF:跨站请求伪造
CSRF(Cross-Site Request Forgery,跨站请求伪造)是指攻击者诱导用户访问第三方页面,由用户的浏览器向目标站点发起请求。浏览器可能自动带上目标站点的 Cookie 或其他隐式凭证,目标站点如果只验证“凭证有效”,就可能把伪造请求当成用户主动操作。
CSRF 通常不能让攻击者直接读取目标站点的 Cookie;攻击者也不一定需要读取响应,只需要让目标站点执行一个改变状态的操作即可。可能的后果包括修改邮箱、改密码、下单、转账或提升权限,具体取决于用户权限和接口能力。

1. 典型流程和常见误区
- 用户登录受信任的站点 A,浏览器保存了会话 Cookie;
- 用户没有退出 A,又访问了攻击者控制的站点 B;
- B 通过图片、表单、链接或其他方式向 A 发起请求;
- 如果 Cookie 满足浏览器的发送条件,浏览器会自动带上它;
- A 只检查 Cookie 便执行了操作。
过去常见的错误接口是用 GET 完成转账:
GET https://bank.example/transfer?to=attacker&amount=1000
攻击者可以把它放进图片或链接中。把接口改成 POST 仍然不等于解决 CSRF,因为第三方页面可以通过隐藏表单提交 POST:
<form action="https://bank.example/transfer" method="post">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
因此,所有改变状态的接口都不应使用 GET,且必须有额外的请求真实性校验。CORS 也不是 CSRF 防御:CORS 主要控制脚本能否读取响应,简单表单请求仍可能发送到服务端。

2. CSRF 防御
2.1 优先使用框架内置方案
先检查后端框架是否已经提供 CSRF 中间件和 Token 校验。内置方案通常包含 Token 生成、比较、失效和错误处理,优于每个项目自行发明一套实现。
2.2 不使用 GET 改变状态
GET、HEAD、OPTIONS 应保持安全和幂等语义;删除、修改、转账等操作使用 POST、PUT、PATCH 或 DELETE,并继续执行 Token、Origin 等校验。只改 HTTP 方法不能单独防御 CSRF。
2.3 Synchronizer Token(同步 Token)
有服务端会话的应用可以为会话生成一个高熵、不可预测且保密的 Token,放入 HTML 或接口响应,再由表单隐藏字段或 AJAX 自定义请求头带回:
<form action="/profile/email" method="post">
<input type="hidden" name="csrf_token" value="服务端生成的随机值">
<input name="email" type="email">
<button type="submit">保存</button>
</form>
服务端必须比较请求中的 Token 与当前会话中的 Token,不存在或不匹配就拒绝请求。Token 不应放到 URL 中,因为 URL 可能进入浏览器历史、代理日志、服务器日志和 Referer。每次请求都更换 Token 并非绝对必要;按请求更新可能导致用户打开多个页面或使用后退按钮时产生误报,应根据业务和框架取舍。
对于 AJAX/API,可以把 Token 放在自定义请求头中,例如 X-CSRF-Token。跨源页面不能随意设置这类请求头,浏览器通常会先发 CORS 预检;不过服务端仍应对所有接受的简单请求做好 CSRF 防护。
2.4 Double Submit Cookie
无服务端会话时,可以使用 Double Submit Cookie:浏览器保存一个 CSRF Cookie,同时在请求头或请求体中显式提交同一个值,服务端比较二者。
仅仅比较两个相同字符串的“朴素方案”可能受到 Cookie 注入或子域名控制影响。新系统更适合使用与会话绑定、由服务端密钥 HMAC 签名的 Double Submit Cookie,并使用安全的 Cookie 范围。CSRF Cookie 本身通常不能设置为 HttpOnly,否则前端无法读取它;真正的会话 Cookie 仍应使用 HttpOnly。
2.5 校验 Origin 和 Referer
对改变状态的请求,优先严格校验 Origin;必要时把 Referer 作为兼容性回退。比较时使用完整的协议、主机和端口白名单,不能只查找字符串,例如不能把 https://qq.com.attacker.example 当成 https://qq.com。
受隐私策略、书签、代理或浏览器行为影响,Referer 可能缺失或只包含源,因此它适合做纵深防御或监控,不应成为唯一防线。Origin: null 也必须按业务明确处理,不能无条件放行。
2.6 SameSite Cookie
SameSite 能减少跨站请求携带 Cookie 的机会,但应当与 Token 和来源校验组合使用:
| 属性 | 主要行为 | 注意事项 |
|---|---|---|
Strict | 跨站场景基本不发送 Cookie | 防护更强,但可能影响从外部链接进入网站的体验 |
Lax | 允许同站请求,以及部分跨站顶级安全导航(如 GET) | 跨站图片、脚本、iframe 等子资源通常不带 Cookie;不能代替 Token |
None | 允许跨站发送 Cookie | 必须同时设置 Secure,通常意味着需要额外 CSRF 防御 |
现代浏览器对未显式声明的 Cookie 通常采用接近 Lax 的默认策略,但兼容性、最近创建 Cookie 的宽松例外和第三方 Cookie 政策仍可能不同。不要把浏览器默认行为当成应用的唯一安全边界。
2.7 Fetch Metadata
现代浏览器可能发送 Sec-Fetch-Site、Sec-Fetch-Mode、Sec-Fetch-Dest 等 Fetch Metadata 请求头。服务端可以把 Sec-Fetch-Site: cross-site 的非安全方法作为明显的跨站请求拒绝,并对缺失请求头的旧客户端使用 Token 或 Origin/Referer 回退。它适合做额外防线,不应替代已有的 CSRF 方案。
2.8 验证码和用户交互
验证码、重新输入密码、二次确认或 WebAuthn 等用户交互适合保护转账、改密、删除账号等高风险操作。若每个普通请求都要求验证码,体验会很差,也不能代替基础 Token 校验。
XSS 可以在目标站点的同源上下文中读取 Token 并发起请求,因此 XSS 能绕过 CSRF 防护。CSRF 与 XSS 必须分别修复。

四、点击劫持(Clickjacking)
点击劫持也叫 UI Redress Attack。攻击者把目标页面放在透明或伪装的 iframe 中,再把按钮、图片等诱导内容放在上面,让用户以为自己点击的是假页面,实际上点击了目标页面中的操作。
1. 首选:CSP frame-ancestors
如果页面不需要被嵌入,建议通过 HTTP 响应头禁止所有祖先页面嵌入:
Content-Security-Policy: frame-ancestors 'none'
只允许同源页面嵌入:
Content-Security-Policy: frame-ancestors 'self'
如果必须允许合作方嵌入,应列出精确的来源:
Content-Security-Policy: frame-ancestors 'self' https://partner.example
frame-ancestors 必须通过响应头配置,写成 <meta http-equiv> 不可靠且不能替代响应头。
2. X-Frame-Options 兼容防线
X-Frame-Options 仍可作为旧浏览器的兼容防线:
X-Frame-Options: DENY
或:
X-Frame-Options: SAMEORIGIN
ALLOW-FROM 已过时,现代浏览器通常会忽略它;需要允许多个来源时应使用 CSP frame-ancestors。X-Frame-Options 也必须是 HTTP 响应头,放在 HTML 的 <meta> 中没有效果。
3. Frame busting 只能作为遗留浏览器的补充
过去常见的代码是:
if (top.location !== window.location) {
top.location = window.location
}
它可能被 iframe sandbox、多层嵌套、浏览器导航策略等方式绕过,不能替代服务端响应头。对于必须兼容非常老的浏览器,可以把它作为额外措施;现代应用应以 CSP 和 X-Frame-Options 为主。
SameSite Cookie 也能降低已登录页面在 iframe 中被利用的机会,但未登录页面、旧浏览器或不依赖 Cookie 的攻击仍可能存在,所以不能单独依赖它。
五、扩展:SQL 注入、DDoS 与 SYN Flood
这几类问题主要属于后端和基础设施安全,不是前端代码单独能够解决的。原文为了完整性进行了介绍,这里保留核心概念并修正防护方式。
1. SQL 注入
SQL 注入是把不可信输入拼接进 SQL 语句,使数据库把输入当成 SQL 代码执行。下面这种字符串拼接是不安全的:
const sql = `SELECT * FROM users WHERE name = '${name}'`
正确做法是使用参数化查询、预编译语句或 ORM 的绑定参数:
const result = await db.query(
'SELECT * FROM users WHERE name = ?',
[name]
)
不同数据库的占位符写法可能不同,应使用对应驱动提供的参数绑定。表名、列名、排序方向通常不能直接作为绑定参数,应从代码内置的白名单映射得到,而不是把用户输入直接拼接进去。
其他纵深防御包括:
- 为每个应用使用权限最小的数据库账号,禁止直接使用 DBA/管理员账号;
- 限制数据库和应用服务器的网络暴露面;
- 不把密码“加密后保存”当作密码存储方案,密码应使用 Argon2id、scrypt、bcrypt 或 PBKDF2 等专用慢哈希算法,并为每个密码使用唯一盐;
- 统一记录异常、限制查询结果规模并妥善处理错误信息。
“把单引号、双引号替换掉”或“把所有输入转义后再拼 SQL”都不是可靠的主防线;参数化查询才是首选。
2. DoS 与 DDoS
DoS(拒绝服务)是让服务无法正常响应,DDoS(分布式拒绝服务)则由大量分布式节点共同消耗目标的带宽、连接、CPU、内存或应用资源。攻击可能发生在:
- 网络带宽层,发送大量流量;
- 协议层,消耗连接和设备状态;
- 应用层,发送看似正常但成本很高的请求;
- 针对特定服务或用户制造不可用。
防护通常需要上游网络、CDN/Anycast、云清洗、WAF、限速、连接数限制、缓存、队列和监控配合。单靠前端加验证码不能解决大规模 DDoS;也不能把所有高流量都简单判定为攻击,需要结合基线、来源、请求特征和业务指标分析。
3. SYN Flood
TCP 三次握手中,服务器发送 SYN-ACK 后等待客户端 ACK 的阶段会占用半连接状态。攻击者可以制造大量未完成握手,使监听队列或连接状态资源被耗尽,正常连接因此受到影响。
只看到大量 SYN_RECV 或随机源 IP 不能单独证明遭受攻击,还应结合连接速率、完成率、带宽和上游设备指标判断。常见缓解手段包括:
- 启用并正确配置 SYN cookies;
- 合理调整监听队列、连接超时和防火墙连接限制;
- 在负载均衡器、云网络或运营商侧进行清洗和限速;
- 保持系统、内核和网络设备配置与实际流量模型匹配。
缩短超时或盲目增大半连接数可能只是暂时缓解,甚至引起新的资源消耗,不能作为通用答案。
六、现代 Web 安全响应头和工程措施
响应头不能修复业务漏洞,但成本低、适合做纵深防御。常见起点包括:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
- HSTS 只应在确认整个站点及子域都能稳定使用 HTTPS 后逐步开启;
nosniff减少 MIME 类型猜测,服务端仍必须设置正确的Content-Type;Referrer-Policy减少 URL 中敏感信息泄露;Permissions-Policy关闭应用不需要的摄像头、麦克风、定位等能力;- 对密码重置、个人信息和其他敏感响应使用合适的
Cache-Control,必要时使用no-store。
X-XSS-Protection、HPKP(Public-Key-Pins)和 X-Frame-Options: ALLOW-FROM 都属于过时方案,不应作为新项目的主防线。CSP、正确编码、Cookie 属性、依赖升级、日志告警和最小权限应共同工作。
七、安全检查清单
上线前至少检查:
- 所有用户可控数据都经过了与输出上下文匹配的编码或清洗;
- 没有把不可信内容直接交给
innerHTML、eval、事件属性或危险 URL; - 使用了 CSP,并通过 Report-Only 逐步收敛策略;
- 会话 Cookie 使用
Secure、HttpOnly、合适的SameSite,并尽量采用 host-only 范围; - 所有改变状态的接口禁用 GET,并有 Token、Origin/Referer 或 Fetch Metadata 等 CSRF 防护;
- 页面通过
frame-ancestors和兼容性的X-Frame-Options防止不必要的嵌入; - CORS 只允许明确的来源,带凭证时不使用
*,并正确设置Vary: Origin; - SQL 使用参数化查询,应用数据库账号遵循最小权限;
- 依赖、构建产物、第三方脚本和上传文件都有安全审查;
- 使用 OWASP WSTG、ZAP 等在授权测试环境中验证,并人工复核扫描结果;
- 有日志、告警、备份、补丁和漏洞修复流程。
参考资料
- OWASP Cross Site Scripting Prevention Cheat Sheet
- OWASP Cross-Site Request Forgery Prevention Cheat Sheet
- OWASP Clickjacking Defense Cheat Sheet
- OWASP HTTP Headers Cheat Sheet
- OWASP SQL Injection Prevention Cheat Sheet
- OWASP Password Storage Cheat Sheet
- OWASP Web Security Testing Guide
- MDN Content Security Policy
- MDN X-Frame-Options
- MDN SameSite cookies