FinClip小程序里如何安全使用SVG


?

在小程序中使用SVG,和在普通网页中不太一样。SVG也并不仅是另一种图片格式这么简单。它是代码,需要有额外的安全考量。在小程序里成功使用SVG的诀窍在于(1)结合CSS background image,(2)采用inline方式,(3)用对Data URI scheme,(4)把内容转换为base64。经本文整理,向读者提供最完整的介绍。

网上零零散散有一些关于在小程序中如何使用SVG的内容,但不是语焉不详,就是信息不完整。在此整理一下,供哪怕是此前从来没有接触过SVG的开发者也可以参考,迅速利用。首先SVG可以被视为一种DSL(Domain Specific Language),也就是说它并不仅仅是JPEG、PNG、GIF之外的又一种图片格式,它还是一种“代码”;也因此,它既有无比的潜力让开发者发挥创意作出有意思的应用,它也有潜在安全风险。在小程序中,起码至目前为止,它的使用方式和在普通网页并不完全一样。

什么是SVG

SVG是Scalable Vector Graphics的缩写。它:

  • 用于定义矢量图
  • 是一种XML文本
  • 所定义的每一个元素(Element)及其属性(Attribute)均可以支持动画
  • 是W3C推荐的开放标准
  • 能与其他W3C标准如DOM、XSL等结合使用
  • 有以下的好处:
    • 文件能用文本编辑器编辑,虽然文件后缀是svg,但和JPG、PNG、GIF等不是一回事,而是一种XML格式的文本
    • SVG图形内容,能被索引、搜索、脚本化操作处理、压缩。例如Google就明确声明,它的网络爬虫会索引SVG图形的文本内容,因此用户可以通过SEO加以利用
    • 矢量图放大缩小不失真

以下的svg描述了一个多边形:


  

SVG图形是如何被引用至网页中的

第一种,也是最简单直观的方式,即把svg后缀的文件视作为和PNG、JPEG、GIF类似的图片:


第二种,当嵌入的svg文件需要引用外部资源(例如字体、脚本、其他bitmap类型的二进制图片或者其他svg),或者对内容有一定的交互和处理,标签无法支持,这时可以采用标签:


第三种,是直接把svg内容,通过标签嵌入至网页中,也就是说,svg的数据内容直接是当前网页的一部分,浏览器是在加载当前网页时直接解释渲染的,而前面两种方式,则作为svg文件资源,由浏览器在加载解释当前页面时按文件所在URL进行网络下载。这是所谓的inline svg模式,或者称为内联的svg。例如:






  
  Sorry, your browser does not support inline SVG.




第四种,在CSS中作为background image引入,例如:

#id {
  background-image: url(image.svg);
}

这本质上和第一种方式相似。

上述四种方式的使用,各有优劣,例如:

  • inline方式加载速度最快,而方式则比较慢
  • 浏览器可以对方式和方式的svg图片资源作缓存。但inline模式下,浏览器则无法对svg作单独缓存
  • 前二者均可以通过GZip压缩(而且通常能达到75%-85%的压缩率),但inline模式下svg数据和网页融为一体,就没办法单独压缩了
  • inline方式下,svg内容是当前网页里的标签下的数据内容,所以是可以被脚本动态处理的,可以基于用户操作行为作出动态的响应,交互性非常好;方式下,也有一定程度的交互灵活性,但方式则是完全不可能了
  • 方式下,svg数据都是“封装”在各自的文件载体下,不用担心其中数据与当前网页中的其他内容冲突(例如里面的ID、Class和其他svg图形中Element的ID、Class重复),所以它们的svg数据是隔离的,修改维护都容易。但inline方式下,你必须保证每一个svg标签下的内容中的Element的ID、Class都是在当前网页下唯一的,否则渲染就会出问题

    什么时候该用哪种方式?正常情况下,方式是最简单直接、容易维护。但当你需要互动能力的时候,inline是最佳选择。

    使用SVG是否有安全风险

    TL;DR 对于没时间兴趣关注本话题的读者,可以跳到下一节。简短的回答是:有 - 看你怎么用。但观点是:但不能因噎废食,在小程序里我们可以运用。

    以下是关于SVG安全相关的详细内容。

    首先,如上所述,SVG是可以被脚本化的,例如:

    <?xml version="1.0" standalone="no"?>
    
     
    
    
    <script type="text/javascript">
    alert('This app is probably vulnerable to XSS attacks!');
    </script>
    
    

    看到<script>这个标签就全明白了,svg不仅是矢量图,里面可以内嵌脚本。

    XSS攻击

    这是如何发生的?只要你的Web应用允许用户上传、提交svg文件,内嵌在其中的恶意代码就可以妥妥的操作你应用页面里的DOM,余下的就是“常规”XSS攻击的事情。

    HTML注入

    SVG用XML语法和格式描述矢量,在XML中无法直接引用HTML。为了满足这方面的应用需求,SVG提供了一个叫foreignObject的元素,以便于开发者引入外部XML namespace下的元素。例如:

    <?xml version= 1.0" standalone="no"?>
    
    
        ?rect x="10" y="1?" width="100" height="100" stroke="red" stroke-width="10" fill="white" />