[{"data":1,"prerenderedAt":234},["ShallowReactive",2],{"tag-过滤-1":3},{"title":4,"articles":5,"hot_article":19,"month_arch":82,"total":230,"cover":231},"过滤 - 独孤影的博客",[6],{"id":7,"title":8,"uri":8,"author":9,"tags":10,"created_at":16,"view_count":17,"content":18},26,"xss攻击最终应对方案","duguying",[11,12,13,14,15],"markdown","epiceditor","xss","后端","过滤","2014-12-16 14:54:42",3802,"\u003Cp>今天终于解决了xss这个潜在的问题，编辑器使用的是Markdown编辑器EpicEditor，\u003Cspan style=\"line-height: 23px;\">EpicEditor是一款Markdown编辑器，或者说是一款可以自己定制的编辑器。后台Markdown解析器用的是&quot;github.com\u002Frussross\u002Fblackfriday&quot;，后台代码\u003C\u002Fspan>\u003C\u002Fp>\u003Cpre class=\"brush:go;toolbar:false\">func&nbsp;Markdown2HTML(content&nbsp;string)&nbsp;string&nbsp;{\n\tcontent&nbsp;=&nbsp;html.EscapeString(content)\n\toutput&nbsp;:=&nbsp;blackfriday.MarkdownCommon([]byte(content))\n\treturn&nbsp;string(output)\n}\u003C\u002Fpre>\u003Cp>\u003Cspan style=\"line-height: 23px;\">\u003C\u002Fspan>直接将markdown语法的串解析为html语法的串，后台还设置了一个markdown解析api后面待用，api的controller代码如下\u003C\u002Fp>\u003Cpre class=\"brush:go;toolbar:false\">\u002F\u002F&nbsp;parse&nbsp;markdown\ntype&nbsp;MarkdownController&nbsp;struct&nbsp;{\n\tcontrollers.BaseController\n}\n\nfunc&nbsp;(this&nbsp;*MarkdownController)&nbsp;Get()&nbsp;{\n\tthis.Data[&quot;json&quot;]&nbsp;=&nbsp;map[string]interface{}{\n\t\t&quot;result&quot;:&nbsp;false,\n\t\t&quot;msg&quot;:&nbsp;&nbsp;&nbsp;&nbsp;&quot;only&nbsp;post&nbsp;method&nbsp;support&quot;,\n\t\t&quot;refer&quot;:&nbsp;&nbsp;nil,\n\t}\n\n\tthis.ServeJson()\n}\n\nfunc&nbsp;(this&nbsp;*MarkdownController)&nbsp;Post()&nbsp;{\n\tcontent&nbsp;:=&nbsp;this.GetString(&quot;content&quot;)\n\n\tlog.Blueln(content)\n\trst&nbsp;:=&nbsp;utils.Markdown2HTML(content)\n\n\tthis.Data[&quot;json&quot;]&nbsp;=&nbsp;map[string]interface{}{\n\t\t&quot;result&quot;:&nbsp;&nbsp;true,\n\t\t&quot;msg&quot;:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;success&quot;,\n\t\t&quot;preview&quot;:&nbsp;rst,\n\t\t&quot;refer&quot;:&nbsp;&nbsp;&nbsp;nil,\n\t}\n\n\tthis.ServeJson()\n}\u003C\u002Fpre>\u003Cp>前端将串传给后端，这个串期望是纯markdown的，因此如果其中夹杂着有html语法，html将会被转义，之后返回带有解析后的html串。\u003C\u002Fp>\u003Cp>前端的EpicEditor默认是能够解析Markdown和兼容HTML的，甚至不过滤js(\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FOscarGodson\u002FEpicEditor\u002Fissues\u002F351\" target=\"_blank\" title=\"https:\u002F\u002Fgithub.com\u002FOscarGodson\u002FEpicEditor\u002Fissues\u002F351\">这个问题\u003C\u002Fa>我已经向项目作者反馈过，他决定之后会给其添加选项以禁用js)，由于我\u003Ca href=\"http:\u002F\u002Fduguying.net\u002Farticle\u002FXSS%E6%94%BB%E5%87%BB%E5%BA%94%E5%AF%B9%E6%96%B9%E6%A1%88\" target=\"_blank\" title=\"http:\u002F\u002Fduguying.net\u002Farticle\u002FXSS%E6%94%BB%E5%87%BB%E5%BA%94%E5%AF%B9%E6%96%B9%E6%A1%88\">之前的文章\u003C\u002Fa>提到css都会给用户机会造成页面混乱，而\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fmicrocosm-cc\u002Fbluemonday\" target=\"_blank\" title=\"https:\u002F\u002Fgithub.com\u002Fmicrocosm-cc\u002Fbluemonday\">我所找到的过滤器\u003C\u002Fa>只支持xss过滤，即只过滤js不过滤css，并且那个过滤器有一个依赖包在墙外，这将会是很蛋疼，所以决定使用markdown，是纯markdown。EpicEditor是兼容html的，因此不满足我的需求，于是我问EpicEditor的作者是否有禁用html的选项（\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FOscarGodson\u002FEpicEditor\u002Fissues\u002F352\" target=\"_blank\" title=\"https:\u002F\u002Fgithub.com\u002FOscarGodson\u002FEpicEditor\u002Fissues\u002F352\">传送\u003C\u002Fa>），他告诉我EpicEditor不负责解析的，解析器可以自己定制，EpicEditor默认使用的解析器是\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fchjj\u002Fmarked\" target=\"_blank\" title=\"https:\u002F\u002Fgithub.com\u002Fchjj\u002Fmarked\">markd\u003C\u002Fa>解析器，然后给了我一个\u003Ca href=\"https:\u002F\u002Fgithub.com\u002FOscarGodson\u002FEpicEditor#custom-parsers\" target=\"_blank\" title=\"https:\u002F\u002Fgithub.com\u002FOscarGodson\u002FEpicEditor#custom-parsers\">页面\u003C\u002Fa>，其上有定制解析器的demo\u003C\u002Fp>\u003Cpre class=\"brush:js;toolbar:false\">var&nbsp;editor&nbsp;=&nbsp;new&nbsp;EpicEditor({\n&nbsp;&nbsp;parser:&nbsp;function&nbsp;(str)&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;var&nbsp;blacklist&nbsp;=&nbsp;[&#39;foo&#39;,&nbsp;&#39;bar&#39;,&nbsp;&#39;baz&#39;];\n&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;str.split(&#39;&nbsp;&#39;).map(function&nbsp;(word)&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\u002F\u002F&nbsp;If&nbsp;the&nbsp;word&nbsp;exists,&nbsp;replace&nbsp;with&nbsp;asterisks\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(blacklist.indexOf(word)&nbsp;&gt;&nbsp;-1)&nbsp;{\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;&#39;****&#39;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;word;\n&nbsp;&nbsp;&nbsp;&nbsp;}).join(&#39;&nbsp;&#39;);\n&nbsp;&nbsp;}\n}).load();\u003C\u002Fpre>\u003Cp>自己重做一个解析器？看一下markd项目的代码有多少吧，重做工作量太大。仔细分析上面的代码发现parser是一个函数，它的作用是这样的，传入原串，返回解析后的串。这回我之前留的api起作用了，自己实现的parser函数如下\u003C\u002Fp>\u003Cpre class=\"brush:js;toolbar:false\">parser:&nbsp;function&nbsp;(str)&nbsp;{\n\tvar&nbsp;rst&nbsp;=&nbsp;$.ajax({\n\t\tasync:&nbsp;false,\n\t\ttype:&nbsp;&quot;post&quot;,\n\t\turl:&nbsp;&quot;\u002Fapi\u002Fmarkdown\u002Fpreview&quot;,\n\t\tdata:&nbsp;{&quot;content&quot;:&nbsp;str},\n\t\tdataType:&quot;json&quot;,\n\n\t}).responseText;\n\tvar&nbsp;json&nbsp;=&nbsp;eval(&#39;(&#39;+rst+&#39;)&#39;)\n\tconsole.log(json);\n\treturn&nbsp;json.preview;\n},\u003C\u002Fpre>\u003Cp>是的，我将原数据传给服务器去解析，得到结果，这样还有一个好处就是保证了解析器的一致性，即最终结果与编辑器preview结果是一致的。当然这也是有一些缺点的，那就是给服务器增加了负担，不过我觉得这是值得的。最后附上编辑器工作的截图\u003Cbr\u002F>\u003C\u002Fp>\u003Cp>\u003Cimg src=\"\u002F\u002Fduguying.oss-cn-qingdao.aliyuncs.com\u002F2014\u002F12\u002F16\u002Fepiceditor_back_end_anti_xss.png\" title=\"\" alt=\"epiceditor_back_end_anti_xss.png\"\u002F>\u003C\u002Fp>\u003Cp>问题能够得以解决应当感谢各个开源项目以及其作者的无私奉献，还有他们的热心答疑，取之于开源用之于开源，Goj也会是一个开源项目，愿早日完成。\u003C\u002Fp>",{"title":20,"list":21},"最热文章",[22,28,34,40,46,52,58,64,70,76],{"id":23,"title":24,"uri":25,"author":9,"created_at":26,"view_count":27},28,"docker容器目录与主机目录同步","\u002Farticle\u002Fdocker容器目录与主机目录同步","2014-12-31 12:34:08",15570,{"id":29,"title":30,"uri":31,"author":9,"created_at":32,"view_count":33},21,"beego中使用fis","\u002Farticle\u002Fbeego中使用fis","2014-12-03 10:47:56",9250,{"id":35,"title":36,"uri":37,"author":9,"created_at":38,"view_count":39},20,"用github登录，oauth开发","\u002Farticle\u002F用github登录，oauth开发","2014-11-23 13:33:37",9113,{"id":41,"title":42,"uri":43,"author":9,"created_at":44,"view_count":45},18,"windows服务注册工具nssm","\u002Farticle\u002Fwindows服务注册工具nssm","2014-10-31 21:34:39",9047,{"id":47,"title":48,"uri":49,"author":9,"created_at":50,"view_count":51},49,"用latex排版中文书籍","\u002Farticle\u002F用latex排版中文书籍","2016-10-31 11:12:02",8634,{"id":53,"title":54,"uri":55,"author":9,"created_at":56,"view_count":57},19,"texlive中文书签乱码","\u002Farticle\u002Ftexlive中文书签乱码","2014-11-15 02:54:01",8461,{"id":59,"title":60,"uri":61,"author":9,"created_at":62,"view_count":63},6,"Windows上的软件包管理器chocolatey","\u002Farticle\u002FWindows上的软件包管理器chocolatey","2014-08-10 15:55:22",8009,{"id":65,"title":66,"uri":67,"author":9,"created_at":68,"view_count":69},3,"利用开源和免费资源搭建CI环境","\u002Farticle\u002F利用开源和免费资源搭建CI环境","2014-07-04 16:54:58",7732,{"id":71,"title":72,"uri":73,"author":9,"created_at":74,"view_count":75},16,"php-fpm网站目录权限配置","\u002Farticle\u002Fphp-fpm网站目录权限配置","2014-10-18 01:36:10",7274,{"id":77,"title":78,"uri":79,"author":9,"created_at":80,"view_count":81},27,"sublime text 2插件开发，外部修改文件无法重新加载","\u002Farticle\u002Fsublime text 2插件开发，外部修改文件无法重新加载","2014-12-26 20:24:13",7054,{"title":83,"list":84},"按月归档",[85,89,92,95,98,101,104,108,112,115,118,121,125,128,131,135,138,141,144,148,151,154,157,160,163,166,169,172,175,178,181,184,187,190,193,196,199,202,205,208,212,215,218,221,224,227],{"title":86,"uri":87,"view_count":88},"2026年7月","\u002Farchive\u002F2026\u002F7\u002F1","[1]",{"title":90,"uri":91,"view_count":88},"2026年1月","\u002Farchive\u002F2026\u002F1\u002F1",{"title":93,"uri":94,"view_count":88},"2025年4月","\u002Farchive\u002F2025\u002F4\u002F1",{"title":96,"uri":97,"view_count":88},"2024年6月","\u002Farchive\u002F2024\u002F6\u002F1",{"title":99,"uri":100,"view_count":88},"2024年5月","\u002Farchive\u002F2024\u002F5\u002F1",{"title":102,"uri":103,"view_count":88},"2024年4月","\u002Farchive\u002F2024\u002F4\u002F1",{"title":105,"uri":106,"view_count":107},"2023年12月","\u002Farchive\u002F2023\u002F12\u002F1","[6]",{"title":109,"uri":110,"view_count":111},"2023年11月","\u002Farchive\u002F2023\u002F11\u002F1","[2]",{"title":113,"uri":114,"view_count":88},"2023年10月","\u002Farchive\u002F2023\u002F10\u002F1",{"title":116,"uri":117,"view_count":88},"2023年7月","\u002Farchive\u002F2023\u002F7\u002F1",{"title":119,"uri":120,"view_count":111},"2023年6月","\u002Farchive\u002F2023\u002F6\u002F1",{"title":122,"uri":123,"view_count":124},"2023年5月","\u002Farchive\u002F2023\u002F5\u002F1","[3]",{"title":126,"uri":127,"view_count":111},"2023年4月","\u002Farchive\u002F2023\u002F4\u002F1",{"title":129,"uri":130,"view_count":111},"2023年3月","\u002Farchive\u002F2023\u002F3\u002F1",{"title":132,"uri":133,"view_count":134},"2023年2月","\u002Farchive\u002F2023\u002F2\u002F1","[5]",{"title":136,"uri":137,"view_count":88},"2023年1月","\u002Farchive\u002F2023\u002F1\u002F1",{"title":139,"uri":140,"view_count":111},"2022年12月","\u002Farchive\u002F2022\u002F12\u002F1",{"title":142,"uri":143,"view_count":134},"2022年11月","\u002Farchive\u002F2022\u002F11\u002F1",{"title":145,"uri":146,"view_count":147},"2022年10月","\u002Farchive\u002F2022\u002F10\u002F1","[4]",{"title":149,"uri":150,"view_count":107},"2022年9月","\u002Farchive\u002F2022\u002F9\u002F1",{"title":152,"uri":153,"view_count":134},"2022年8月","\u002Farchive\u002F2022\u002F8\u002F1",{"title":155,"uri":156,"view_count":88},"2019年7月","\u002Farchive\u002F2019\u002F7\u002F1",{"title":158,"uri":159,"view_count":88},"2019年2月","\u002Farchive\u002F2019\u002F2\u002F1",{"title":161,"uri":162,"view_count":88},"2018年9月","\u002Farchive\u002F2018\u002F9\u002F1",{"title":164,"uri":165,"view_count":88},"2018年8月","\u002Farchive\u002F2018\u002F8\u002F1",{"title":167,"uri":168,"view_count":88},"2017年11月","\u002Farchive\u002F2017\u002F11\u002F1",{"title":170,"uri":171,"view_count":88},"2017年8月","\u002Farchive\u002F2017\u002F8\u002F1",{"title":173,"uri":174,"view_count":88},"2017年5月","\u002Farchive\u002F2017\u002F5\u002F1",{"title":176,"uri":177,"view_count":88},"2017年4月","\u002Farchive\u002F2017\u002F4\u002F1",{"title":179,"uri":180,"view_count":88},"2016年11月","\u002Farchive\u002F2016\u002F11\u002F1",{"title":182,"uri":183,"view_count":88},"2016年10月","\u002Farchive\u002F2016\u002F10\u002F1",{"title":185,"uri":186,"view_count":111},"2016年9月","\u002Farchive\u002F2016\u002F9\u002F1",{"title":188,"uri":189,"view_count":88},"2016年8月","\u002Farchive\u002F2016\u002F8\u002F1",{"title":191,"uri":192,"view_count":111},"2016年7月","\u002Farchive\u002F2016\u002F7\u002F1",{"title":194,"uri":195,"view_count":134},"2016年6月","\u002Farchive\u002F2016\u002F6\u002F1",{"title":197,"uri":198,"view_count":88},"2015年12月","\u002Farchive\u002F2015\u002F12\u002F1",{"title":200,"uri":201,"view_count":88},"2015年10月","\u002Farchive\u002F2015\u002F10\u002F1",{"title":203,"uri":204,"view_count":111},"2015年2月","\u002Farchive\u002F2015\u002F2\u002F1",{"title":206,"uri":207,"view_count":88},"2015年1月","\u002Farchive\u002F2015\u002F1\u002F1",{"title":209,"uri":210,"view_count":211},"2014年12月","\u002Farchive\u002F2014\u002F12\u002F1","[8]",{"title":213,"uri":214,"view_count":111},"2014年11月","\u002Farchive\u002F2014\u002F11\u002F1",{"title":216,"uri":217,"view_count":134},"2014年10月","\u002Farchive\u002F2014\u002F10\u002F1",{"title":219,"uri":220,"view_count":124},"2014年9月","\u002Farchive\u002F2014\u002F9\u002F1",{"title":222,"uri":223,"view_count":124},"2014年8月","\u002Farchive\u002F2014\u002F8\u002F1",{"title":225,"uri":226,"view_count":124},"2014年7月","\u002Farchive\u002F2014\u002F7\u002F1",{"title":228,"uri":229,"view_count":111},"2013年9月","\u002Farchive\u002F2013\u002F9\u002F1",1,{"url":232,"title":233},"https:\u002F\u002Fimage.duguying.net\u002F2022\u002F09\u002Fij6x8o4s.webp","老家 摄于2020年秋",1790062269123]