Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

不正确的iframe src输入会导致页面卡死甚至浏览器崩溃 #918

Closed
HerbertHe opened this issue Feb 3, 2021 · 15 comments
Closed

Comments

@HerbertHe
Copy link
Contributor

编辑模式

  • sv 分屏编辑预览模式

描述问题

输入iframe标签的src属性时,如果输入的格式不是标准格式的话,会导致页面卡死、甚至浏览器崩溃(出现的场景为umi(React)框架启用SSR)

下面是错误示例

<iframe  src="www.baidu.com"></iframe>

正确示例无异常

<iframe src="//www.baidu.com"></iframe>
<iframe src="http://www.baidu.com"></iframe>
<iframe src="https://www.baidu.com"></iframe>

期待的结果

调整iframe标签的渲染时机,以规避即时渲染导致的不断错误请求导致页面崩溃

截屏或录像

image

版本信息

  • 版本:v3.7.7
  • 操作系统:Win10
  • 浏览器:Edge(chromium)

其他信息

@Vanessa219
Copy link
Owner

Vanessa219 commented Feb 4, 2021

感觉不像编辑器的问题
image

@HerbertHe
Copy link
Contributor Author

看打印日志好像是短时间多次渲染请求导致的问题,我把渲染间隔调的是300。每次不正确的请求都会导致子页面组件树渲染和API请求,我感觉是大量请求和页面渲染处理导致的性能问题,应该是SSR导致的。

我考虑是不是可以调整一下针对iframe标签渲染时机,来降低请求发送和渲染的频率解决这个问题🤧这个问题在即时渲染和所见即所得模式测试中没有出现,我定位一下问题看能不能在做一层优化🌚比如最后渲染iframe,输入过程中感觉sv模式下iframe的体验不是很好☹️

@HerbertHe
Copy link
Contributor Author

已经从业务代码层优化了这个问题,谢谢

@Vanessa219
Copy link
Owner

iframe 的渲染不太清楚要怎么组织

@HerbertHe
Copy link
Contributor Author

iframe 的渲染不太清楚要怎么组织

业务层处理方式

以前没考虑过这个问题,调日志发现只要iframe键入src就会不断触发渲染,而且好像输入的越快这个时间就越短,会导致短时间的请求会越高,导致被迫请求的子域名不断触发组件树渲染。因为业务开启了SSR,获取数据会在组件里面有一个res的字段const res = (await requestData()) || "",只能通过下面这种方式嵌套设计的页面安全渲染盒这种方式来阻止组件树的构建,并且在获取数据的阶段校验动态路由标准格式,再做一次校验拦截,牺牲了页面性能,不过从业务层面阻止了页面崩溃。

useEffect(() => {
    if (!res) {
        history.push("/404")
    }
}, [])

return (<>
    { !!res && children }
</>)

但是我感觉在别的SSR框架上可能也会导致整个页面的崩溃,如果框架开启了热更新的话。一开始准备从vditor的sv、ir和所见即所得渲染实现的差异下手,我感觉可能是sv与其它两个在渲染时机的差异(实在没有定位到源码在哪emmmm)

我想到的有两种方式,可以一定程度上解决iframe渲染的实现

  1. 我之前issue提到的,根据实际的业务,但并不是完全支持iframe,只是做一个转换 国内引入jsrun.net代码分享预览提高可玩性 #906
  2. 调整iframe的渲染时机,来支持iframe渲染

第二种实现思路

第二种的实现思路如下(我感觉vditor里面好像没有Lute源码renderHTML部分的实现,渲染代码块倒是有,我找了半天也可能没看到😂)

因为iframe是一个块级的元素,会生成新的节点。只要出现iframe标签就开始监听输入,但不是直接渲染,而是仅提取src这部分的数据,然后赋值给data-src,就类似于图片的延时加载的逻辑。

焦点失去或者监听到离开iframe输入的节点,再渲染,这样可以降低发送请求的数量。因为页面崩溃的原因并不是渲染错误,而是大量的错误堆积和大量请求的阻塞。这样降低请求网络延时的数量,性能也可以得到优化。

第一种方式实现

第一种实现方式其实也可以实现iframe,不过并不是直接插入iframe的HTML了

