[{"data":1,"prerenderedAt":232},["ShallowReactive",2],{"article-xss攻击最终应对方案":3},{"title":4,"article":5,"hot_article":18,"month_arch":81,"cover":229},"xss攻击最终应对方案 - 独孤影的博客",{"id":6,"title":7,"uri":7,"author":8,"tags":9,"created_at":15,"view_count":16,"content":17},26,"xss攻击最终应对方案","duguying",[10,11,12,13,14],"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":19,"list":20},"最热文章",[21,27,33,39,45,51,57,63,69,75],{"id":22,"title":23,"uri":24,"author":8,"created_at":25,"view_count":26},28,"docker容器目录与主机目录同步","\u002Farticle\u002Fdocker容器目录与主机目录同步","2014-12-31 12:34:08",15570,{"id":28,"title":29,"uri":30,"author":8,"created_at":31,"view_count":32},21,"beego中使用fis","\u002Farticle\u002Fbeego中使用fis","2014-12-03 10:47:56",9250,{"id":34,"title":35,"uri":36,"author":8,"created_at":37,"view_count":38},20,"用github登录，oauth开发","\u002Farticle\u002F用github登录，oauth开发","2014-11-23 13:33:37",9113,{"id":40,"title":41,"uri":42,"author":8,"created_at":43,"view_count":44},18,"windows服务注册工具nssm","\u002Farticle\u002Fwindows服务注册工具nssm","2014-10-31 21:34:39",9047,{"id":46,"title":47,"uri":48,"author":8,"created_at":49,"view_count":50},49,"用latex排版中文书籍","\u002Farticle\u002F用latex排版中文书籍","2016-10-31 11:12:02",8634,{"id":52,"title":53,"uri":54,"author":8,"created_at":55,"view_count":56},19,"texlive中文书签乱码","\u002Farticle\u002Ftexlive中文书签乱码","2014-11-15 02:54:01",8461,{"id":58,"title":59,"uri":60,"author":8,"created_at":61,"view_count":62},6,"Windows上的软件包管理器chocolatey","\u002Farticle\u002FWindows上的软件包管理器chocolatey","2014-08-10 15:55:22",8009,{"id":64,"title":65,"uri":66,"author":8,"created_at":67,"view_count":68},3,"利用开源和免费资源搭建CI环境","\u002Farticle\u002F利用开源和免费资源搭建CI环境","2014-07-04 16:54:58",7732,{"id":70,"title":71,"uri":72,"author":8,"created_at":73,"view_count":74},16,"php-fpm网站目录权限配置","\u002Farticle\u002Fphp-fpm网站目录权限配置","2014-10-18 01:36:10",7274,{"id":76,"title":77,"uri":78,"author":8,"created_at":79,"view_count":80},27,"sublime text 2插件开发，外部修改文件无法重新加载","\u002Farticle\u002Fsublime text 2插件开发，外部修改文件无法重新加载","2014-12-26 20:24:13",7054,{"title":82,"list":83},"按月归档",[84,88,91,94,97,100,103,107,111,114,117,120,124,127,130,134,137,140,143,147,150,153,156,159,162,165,168,171,174,177,180,183,186,189,192,195,198,201,204,207,211,214,217,220,223,226],{"title":85,"uri":86,"view_count":87},"2026年7月","\u002Farchive\u002F2026\u002F7\u002F1","[1]",{"title":89,"uri":90,"view_count":87},"2026年1月","\u002Farchive\u002F2026\u002F1\u002F1",{"title":92,"uri":93,"view_count":87},"2025年4月","\u002Farchive\u002F2025\u002F4\u002F1",{"title":95,"uri":96,"view_count":87},"2024年6月","\u002Farchive\u002F2024\u002F6\u002F1",{"title":98,"uri":99,"view_count":87},"2024年5月","\u002Farchive\u002F2024\u002F5\u002F1",{"title":101,"uri":102,"view_count":87},"2024年4月","\u002Farchive\u002F2024\u002F4\u002F1",{"title":104,"uri":105,"view_count":106},"2023年12月","\u002Farchive\u002F2023\u002F12\u002F1","[6]",{"title":108,"uri":109,"view_count":110},"2023年11月","\u002Farchive\u002F2023\u002F11\u002F1","[2]",{"title":112,"uri":113,"view_count":87},"2023年10月","\u002Farchive\u002F2023\u002F10\u002F1",{"title":115,"uri":116,"view_count":87},"2023年7月","\u002Farchive\u002F2023\u002F7\u002F1",{"title":118,"uri":119,"view_count":110},"2023年6月","\u002Farchive\u002F2023\u002F6\u002F1",{"title":121,"uri":122,"view_count":123},"2023年5月","\u002Farchive\u002F2023\u002F5\u002F1","[3]",{"title":125,"uri":126,"view_count":110},"2023年4月","\u002Farchive\u002F2023\u002F4\u002F1",{"title":128,"uri":129,"view_count":110},"2023年3月","\u002Farchive\u002F2023\u002F3\u002F1",{"title":131,"uri":132,"view_count":133},"2023年2月","\u002Farchive\u002F2023\u002F2\u002F1","[5]",{"title":135,"uri":136,"view_count":87},"2023年1月","\u002Farchive\u002F2023\u002F1\u002F1",{"title":138,"uri":139,"view_count":110},"2022年12月","\u002Farchive\u002F2022\u002F12\u002F1",{"title":141,"uri":142,"view_count":133},"2022年11月","\u002Farchive\u002F2022\u002F11\u002F1",{"title":144,"uri":145,"view_count":146},"2022年10月","\u002Farchive\u002F2022\u002F10\u002F1","[4]",{"title":148,"uri":149,"view_count":106},"2022年9月","\u002Farchive\u002F2022\u002F9\u002F1",{"title":151,"uri":152,"view_count":133},"2022年8月","\u002Farchive\u002F2022\u002F8\u002F1",{"title":154,"uri":155,"view_count":87},"2019年7月","\u002Farchive\u002F2019\u002F7\u002F1",{"title":157,"uri":158,"view_count":87},"2019年2月","\u002Farchive\u002F2019\u002F2\u002F1",{"title":160,"uri":161,"view_count":87},"2018年9月","\u002Farchive\u002F2018\u002F9\u002F1",{"title":163,"uri":164,"view_count":87},"2018年8月","\u002Farchive\u002F2018\u002F8\u002F1",{"title":166,"uri":167,"view_count":87},"2017年11月","\u002Farchive\u002F2017\u002F11\u002F1",{"title":169,"uri":170,"view_count":87},"2017年8月","\u002Farchive\u002F2017\u002F8\u002F1",{"title":172,"uri":173,"view_count":87},"2017年5月","\u002Farchive\u002F2017\u002F5\u002F1",{"title":175,"uri":176,"view_count":87},"2017年4月","\u002Farchive\u002F2017\u002F4\u002F1",{"title":178,"uri":179,"view_count":87},"2016年11月","\u002Farchive\u002F2016\u002F11\u002F1",{"title":181,"uri":182,"view_count":87},"2016年10月","\u002Farchive\u002F2016\u002F10\u002F1",{"title":184,"uri":185,"view_count":110},"2016年9月","\u002Farchive\u002F2016\u002F9\u002F1",{"title":187,"uri":188,"view_count":87},"2016年8月","\u002Farchive\u002F2016\u002F8\u002F1",{"title":190,"uri":191,"view_count":110},"2016年7月","\u002Farchive\u002F2016\u002F7\u002F1",{"title":193,"uri":194,"view_count":133},"2016年6月","\u002Farchive\u002F2016\u002F6\u002F1",{"title":196,"uri":197,"view_count":87},"2015年12月","\u002Farchive\u002F2015\u002F12\u002F1",{"title":199,"uri":200,"view_count":87},"2015年10月","\u002Farchive\u002F2015\u002F10\u002F1",{"title":202,"uri":203,"view_count":110},"2015年2月","\u002Farchive\u002F2015\u002F2\u002F1",{"title":205,"uri":206,"view_count":87},"2015年1月","\u002Farchive\u002F2015\u002F1\u002F1",{"title":208,"uri":209,"view_count":210},"2014年12月","\u002Farchive\u002F2014\u002F12\u002F1","[8]",{"title":212,"uri":213,"view_count":110},"2014年11月","\u002Farchive\u002F2014\u002F11\u002F1",{"title":215,"uri":216,"view_count":133},"2014年10月","\u002Farchive\u002F2014\u002F10\u002F1",{"title":218,"uri":219,"view_count":123},"2014年9月","\u002Farchive\u002F2014\u002F9\u002F1",{"title":221,"uri":222,"view_count":123},"2014年8月","\u002Farchive\u002F2014\u002F8\u002F1",{"title":224,"uri":225,"view_count":123},"2014年7月","\u002Farchive\u002F2014\u002F7\u002F1",{"title":227,"uri":228,"view_count":110},"2013年9月","\u002Farchive\u002F2013\u002F9\u002F1",{"url":230,"title":231},"https:\u002F\u002Fimage.duguying.net\u002F2022\u002F09\u002Fij6x8o4s.webp","老家 摄于2020年秋",1790062269047]