编写hexo-comment
逛了一圈hexo的评论插件,之前被wordpress的机器人的恶意评论搞怕了,必须要有审核功能,但公开的评论插件都无法满足审核的需求。
安全工程师的开发水平还是要跟上的,作为一个半吊子全栈工程师,花三天时间自己写了一套评论系统,本文介绍一下整个流程。
整体设计
需求分析
- 继承 wordpress 的评论。读取原先存放在 mysql里的数据,然后想办法复原。
- 后端开发。支持增、查、审核,需要存数据库,字段和类型自定义。
- 前端开发。在原先 hexo 的静态页面上,支持查看评论和新增评论。
- 部署。原先服务是普通的 apache2.4,hexo 是直接以静态文件的方式部署到
/var/www/html
。
选型
- 前端。因为是在 hexo 上进行开发,无法直接选型,hexo 前端甚至都没有引入 jquery,因此使用最原始的 javascript 进行 ajax 操作和 dom 操作即可,抄一下 CSS。
- 后端。hexo 纯静态,因此新加的后端可以自由发挥。由于上次 wordpress 被打挂之后的迁移和扩容非常麻烦,因此不愿意使用mysql。由于我完全不懂数据库,更愿意使用高级的接口,选择 django,仅占用本地单个 sqlite 文件,迁移非常顺滑。
- 部署。了解到 apache2.4 支持 python 拓展(就像支持 php 拓展一样),对于 URL,不同的路径下可以配不同的解析服务,可以保证原先静态的主站不变,新的路径指派给评论后端来处理。
- 交互协议,使用 RESTful 风格的 API,传 json 前后端写着都方便。
后端开发
代码位于:https://github.com/LeadroyaL/blog_comment
使用 python3写个 django 项目,没有环境限制,其实还挺好写的,随便提一下设计吧。
- 避免参数解析的繁琐,在 path 中指定参数和类型,更加 RESTful。
1
2
3
4
5
6
7urlpatterns = [
path('get/<int:post_id>', views.get),
path('put/<int:post_id>', views.put),
path('admin', views.admin),
path('admin/accept/<int:comment_id>', views.accept),
path('admin/reject/<int:comment_id>', views.reject),
] - django 自带的 auth 功能过度复杂,使用 session 存放登录态,反正审核功能也只有我一个人会用到。
- 考虑到外界输入不可信,审核界面可能会 XSS,经过调研,django 的模板输出时会自动转义,不会被 XSS。
- 使用 django 的 models,就可以不防 SQL 注入了,功能简单也不大会有其他漏洞。
唯一坑点就是 python3.8 和 python3.5语法变化,本机自动生成的代码在服务器上编译错误,而服务器的 python3 我也懒得升级,调整一下语法即可。
后端部署
由于对 apache2 的配置完全不懂,django官网以及其他网上资料写的不准确,每个人讨论的环境都不一样,导致这里卡了非常久。
思路:安装 libapache2-mod-wsgi-py3,修改 sites-available 里的配置,配置好文件权限。
参考链接:django官网
第一步,安装
sudo apt install libapache2-mod-wsgi-py3
第二步,放好文件
这一步坑非常大,主要受制于 linux 的权限管控,error.log 信息又很模糊,只有一个 403。
apache2进程是 www-data:www-data 运行的,如果把 django 项目放到主用户的 HOME 目录下,老是加载不起来。试了很多种方法都不大行,包括修改 apache conf 里的 Directory Files Require all granted,包括 chmod、chown,都不大行。
最后我选择放到 /var/www/blog_comment
下,然后 chown 给 www-data:www-data 就行了。
之后 python3 manage.py migrate
等操作也要用 www-data 用户来操作,不然将来没权限去写sqlite3.db。
第三步,修改 apache2
原先的配置:(SSL)
1 | <VirtualHost *:443> |
将 /comment
路径下的内容全部转发到 wsgi,并且配置 python-path。
1 | <VirtualHost *:443> |
那三句话大概的意思是(不保证描述正确)
- 使用 deamon 方式启动,可能进程名字叫 example.com,配置 python-path
- 可能进程组叫 example.com
- 添加一条路由,将 /comment 开头的全都转发给 wsgi,只转发后半段,不包括 /comment 本身
如果不配置 python-path 的话,会提示 blog_comment 这个 module 找不到。非要的话,就要在 py 里硬编码 python path,不优雅。
这里还遇到过问题,提示 django 找不到,因为之前 django 被安装到了 home 目录下,www-data 又被权限管理给挡了,要装到 /usr/local 这种公共的地方。
总之,运行不来就看 error.log。
效果:访问 /comment/get/{post_id}
可以通过访问到 django 拿到该文章的评论,访问除此之外的目录,会访问到主站的 html 等静态资源。
前端开发
代码位于:https://github.com/LeadroyaL/blog_comment_frontend
前端开发是最玄学的地方,之前也就改 config,写 markdown 页面,一行代码都没写过。另外,因为 hexo 的目的是批量生成 html,显然是用模板的,如何在框架上二次开发,是需要一定学习和模仿能力的。
我使用的 theme-next,因此代码全部位于 /path/theme/next 下,也只需要对它进行修改,侵入性尽可能要小。
先对页面F12,观察文章页(即 post)的独有元素,寻找合理的插入位置,发现只有页面的底部有 “上一篇” 和 “下一篇” 的元素,叫 "post-nav",页面底部叫 "post-footer"。
找到它们的实现,在 layout/_macro/post.njk
,附近有 post-footer.njk
。
经过观察,njk 应该是一种模板文件,可以 include 其他模板,可以访问hexo提供的与文章有关的变量和函数,最终变成 html。
此时还缺少 css 和 js,很容易搜到 source/css/_common/components/post/index.styl
,应该是 css 的模板文件。而运行时仅有一个 main.css,应该是由所有的 styl
拼接成的一个大 css。
而 js 的话,参考 {{- next_js('config.js') }}
,会生成 <script src="/js/config.js"></script>
, 位于 source/js
下面,抄一下就可以自己写 js 了。
综上,共需要作出 3 处修改:
layout/_macro/post.njk
中,引入评论区的 layout,1
{{ partial('_partials/post/post-comments.njk', {}, {cache: theme.cache.enable}) }}
source/css/_common/components/post/index.styl
中,引入评论区的 css1
@import 'post-comment';
编写对应的 njk 和 styl,编写并按照规范引入 js
而 js 本身的逻辑,就是访问后端 API,拿到数据后渲染在 html 里。
加载评论的时机,选择了 IntersectionObserver
这个类,只有评论区被显示时才会触发加载请求,从而减少无意义的请求。
作为安全工程师,看到这个情景就要防XSS,我不愿意改后端代码,只能在前端上做文章。但原生 js 里自带的 escape 的功能会把日期中的冒号转义成百分号格式的,让我很恼火。最后选择是直接修改 textContent 来避免 XSS,参考 mozilla 的API文档 。
1 | function comment_to_ele(author, time, content) { |
联调测试
本地测试的话还会遇到 mock server 的跨域问题,在 server 端配好就行,不然浏览器会丢弃来自 server 的返回数据。
还遇到了 styl 文件没有被整合到 css 里,乱试半天,模仿得惟妙惟肖才成功。