```iframe
// 遵循toml的语法(改成JSON也可以,只是写起来会很麻烦)
// 属性为标准的iframe的属性,渲染时机触发与上面相同
src=""
width=""
content=""  // content是指可能的子节点
```

转化为

<iframe src="" width="">{content}</iframe>

这种方式最大的好处在于可以很好的兼容现有的lute引擎在代码块层面的渲染逻辑(同时需要过滤掉lute里面的renderHTML对于这个iframe的渲染),而且前端的改动不是很大。并且使用配置项可以一步到位支持iframe的src的域名安全过滤,避免用户对业务造成比较大的影响

在我目前的业务(重构之后的版本暂未开源,之后可能会开源)层面通过正则实现了安全过滤,通过getValue()获取值,然后阻止用户POST

下面是正则表达式

const iframeRegexp = /<iframe\s*src=["|'](.*?)["|']\s*><\/iframe>/g

const httpUrlRegexp = /[https?:]?\/\/(\S[^\/]*)/

校验方法

/**
 * 检查文本的iframe的src是否输入合法域
 * @param raw 源文本
 */
export const check4Iframe = (raw: string): Array<boolean | string> => {
    const ans = iframeRegexp.exec(raw)
    if (!ans) return [true]

    // 提取域名
    const url = httpUrlRegexp.exec(ans[1])
    if (!url) return [true]

    // 不属于允许的iframe domain
    if (!allowDomainList.includes(url[1])) return [false, url[1]]

    return [false, ""]
}

核心思想

  • 两种方式最根本的想法都是降低请求的数量,第一种是直接在转化过程之中进行校验,第二种是利用data-src的属性替代src延时渲染
  • 渲染的触发时机,我想的是离开iframe编辑节点或者是编辑器失焦、或者可能是渲染完成之后的after?V姐应该能想到一个更好的触发时机😏

iframe目前还不在文档里,我怕有些业务会用这个导致体验可能会不太好。

@Vanessa219
Copy link
Owner

其实我不太清楚 ssr 为什么会拦截 iframe 的请求并且去触发渲染,按道理他应该负责服务端渲染而已,用户的输入已经在客服端了,不应该会产生影响。

@HerbertHe
Copy link
Contributor Author

其实我不太清楚 ssr 为什么会拦截 iframe 的请求并且去触发渲染,按道理他应该负责服务端渲染而已,用户的输入已经在客服端了,不应该会产生影响。

其实是这样的过程,客户端的vditor渲染iframe导致了其不断发送GET请求,获取iframe内部的页面。vditor编辑器所在页面路由如果是/p/new,如果不正确输入的地址为www.baidu.com,他会不断请求/p/w/p/www/p/www.baidu(随机的,不好说渲染的究竟是哪些路由,我没特别深究过)

因为iframe是一个内嵌的页面窗口,这样会触发SSR渲染iframe里面的页面,渲染完成一次大概需要300多ms,issue的截图里面有日志

@Vanessa219
Copy link
Owner

有没有 ssr 和 iframe 渲染的配置项之类的,要不这不科学。

@HerbertHe
Copy link
Contributor Author

有没有 ssr 和 iframe 渲染的配置项之类的,要不这不科学。

没emmmm,就是完全最基础的。。。我在准备测这可能导致的安全问题,iframe确实是在客户端渲染的,只是他的GET触发了页面渲染😂就感觉跟套娃一样

@HerbertHe
Copy link
Contributor Author

image
image
image

测试的结果符合了我的猜想,而且iframe的渲染并不需要完整输入标签,仅仅是输到src=就开始了,我还得进一步优化正则提取

经过检测vditor过滤了可能的XSS攻击风险💪

@Vanessa219
Copy link
Owner

那应该是你开启了options.markdown.sanitize

@HerbertHe
Copy link
Contributor Author

那应该是你开启了options.markdown.sanitize

默认是开的,但是吧iframe发送大量GET请求是存在的,如果目标网站有风控的话,可能会有问题了;业务层不特殊处理,会出现我上面那个问题

@Vanessa219
Copy link
Owner

先外部处理吧,因为只要有加载,就会有问题。

@HerbertHe
Copy link
Contributor Author

我再另外写个库自行实现第一种方式,顺便可以给docsify之类的做拓展

@HerbertHe
Copy link
Contributor Author

已经实现 toml2iframe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants