-
目前存在的问题,CSRF需要构建(redis、memcache、dcache)等内存服务器,解决多进程,多服务的cookie,以及token的管理;
-
使用g_tk的方式,进行csrf的防御。cookie进行token的转换,每次请求,都发送g_tk。
组件库引用:tnpm install --save xss 或npm install --save xss
- XSS允许标签以data-开头的属性
let str = '<div a="1" b="2" data-a="3" data-b="4">hello</div>';
let html = xss(str, {
onIgnoreTagAttr: function(tag, name, value, iswhiteAttr) {
if (name.substr(0, 5) === 'data-') {
// 通过内置的escapeAttrValue函数对属性值进行转义
return name + '="' + xss.escapeAttrValue(value) + '"';
}
}
});
- XSS过滤特定标签开头的XSS注入
let str = "<x><x-1>he<x-2 checked></x-2>wwww</x-1></a>";
let html = xss(str, {
onIgnoreTag: function(tag, html, options) {
if (tag.substr(0, 2) === 'x-') {
// 不对其属性列表进行过滤
return html;
}
}
});
- 过滤所有非白名单标签的HTML,script标签比较特殊,需要过滤标签中间的内容
let str = "<strong>hello</strong><script>alert(/xss/);</script>world";
let html = xss(str, {
whiteList: [], // 白名单为空,表示过滤所有标签
stripIgnoreTag: true, // 过滤所有非白名单标签的HTML
stripIgnoreTagBody: ['script'] // script标签较特殊,需要过滤标签中间的内容
});
- 过滤img标签
let str = '<img src="img1">a<img src="img2">b';
let html = xss(str, {
onTagAttr: function(tag, name, value, isWhiteAttr) {
if (tag === 'img' && name === 'src') {
// 使用内置的friendlyAttrValue函数对属性值进行转义,可将<这类的实体标记转换成打印字符<
list.push(xss.friendlyAttrValue(value));
}
// 不返回任何值,表示还是按照默认的方法处理
}
})
组件库引用:tnpm install --save mysql 或npm install --save mysql
- 防注入方法一:使用connection.query的查询参数占位符符
// `INSERT INTO user(id, name, age) VALUES(0, ?, ?)`
const mysql = require("mysql");
let sql = {
insert: 'INSERT INTO user(id, name, age) VALUES(0, ?, ?)'
};
let db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '123456',
database: 'test'
});
db.getConnection(function(err, connection) {
connection.query(sql.insert, [name, age], function(err, result) {
// ****
});
});
- 使用mysql.escape()对传入参数进行编码:
const mysql = require("mysql");
let db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '123456',
database: 'test'
});
let userId = 1;
let name = "test";
db.getConnection(function(err, connection) {
connection.query("select * from users where id = " + connection.escape(userId) + ", name = " + connection.escape(name), function(err, result) {
// ****
});
});
-
使用escapeId()编码SQL查询标识符,最常用于排序。
-
使用mysql.format()转义参数:准备查询,该函数会选择合适的转义方法转义参数。
组件库引用:tnpm install --save csurf 或npm install --save csurf
- 通过后台模板直出token;
- 页面提交请求后,带回token,后台做校验;
- 这里记得组件是不对cookie做检查的,开发者需要在接口中对cookie做校验,识别用户身份
- 注意事项:服务必须是单进程,否则多进程的时候token存在下发不一致
let parseForm = bodyParser.urlencoded({ extended: false });
let csrf = require('csurf');
let csrfProtection = csrf({ cookie: true });
// 以express框架为例
app.use("/csrf", parseForm, csrfProtection, function(req, res) {
res.json({ csrfToken: req.csrfToken() })
});
伪随机算法可以公开
- 客户端和前端都采用一种伪随机的方式,进行cookie转token;
- csrf的前提是没有xss,http劫持。如果有了xss和http劫持,那么csrf就没有意义; 3、在cookie安全的情况下,无法获取到cookie,也就是无法知道g_tk的值,因此就可以作为csrf防护。
// 以某个用户身份作为示例
let gTkDemo = function(req, res) {
let cookie = req.cookie['user'];
let hash = 9999;
for (let i = 0; i < cookie.length; i++) {
hash += (hash << 5) + cookie.charAt(i).charCodeAt();
}
hash = hash & 0x7fffffff;
res.render('g_tk', { g_tk: hash });
};
// 存在g_tk
let gTkHave = function(req, res) {
let cookie = req.cookie['user'];
let hash = 9999;
for (let i = 0; i < cookie.length; i++) {
hash += (hash << 5) + cookie.charAt(i).charCodeAt();
}
hash = hash & 0x7fffffff;
let user_g_tk = req.query['g_tk'] || "";
if (user_g_tk == hash) {
res.json({
data: {},
status: {
code: 200,
msg: 'success'
}
});
}
};
/**
* 不存在g_tk的情况,认为有问题,不是该用户过滤请求的数据
*/
let noGTkHave = function(req, res) {
let cookie = req.cookie['user'];
let hash = 9999;
for (let i = 0; i < cookie.length; i++) {
hash += (hash << 5) + cookie.charAt(i).charCodeAt();
}
hash = hash & 0x7fffffff;
let user_g_tk = req.query['g_tk'] || "";
if (user_g_tk == hash) {
res.json({
data: {},
status: {
code: 200,
msg: 'success'
}
});
}
};