Posts List

富文本场景下的 XSS

富文本编辑器是一个常见的业务场景,一般来说,通过富文本编辑器编辑的内容最终也会 html 的形式来进行渲染,比如 VUE,一般就会使用 v-html 来承载富文本编辑的内容。因为文本内容需要通过 html 来进行渲染,那么显然普通的编码转义不适合这种场景了,因为这样最终的呈现的效果就不是我们想要的了。针对于这种场景,显然过滤是唯一的解决方案了,不过过滤其实可以在后端和前端都是可以做的,后端做的话,一般是在数据存储在数据库之前。前端做的话,则是在数据最终在页面渲染之前做过滤。 前端的过滤方案,可以尝试使用开源的 [js-xss](https://github.com/leizongmin/js-xss)。先介绍一下这个库的使用方法,这个库可以在 nodejs 中使用,同样也可以在浏览中直接引入使用。 // nodejs 中使用 var xss = require("xss"); var html = xss('<script>alert("xss");</script>'); console.log(html); // 浏览器中使用 <script src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js"></script> <script> // apply function filterXSS in the same way var html = filterXSS('<script>alert("xss");</scr' + "ipt>"); alert(html); </script> 一般在 vue 的项目中,通过 webpack 也可以直接通过 CommonJS 的方式引入,与 nodejs 的引入方式基本一致。值得注意的一个问题是,默认情况下会去禁用 style 属性,这样会导致富文本的样式展示异常,需要禁用 css 过滤或者使用白名单的方式来进行过滤。 const xssFilter = new xss.FilterXSS({ css: false }); html = xssFilter.process('<script>alert("xss");</script>'); const xssFilter = new xss.FilterXSS({ css: { whiteList: { position: /^fixed|relative$/, top: true, left: true, }, }, }); html = xssFilter.process('<script>alert("xss");</script>'); 其实 js-xss 的原理并不是很复杂,如果去扒一下源码,原理其实主要就是实现标签和属性的白名单过滤,这样的方案简单有效。因为默认配置了大部分标签以及属性的白名单方案,所以一般可以做到开箱即用,当然如果有定制化的需求需要进一步定制化函数。

通过七牛云建立私有图床

七牛云是国内一家领先的云存储公司,可以利用七牛云存储对象存储图片。虽然现在各种图床,但还是希望能够搭建一个私有的图床。所以一直有希望使用七牛云搭建图床的想法,之前一直没有好好地看懂七牛云的 SDK,后来在仔细地看了一遍之后,终于知道如何利用官方的 SDK 来实现图片上传。过年在家花了一点时间,后来陆续也写了一点,完成了这个七牛云图床 chrome 拓展。 注册账户 首先你可以通过这个链接注册你的七牛云账户。在成功注册账户之后,可能还需要绑定手机号,你就可以创建存储空间,可以理解成为文件存储的文件夹。 创建好存储空间(bucket)就已经完成了私有图库的第一步。 开发 在这也会对所有代码一一解释,主要是讲解一下在开发中遇到的一些问题。首先基于七牛云存储开发,有必要学会七牛云存储 API 的使用。可以在官方 SDK 文档获取所有文档。本拓展的开发主要是基于 js 来进行开发,因此我们只需要了解 js SDK 文档。 文档中提到了一点:JS-SDK 依赖服务端颁发 token,可以通过以下二种方式实现: 利用七牛服务端 SDK 构建后端服务 利用七牛底层 API 构建服务,详见七牛上传策略和上传凭证(https://developer.qiniu.com/kodo/manual/1208/upload-token) 第一个方法还需要搭建服务器来颁发 token,显然这种方法不太经济,如果仅仅是为了这个图床搭建一个后端服务,就不太划算了。因此,我选择第二种,在客户端来生成 token。这种方法就需要你了解上传策略以及上传凭证。 上传策略是资源上传时附带的一组配置设定。通过这组配置信息,七牛云存储可以了解用户上传的需求:它将上传什么资源,上传到哪个空间,上传结果是回调通知还是使用重定向跳转,是否需要设置反馈信息的内容,以及授权上传的截止时间等等。上传策略主要是 scope 和 dealine 这两个字段是必须要的。scope 是指定上传的目标资源空间 Bucket 和资源键 Key,这里我们只需要设置 bucket。deadline 是上传凭证有效截止时间。Unix时间戳,单位为秒。该截止时间为上传完成后,在七牛空间生成文件的校验时间,而非上传的开始时间,官方建议建议设置为上传开始时间 + 3600s。 function genPolicy(scope) { let policy = { scope: scope, deadline: (new Date()).getTime() + 3600 } return policy; } 按照上述算法流程构建客户端的上传 token,官方有提供上传凭证的在线示例,通过整理形成了自己的 token.js。 完成本地 token 的,我的开发就完成了一大步。这个拓展需要两个界面的设置,就是包括图片上传页面以及七牛云存储配置页面。关于这两个页面的设计,我也就不一一赘述,主要讲几点: 七牛云信息存储 为了上传图片,我们需要配置 AK,SK,bucket 以及 domain。这些都可以在七牛云的控制台里面获取,因此我们需要在首次打开拓展的时候检查是否具有这些数据。至于这些配置信息保存在什么地方,一开始打算采取的是 chrome.storage 来进行存储,这样做的好处是可以利用账号同步数据,但缺点是操作不方便,是异步的。后来还是决定使用 localStorage,一来使用起来非常方便,二是我们并不是特别需要账号同步。因此,我们首先会检查配置信息是否存储在 localStorage,否则就打开配置页:

Mongoose中document和object的区别

这个问题其实是mongoose非常常见的问题,经常有很多以前没遇到这个问题的人都会被这个问题弄得怀疑人生。我们先介绍一些问题的背景。先看下面一段代码: router.get('/', function(req, res, next) { // res.render('index', { title: 'Express' }); const model = mongoose.model('realestate'); const queryCretia = {}; model.find(queryCretia, (err, docs) => { res.render('index', { title: 'express', docs: docs }) }) }); <!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> <!-- <%= docs %> --> <ul> <% docs.forEach(function(doc){ %> <li><%= doc.type %></li> <% }) %> </ul> </body> </html> 在第一段代码中,通过model.find我们应该能够获取到根据queryCriteria获取的结果,结果应该是一个对象数组,类似于这样: [{ "_id" : ObjectId("59bdeadb2a5c612514ee7970"), "title" : "好楼层,中等装修,满5年,上门实拍", "type" : "2室1厅", "square" : "75.42平", "direction" : "朝南", "floor" : "中区/6层", "unitPrice" : 47732, "totalPrice" : 360, "location" : null, "specialExplain" : "满五", "url" : "http://sh.lianjia.com//ershoufang/sh4528035.html", "station" : "江杨北路", "line" : "3号线", "updateTime" : "2017-09-17 11:24:11" } { "_id" : ObjectId("59bdeadb2a5c612514ee7971"), "title" : "南北户型,厨卫全明,高区采光好,装修精美", "type" : "2室2厅", "square" : "90.92平", "direction" : "朝南北", "floor" : "高区/6层", "unitPrice" : 46194, "totalPrice" : 420, "location" : null, "specialExplain" : "满五", "url" : "http://sh.lianjia.com//ershoufang/sh4546221.html", "station" : "江杨北路", "line" : "3号线", "updateTime" : "2017-09-17 11:24:11" }] 预期index.ejs应该渲染的页面是一个ul渲染的结果,类似于