egg.js 中helper方法下的shtml 会删除无域名的src链接处理解决方案
开发网页或多或少会碰到要保存富文本内容的情况.但凡碰到需要保存富文本,服务端就必须开启xss防护.否则是非常不安全的.
egg.js默认集成了XSS框架,附上框架链接:
https://jsxss.com/zh/index.html
使用方法是 const safehtml = this.ctx.helper.shtml(html)
返回的html就是过滤后的html内容,可以安全保存在数据库中了.
里面有各种对不同的tag,不同属性的配置,这里就不举例了,大部分情况下默认配置就行,个别情况要改的话,根据默认配置增减就行.这里附上源码中的默认配置,以便查阅
https://github.com/leizongmin/js-xss/blob/master/lib/default.js
这里要说的主要内容是在egg.js中调用shtml,如果src或者href中的内容是'/'开头的没有域名,他是直接剔除掉的,附上源码:
if (isWhiteAttr && (name === 'href' || name === 'src')) {
if (!value) {
return;
}
value = String(value);
if (value[0] === '/' || value[0] === '#') {
return;
}
const hostname = utils.getFromUrl(value, 'hostname');
if (!hostname) {
return;
}
// If we don't have our hostname in the app.security.domainWhiteList,
// Just check for `shtmlConfig.domainWhiteList` and `ctx.whiteList`.
if (!isSafeDomain(hostname, domainWhiteList)) {
// Check for `shtmlConfig.domainWhiteList` first (duplicated now)
if (shtmlConfig.domainWhiteList && shtmlConfig.domainWhiteList.length !== 0) {
app.deprecate('[egg-security] `config.helper.shtml.domainWhiteList` has been deprecate. Please use `config.security.domainWhiteList` instead.');
shtmlConfig.domainWhiteList = shtmlConfig.domainWhiteList.map(domain => domain.toLowerCase());
if (!isSafeDomain(hostname, shtmlConfig.domainWhiteList)) {
return '';
}
} else {
return '';
}
}
}
源码可以看到 if(value[0] === '/' || value[0] === '#'){ return }
但是一些情况下,在开发环境中,我们就是不想加域名,而是通过代理去实现图片访问,这个时候图片下的src属性值就是'/'开头.
通过源码可以看到,他合并了egg.js中的config,然后在最后判断了config中是否有onTagAttr方法,用来重写他的onTagAttr方法
...
const shtmlConfig = utils.merge(this.app.config.helper.shtml, securityOptions.shtml);
...
// avoid overriding user configuration 'onTagAttr'
if (shtmlConfig.onTagAttr) {
const original = shtmlConfig.onTagAttr;
shtmlConfig.onTagAttr = function() {
const result = original.apply(this, arguments);
if (result !== undefined) {
return result;
}
return shtmlConfig[BUILD_IN_ON_TAG_ATTR].apply(this, arguments);
};
} else {
shtmlConfig.onTagAttr = shtmlConfig[BUILD_IN_ON_TAG_ATTR];
}
我这里在config.default.js中添加了helper.shtml.onTagAttr方法,然后在config.prod.js文件中把他覆盖掉了,这样开发环境就可以使用'/'开头了.附上config.default.js中的配置代码:
const config = {
// ...
helper: {
shtml: {
onTagAttr(tag, name, value, isWhiteAttr) {
if (isWhiteAttr && (name === 'href' || name === 'src')) {
if (!value) {
return
}
value = String(value)
// 测试环境通过/做代理,不要过滤掉.
if (value[0] === '/') {
return `${name}="${value}"`
}
}
},
},
},
// ...
}