xss: 跨站脚本攻击(Cross Site Scripting,本来缩写是CSS,为了和层叠样式的CSS有所区别,所以在安全领域叫“XSS”。)是最常见和基本的攻击 WEB 网站方法。
XSS 的原理是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而可以达到攻击者盗取用户信息或其他侵犯用户安全隐私的目的。
XSS主要分为三类:
1、DOM XSS :
DOM即文本对象模型,DOM通常代表在html、xhtml和xml中的对象,使用DOM可以允许程序和脚本动态的访问和更新文档的内容、结构和样式。它不需要服务器解析响应的直接参与,触发XSS靠的是浏览器端的DOM解析,可以认为完全是客户端的事情。
实际上就是前端 js 代码把不可信的内容插入到了页面。在使用 .innerHTML
、.outerHTML
、.appendChild
、document.write()
等API时要特别小心,不要把不可信的数据作为 HTML 插到页面上,尽量使用 .innerText
、.textContent
、.setAttribute()
等。
2、反射型 XSS :
反射型XSS也被称为非持久性XSS,是现在最容易出现的一种XSS漏洞。发出请求时,XSS代码出现在URL中,最后输入提交到服务器,服务器解析后在响应内容中出现这段XSS代码,最后浏览器解析执行。也就是说,黑客往往需要引诱用户点击一个非法链接,才能攻击成功。
例如一个非法连接中可能存在这样一个script:
当执行这个非法链接后,即可将用户的cookie写入到xx.xx.xx.xx的服务器下的cookie.txt文件中。
3、存储型 XSS :
存储型XSS又被称为持久性XSS,它是最危险的一种跨站脚本,相比反射型XSS和DOM型XSS具有更高的隐蔽性,所以危害更大,因为它不需要用户手动触发。 允许用户存储数据的web程序都可能存在存储型XSS漏洞,当攻击者提交一段XSS代码后,被服务器端接收并存储,当所有浏览者访问某个页面时都会被XSS,其中最典型的例子就是留言板。
XSS预防:
1、过滤特殊字符
2、设置httpOnly,浏览器将禁止页面的JS访问带有HttpOnly属性的Cookie。HttpOnly解决的是XSS后的Cookie劫持攻击。
3、在服务端使用 HTTP的 Content-Security-Policy
头部来指定策略,或者在前端设置 meta
标签。
跨站请求伪造 (Cross-Site Request Forgery) 是一种伪造跨站请求的攻击方式. 例如利用你在 A 站 (攻击目标) 的 cookie / 权限等, 在 B 站 (恶意/钓鱼网站) 拼装 A 站的请求.
已知某站点 A 删除的接口是 get 到某个地址, 并指定一个帖子的 id. 在网站 B 上组织一个删除A站某文章的get请求. 然后A站用户访问B站,触发该请求. 就可以不知情的情况下删除某个帖子.
同源策略是最早用于防止 CSRF 的一种方式, 即关于跨站请求 (Cross-Site Request) 只有在同源/信任的情况下才可以请求. 但是如果一个网站群, 在互相信任的情况下, 某个网站出现了问题:
a.public.com
b.public.com
c.public.com
...
以上情况下, 如果 c.public.com 上没有预防 xss 等情况, 使得攻击者可以基于此站对其他信任的网站发起 CSRF 攻击.
另外同源策略主要是浏览器来进行验证的, 并且不同浏览器的实现又各自不同, 所以在某些浏览器上可以直接绕过, 而且也可以直接通过短信等方式直接绕过浏览器.
预防:
在 HTTP 头中自定义属性并验证
检查 CSRF token.
cookie中加入hash随机数.
通过检查来过滤简单的 CSRF 攻击, 主要检查一下两个 header:Origin Header和Referer Header
注入攻击是指当所执行的一些操作中有部分由用户传入时, 用户可以将其恶意逻辑注入到操作中. 当你使用 eval, new Function 等方式执行的字符串中有用户输入的部分时, 就可能被注入攻击. 上文中的 XSS 就属于一种注入攻击. 前面的章节中也提到过 Node.js 的 child_process.exec 由于调用 bash 解析, 如果执行的命令中有部分属于用户输入, 也可能被注入攻击.
Sql 注入是网站常见的一种注入攻击方式. 其原因主要是由于登录时需要验证用户名/密码, 其执行 sql 类似:
SELECT * FROM users WHERE usernae = 'myName' AND password = 'mySecret';
其中的用户名和密码属于用户输入的部分, 那么在未做检查的情况下, 用户可能拼接恶意的字符串来达到其某种目的, 例如上传密码为 '; DROP TABLE users; --
使得最终执行的内容为:
SELECT * FROM users WHERE usernae = 'myName' AND password = ''; DROP TABLE users; --';
其能实现的功能, 包括但不限于删除数据 (经济损失), 篡改数据 (密码等), 窃取数据 (网站管理权限, 用户数据) 等. 防治手段常见于:
给表名/字段名加前缀 (避免被猜到)
报错隐藏表信息 (避免被看到, 12306 早起就出现过的问题)
过滤可以拼接 SQL 的关键字符
对用户输入进行转义
验证用户输入的类型 (避免 limit, order by 等注入)
看个简单的情况:
let {user, pass, age} = ctx.query;
db.collection.find({
user, pass,
$where: `this.age >= ${age}`
})
那么这里的 age 就可以注入了. 另外 GET/POST 还可以传递深层结构 (比如 ?name[0]=alan 传递上来), 通过 qs 之类的模块解析后导致注入, 如 cnodejs 遭遇 mongodb 注入.
Node.js 的 crypto
模块封装了诸多的加密功能, 包括 OpenSSL 的哈希、HMAC、加密、解密、签名和验证函数等.
加密是如何保证用户密码的安全性?
在客户端加密, 是增加传输的过程中被第三方嗅探到密码后破解的成本. 对于游戏, 在客户端加密是防止外挂/破解等. 在服务端加密 (如 md5) 是避免管理数据库的 DBA 或者攻击者攻击数据库之后直接拿到明文密码, 从而提高安全性.
早期的网络传输协议由于只在大学内使用, 所以是默认互相信任的. 所以传统的网络通信可以说是没有考虑网络安全的. 早年的浏览器大厂网景公司为了应对这个情况设计了 SSL (Secure Socket Layer), SSL 的主要用途是:
认证用户和服务器, 确保数据发送到正确的客户机和服务器;
加密数据以防止数据中途被窃取;
维护数据的完整性, 确保数据在传输过程中不被改变.
存在三个特性:
机密性:SSL协议使用密钥加密通信数据
可靠性:服务器和客户都会被认证, 客户的认证是可选的
完整性:SSL协议会对传送的数据进行完整性检查
1999年, SSL 因为应用广泛, 已经成为互联网上的事实标准. IETF 就在那年把 SSL 标准化/强化. 标准化之后的名称改为传输层安全协议 (Transport Layer Security, TLS). 很多相关的文章都把这两者并列称呼 (TLS/SSL), 因为这两者可以视作同一个东西的不同阶段.
在网络上, 每个网站都在各自的服务器上, 想要确保你访问的是一个正确的网站, 并且访问到这个网站正确的数据 (没有被劫持/篡改), 除了需要传输安全之外, 还需要安全的认证, 认证不能由目标网站进行, 否则恶意/钓鱼网站也可以自己说自己是对的, 所以为了能在网络上维护网络之间的基本信任, 早期的大厂们合力推动了一项名为 PKI 的基础设施, 通过第三方来认证网站.
公钥基础设施 (Public Key Infrastructure, PKI) 是一种遵循标准的, 利用公钥加密技术为电子商务的开展提供一套安全基础平台的技术和规范. 其基础建置包含认证中心 (Certification Authority, CA) 、注册中心 (Register Authority, RA) 、目录服务 (Directory Service, DS) 服务器.
由 RA 统筹、审核用户的证书申请, 将证书申请送至 CA 处理后发出证书, 并将证书公告至 DS 中. 在使用证书的过程中, 除了对证书的信任关系与证书本身的正确性做检查外, 并透过产生和发布证书废止列表 (Certificate Revocation List, CRL) 对证书的状态做确认检查, 了解证书是否因某种原因而遭废弃. 证书就像是个人的身分证, 其内容包括证书序号、用户名称、公开金钥 (Public Key) 、证书有效期限等.
在 TLS/SLL 中你可以使用 OpenSSL 来生成 TLS/SSL 传输时用来认证的 public/private key. 不过这个 public/private key 是自己生成的, 而通过 PKI 基础设施可以获得权威的第三方证书 (key) 从而加密 HTTP 传输安全. 目前博客圈子里比较流行的是 Let's Encrypt 签发免费的 HTTPS 证书.
需要注意的是, 如果 PKI 受到攻击, 那么 HTTPS 也一样不安全. 可以参见 HTTPS 劫持 - 知乎讨论 中的情况, 证书由 CA 机构签发, 一般浏览器遇到非权威的 CA 机构是会告警的 (参见 12306), 但是如果你在某些特殊的情况下信任了某个未知机构/证书, 那么也可能被劫持.
此外有的 CA 机构以邮件方式认证, 那么当某个网站的邮件服务收到攻击/渗透, 那么攻击者也可能以此从 CA 机构获取权威的正确的证书.
DDoS即Distributed Denial of Service,分布式拒绝服务。也就是攻击者借助或者利用服务器技术,将多个计算机(肉鸡或僵尸机)联合起来作为攻击平台,对一个或者多个目标服务器,同一时间发送大量垃圾信息,或利用某种干扰信息的方式,导致目标服务器无法及时响应正常用户正常请求,或者直接导致目标服务器宕机,从而无法为正常用户提供服务的一种攻击行为。
攻击方式:
端口扫描攻击
ping洪水(flooding)攻击
SYN洪水(flooding)攻击
FTP跳转攻击
防范手段:
保证服务器系统的安全,确保服务器软件没有任何漏洞,防止攻击者入侵。
确保服务器采用最新系统,并打上安全补丁。
在服务器上删除未使用的服务,关闭未使用的端口。
对于服务器上运行的网站,确保其打了最新的补丁,没有安全漏洞。
隐藏服务器的真实IP地址
Clickjacking: 点击劫持,是指利用透明的按钮或连接做成陷阱,覆盖在 Web 页面之上。然后诱使用户在不知情的情况下,点击那个连接访问内容的一种攻击手段。这种行为又称为界面伪装(UI Redressing) 。
大概有两种方式:
一张图了解
一般步骤
X-FRAME-OPTIONS;
X-FRAME-OPTIONS HTTP 响应头是用来给浏览器指示允许一个页面可否在,
或者
中展现的标记。网站可以使用此功能,来确保自己网站内容没有被嵌到别人的网站中去,也从而避免点击劫持的攻击。
有三个值:
配置 X-FRAME-OPTIONS:
- Apache
把下面这行添加到 'site' 的配置中:
```
Header always append X-Frame-Options SAMEORIGIN
```
- nginx
把下面这行添加到 'http', 'server' 或者 'location',配置中
```
add_header X-Frame-Options SAMEORIGIN;
```
- IIS
添加下面配置到 Web.config 文件中
```
...
...
```
function locationTop(){
if (top.location != self.location) {
top.location = self.location; return false;
}
return true;
}
locationTop();
// 破解:
// 顶层窗口中放入代码
var location = document.location;
//或者
var location = "";
九、前端URL和文本框过滤器(防止sql注入和xss攻击过滤)
_checkInputSQLInjectionAttack(oField){
let re = /select|update|delete|exec|count|'|"|=|;|>|<|%/i;
if (re.test(oField.value)) {
openNotificationWithIcon("error");
oField.value = '';
oField.focus();
return false;
}
}
_checkUrlSQLInjectionAttack() {
let sUrl = window.location.search.toLowerCase();
let sQuery = sUrl.substring(sUrl.indexOf("=") + 1);
let re = /select|update|delete|truncate|join|union|exec|insert|drop|count|'|"|;|>|<|%/i;
if (re.test(sQuery)) {
openNotificationWithIcon("error");
window.location.href = sUrl.replace(sQuery, "");
}
}
componentDidMount() {
var that = this;
let eventDelegation = document.querySelectorAll('input');
if(window.addEventListener){
for(let i = 0, l = eventDelegation.length; i < l; ++i){
eventDelegation[i].addEventListener('blur', (e) => {
var target = e.target || e.srcElement;
this._checkInputSQLInjectionAttack(target);
}, false);
}
}else{
for (let i = 0, l = eventDelegation.length; i < l; ++i) {
eventDelegation[i].addEventListener('blur', (e) => {
var target = e.target || e.srcElement;
this._checkInputSQLInjectionAttack(target);
}, false);
}
}
}
ps:如果是React项目,input可以放在componentDidMount中完成,url可以放在初始化校验url阶段和请求数据前对接口进行过滤。