小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金
前言:本文主要介绍测试环境和正式环境因为nginx服务器配置的CSP(content-security-policy)内容安全策略响应头不一致,导致H5页面在IOS端下加载SKD功能失效的问题复盘
项目背景
我们的一个H5页面,页面域名是https://*.pineapple.com
,需要分别内嵌在X app
和 Y app
里面,X app
和 Y app
都有对应的ios端和android端,H5页面分别需要调用X app
或者 Y app
提供的sdk,去调用app的功能,如分享功能等
遇到的问题
H5页面在X app
里安卓端表现正常,ios端表现不正常,具体表现为在ios机型下,页面引用X app
的sdk去调用分享等其他功能失效,安卓则正常。但是H5页面在Y app
里安卓端和ios端都表现正常。
导致问题的具体原因
测试环境和正式环境的内容安全策略不一致,正式环境的服务器响应头比测试环境的多了content-security-policy,导致H5页面在X app
内加载SDK成功后,但通过iframe src触发伪协议的请求被csp拦截了,从而引发分享等功能失效,因而问题没在测试环节提前暴露出来。
原正式环境配置的响应头:
Content-Security-Policy: script-src <https://*.baidu.com><https://*.pineapple.com> 'unsafe-inline' 'unsafe-eval'; frame-src <https://*.pineapple.com><http://*.pineapple.com> webcompt:; report-uri <https://一个废弃的地址>
什么是Content-Security-Policy
content-security-policy(内容安全策略)主要是定义了当前哪些资源可以被页面加载,也可以指定控制浏览器加载iframe的资源范围。CSP的作用主要是为了减小和报告XSS攻击。
解决方案
定位csp内容拦截了那部分内容然后进行修改,以及测试环境也添加上csp,让问题及时暴露,以免上线前才匆忙暴露问题。
把配置里的 frame-src 和 report-uri去掉了就可以了,因为report-uri 里的这个地址没再维护了,因此 frame-src设置有问题
后面线上改成响应头是:
Content-Security-Policy: script-src <https://*.pineapple.com> 'unsafe-inline' 'unsafe-eval';
怎样定位到问题的
刚刚开始真的是超级难定位问题的,因为走代理的时候没经过配置的服务器,因而代理的时候又可以正常成功,像极了双缝干涉实验
一样,是否观察影响了结果。此时其实可以考虑到是服务器的原因
- 1.猜测是否在ios机型下,页面加载sdk失效
原以为是三端sdk加载失败,实际加载成功了,只是里面的请求(native和h5交互的请求)被CSP拦截了
-
2.配置了代理正常,不配置代理就异常 因为导致这个问题是由于经过服务器返回导致的,配置了代理走的是本地的,没有走配置的nginx服务器,因此配置了代理就可以了,此时应该就思考服务器的影响了
-
3.测试环境和正式环境的区别 经定位发现正式环境服务器配置多了个CSP,附以下whistler配置返回头的教程
如何调试CSP
3.1 在rules配置:
/<https://*.pineapple.com/> resHeaders://{cspHeader}
3.2 cspHeader配置:
content-security-policy: script-src <https://*.pineapple.com/> 'unsafe-inline' 'unsafe-eval'
3.3 配置手机代理,即可进行调试,从而修改策略内容定位问题。
导致该问题的原理分析
经过定位排查是frame-src的配置导致的,那为什么ios下配置frame-src失效,安卓却是正常,这就要说要到Hybird APP之native和原生h5的交互原理,经查阅资料得知
1.ios是原生h5通过调用js bridge,js bridge内创建一个iframe,然后通过修改iframe的src为提前定义好的伪协议,去触发scheme,或者其他native自带方式。
2.安卓可以不用修改iframe的src方法去触发scheme,而是调用window.prompt(uri, '')的方式去触发scheme,或者其他native自带的原生交互方式(注意原生交互方式对ios和android低版本不兼容)。
综上所述,js bridge是分别针对ios和安卓的系统调用不同方法进行捕获scheme的触发。
为了验证上面的原理,我搜了下X app
下的bridge的源码发现确实是有创建iframe的一步。但是Y app
源码是没有创建iframe的一步,是使用native自带原生的交互方式。就可以大致解析Y app
为什么可以正常,加载X app
的sdk后调用里面的功能失效。
最后
以上是我在内嵌页面遇CSP设置的一些坑,希望能对大家有帮助~如果能获得一个小小的赞作为鼓励会十分感激!!大家也可以多在评论区交流讨论哦
更多文章推荐:
「不再迷茫!看了这篇文章让你上手Vue3.0开发有丝滑般体验」
「三分钟学会使用requestAnimationFrame实现倒计时」
「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」