整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

通过 webview 获取网页元素

求出现的原因是这样的:

我们有一个 h5 页面既需要嵌套到 App 内部进行用户的信息填写及提交,同时这个 h5 页面也能够单独使用。
而这些用户信息中就包括 `input`框选择图片。
<input type="file" name="avatar" id="avatar" />

出现了什么问题呢?

那么问题就出在 input 框这里,点击这个 input 框,调起 android 手机自带的拍照和选择文件的对话框。



小米手机系统调起

只要选择「图片」或者「拍照」都没有问题

问题就出现在选择「取消」的时候

出现了问题,需要先解决点击「取消」程序卡死的问题

这个问题,这篇文章已经介绍过了

H5 通过 input 标签,调起 Android 手机相册,点击取消时手机卡住

如果之前没有配置过,可以参考配置一下。


下面说今天的正题,就是点击取消之后,如果之前头像内有图片,那么取消后会把之前的图片干掉,展示出一个加载错误的样子

原因找到了:当取消的时候,为了避免卡顿,返回了一个图片的路径,但是这个图片路径上对应的图片是不存在的,从而导致了 H5 img 位置 显示加载失败



加载失败

那么现在问题就来了:

如果不返回,就会卡顿;

如果返回了,就把 img 之前的展示图片给干掉了

怎么办呢?

我这边的思路是:

将原来的 img 标签内的 src 的内容存储到本地,然后判断:如果用户点击了取消,那么就将这个原来的 src 内容返回回去

那么这样操作的话,就需要知道,如何拿到 img 标签中的 src 的内容。

那么实际需要做这么几部

1、这个需要和 H5 或者后台沟通一下,将 H5 中的 这个头像的 id 唯一,以保证能唯一找到这个头像。

2、根据 id 找到这个 img 标签后,再拿 src 中的内容存储到本地

3、判断用户点击取消时,将本地存储的图片 src 内容,返回给 H5。

第三点已经在

H5 通过 input 标签,调起 Android 手机相册,点击取消时手机卡住

这个文章中提到过了,找到合适的位置直接返回即可。

所以本篇文章中的重点就是第一步:根据 id 找到 src 的内容。

1、创建 addJavascriptInterface 需要的「操作对象」,这个操作对象就是一个类,名字随便起,不过内部的方法必须添加@JavascriptInterface注解

 //内部类
 final class InJavaScriptLocalObj {
 private static final String TAG = "MainActivity";
 /**
 * @JavascriptInterface 必须要有的哦
 * @param html
 *
 * 一旦检测到 html 匹配到了要找的元素或者属性,这里就会调用,否则不会调用这个方法
 */
 @JavascriptInterface
 public void showAvatar(String html) {
 Log.e(TAG, "showAvatar: " + html);
 }
}

2、配置 webview

 private void initWebView() {
 /**
 * 三项均需配置
 */
 webView.getSettings().setJavaScriptEnabled(true);
 webView.addJavascriptInterface(new InJavaScriptLocalObj(), "local_obj");
 webView.setWebViewClient(new WebViewClient() {
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, String url) {
 webView.loadUrl(url);
 return true;
 }
 @Override
 public void onPageFinished(WebView view, String url) {
 super.onPageFinished(view, url);
 /**
 * ambassador_avatar 是和后台(h5)商议的唯一的 id
 */
 view.loadUrl("javascript:window.local_obj.showAvatar(document.getElementById('ambassador_avatar').getAttribute('src'));");
 }
 });
}

那么找元素关键的一句,就是

view.loadUrl("javascript:window.local_obj.showAvatar(document.getElementById('ambassador_avatar').getAttribute('src'));");

备注

找元素的方法很相似,就是跟 html 中的找 dom 节点一样

可以移步到 w3school 逛网看一看

dom 元素查找方法

这里小总结一下

 通过 id 查找 HTML 元素
 通过标签名查找 HTML 元素
 通过类名查找 HTML 元素
 通过 CSS 选择器查找 HTML 元素
 通过 HTML 对象集合查找 HTML 元素
 var myElement = document.getElementById("intro");//返回单个元素
 var x = document.getElementsByTagName("p");//返回元素列表
 var x = document.getElementsByClassName("intro");//返回元素列表
 var x = document.querySelectorAll("p.intro");//返回元素列表
 var x = document.forms["frm1"];//返回元素列表

返回元素列表的方法在使用是 需要像数组那样去取得对应的元素,如x[0]

拿到元素之后,通过 DOM 的文档,拿到想要的属性、值等等信息。

这样就能通过存储原来的图片信息,在点击取消的时候把信息返回,解决了本次需求的问题。

谢谢大家的收藏、关注、转发,持续更新!

1、Android中利用webView调用网页上的Js代码。

Android 中可以通过webView来实现和Js的交互,在程序中调用Js代码,只需要将webView控件的支持Js的属性设置为true,然后通过loadUrl就可以直接进行调用,如下所示:

mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl("javascript:test()");

2、网页上调用Android中Java代码的方法

在网页中调用Java代码,需要在webView控件中添加javascriptInterface。如下所示:

mWebView.addJavascriptInterface(new Object() {
 public void clickOnAndroid() {
 mHandler.post(new Runnable() {
 public void run() {
 Toast.makeText(Test.this, "测试调用java", Toast.LENGTH_LONG).show();
 }
 });
 }
}, "demo");

在网页中,只需要像调用Js方法一样,进行调用就可以

<div id='b'><a onclick="window.demo.clickOnAndroid()">b.c</a></div>

3、Java代码调用Js并传参

首先需要带参数的Js函数,如function test(str),然后只需在调用Js时传入参数即可,如下所示:

mWebView.loadUrl("javascript:test('aa')");

4、Js中调用Java函数并传参

首先一样需要带参数的函数形式,但需注意此处的参数需要final类型,即得到以后不可修改,如果需要修改其中的值,可以先设置中间变量,然后进行修改。如下所示:

mWebView.addJavascriptInterface(new Object() {
 public void clickOnAndroid(final int i) {
 mHandler.post(new Runnable() {
 public void run() {
 int j = i;
 j++;
 Toast.makeText(Test.this, "测试调用java" + String.valueOf(j), Toast.LENGTH_LONG).show();
 }
 });
 }
}, "demo");

然后在html页面中,利用如下代码:

<div id='b'><a onclick="window.demo.clickOnAndroid(2)">b.c</a></div>,

即可实现调用

eb安全策略

web安全对iOS开发者来说重要吗?重要!APP中通常会使用很多web页面,例如广告、登录流程、闪屏,或者需要使用跨平台功能的时候。你可能在页面中仅仅一部分使用web,也可以整个页面都是webView,甚至做一个web app。因此web安全对于app来说非常重要。

来自web的安全攻击有以下几种:

  • 跨域攻击
  • 预测执行攻击
  • 窗口控制攻击

本文将针对这三种攻击类型,给出安全防御措施。

安全传输

网络传输相信大家都很熟悉了,安全的传输能够保证接收到的数据来自可信任的站点,并且在传输工程中不会被篡改。安全传输是其它安全措施的基础,采取的措施包括:

  • HTTPS和WSS(webSocket Secure)
  • Http Strict Transport Security(HSTS)遵循HTTPS安全协议的web只能访问同样遵循HTTPS安全协议的内容,不能访问遵循HTTP不安全协议的内容。
  • Upgrade-Insecure-Requests 请求头中添加这一项表示web支持更加安全的升级机制,服务器可以重定向到这个站点的安全版本。
  • 使用cookie确保安全,cookie只能被安全的传输,例如千万不要把cookie放到粘贴板上
  • 在app的info.plist文件中

Allow Arbitrary Loads in Web Content 这个开关一定要置为 NO!

跨域封锁

web的内容可以来自任何站点,例如,webView上的一张图片可以来自任何服务器,也可以从任意服务器上加载一个脚本或iframe。需要注意的是要当心来自其它服务器的资源。跨域的保护已经有20多年的历史,并且形成了基本原则--同源策略:只有和页面来源相同的脚本才会被该页面执行。例如iframe来自不同的域名,同源策略不允许加载这个iframe。仅仅靠同源策略还是不够的,还需要采取其它的防御措施。

1. Subresource Integrity

服务器可能会发生异常导致下发错误的资源使得web发生crash,但是开发者通常是知道所要请求哪个资源的,在脚本里面增加一个检查签名。如果签名匹配则认为是下发了正确的资源,如果不匹配仍然可以正常工作,此时尝试从页面的资源里查找或者从自己的服务器重新加载。这样做虽然降低了性能,但是提升了安全性。

<script src="https://cdn.example/framework.js"
 integrity="sha256-8WqyJLuWKRB...oZkCnxQbWwJVw=">
</script>
window.framwork || // reload from own domain

2. Content Security Policy

HTTP response:
:status:200
Content-Security-Policy:
 default 'self'; // No inline
 script-src cdn.example;
 frame-src social.example;
 frame-ancestors news.example;

HTTP response的Header里面,default设置成自己,默认只能加载同源的资源;script-src和frame-src 分别指定可信任的脚本和iframe的来源;frame-ancestor设置成news.example,指定只有news.example可以iframe我们的web。

另外不使用inline属性的脚本也是一种防御措施,不使用inline脚本,只从文件加载脚本,这么做分离了逻辑和文件,更加安全。

3. HttpOnly cookies

HTTPOnly cookies作为一种安全措施,已经有至少15年的使用历史。在这之前script通过document.cookie这个强大的api能拿到文档的cookie,留下安全隐患。HTTPOnly cookies能够阻止这种情况,只允许HTTP请求访问cookie,禁止使用script访问cookie。它的使用方式很简单,只需要在HTTP response的Header里面加上HttpOnly这一项,如下

HTTP response:
:status:200
Set-Cookie:
 auth = abc...123; HttpOnly;

4. SameSite cookies

在HTTP response的Header里面将SameSite cookies这一项设置为Strict,那么将不允许把cookie从一个域名发送到另一个域名。例如其他人的web里面嵌入了我们的web,如果我们的服务器HTTP response的Header里面SameSite cookies = Strict,那么其他人将无法使用他的cookie来访问我们的服务器。

HTTP response:
:status:200
Set-Cookie:
 auth = abc...123; HttpOnly;
 SameSite=strict

5. Cross-Origin-Resource-Policy

Cross-Origin-Resource-Policy是推出的新功能。之前web可以加载任意web中的资源,例如图片或者script。在HTTP response的Header里面将Cross-Origin-Resource-Policy这一项设置为Same,将不允许别人的web向我们的服务器请求图片或者script,但是我们自己的web可以。

HTTP response:
:status:200
Cross-Origin-Resource-Policy:Same

6. Cross-Origin-Window-Policy

Cross-Origin-Window-Policy也是新推出的功能。之前通过window.open这个强大的api,其他人的web可以在新窗口中打开我们域名下的web,通过一些手段可以修改我们的web,导航到攻击者指定的页面。在HTTP response的Header里面将Cross-Origin-Resource-Policy这一项设置为Deny,将阻止其他人修改我们web中的内容,当然别人仍然还是可以打开我们的web。Cross-Origin-Resource-Policy适用于希望使用post message 进行窗口间通信,但是不想让别人控制我们自己web内容的情况。

HTTP response:
:status:200
Cross-Origin-Window-Policy:Deny

常见的web攻击及防御手段

Cross-Origin Attacks

  • Cross-Site Scripting
  • Compromised CDN
  • Cross-Site Request Forgeries

Cross-Site Scripting

例如我们的web里面有一个文本框,用户可以输入文字,如下图。假如攻击者注入了这么一段脚本,如果没有采取防御措施,那么我们web的cookie就会被盗取。

在HTTP response的Header中添加HTTPOnly这一项,就能阻止脚本访问文档的cookie,从而防御跨域脚本攻击。

另外一种防御手段是Content-Security-Policy,如下

HTTP response:
:status:200
Content-Security-Policy:
 default-src 'self'; // No inline

Content-Security-Policy能保证拒绝加载外部来源的脚本,并且不使用inline属性的脚本,只从文件中加载脚本。

Compromised CDN

例如我们的web需要从某个外部资源装载一个framework,攻击者可能拦截这个请求,并把它重定向到自己的攻击脚本上,如下图

使用Content-Security-Policy中script-src这个属性可以指定信任的脚本来源,并且在引用资源的时候指定来源和校验签名,如下

在HTTP response中:

HTTP response:
:status:200
Content-Security-Policy:
 default-src 'self'; 
 script-src cdn.example;

在HTML中:

<script src="https://cdn.example/framework.js"
 integrity="sha256-8WqyJLuWKRB...oZkCnxQbWwJVw=">
</script>
window.framwork || // reload from own domain

3. Cross-Site Request Forgeries

攻击者可能在自己的web中嵌入我们的web,然后向我们的服务器发起一个伪造的网络请求(使用的是攻击者网站的cookie),如下图

如果采取了防御措施,将HTTP response的Header里面的SameSite设置为strict,那么就会禁止攻击者网站的cookie发动到我们的服务器上面,如下

HTTP response:
:status:200
Set-Cookie:
 auth=abc...123; SameSite=strict

2. Speculative execution attacks (Spectre)

防御措施有:

  • WKWebView
  • Content Security Policy
  • HttpOnly cookies
  • SameSite cookies
  • Cross-Origin-Resource-Policy

Speculative execution 的定义:预测执行类似于批量执行条件判断语句,例如计算机大量执行"x是否会造成数组array越界"这条指令,就能推测出这个数组的长度,进一步推测出这个数组在内存缓冲区中的地址边界。利用缓冲区溢出这种攻击手段,可以向web中注入攻击脚本。当x超过数组边界的时候,本来应该执行越界的error回调,但是确取出了攻击脚本并执行,造成数据泄露。显然只靠同源策略是无法防御这种攻击的,因为攻击脚本和文档处在同一个域名下,并且在同一个线程中。

防御预测执行攻击的方法是确保web内容和其他iframe(例如攻击脚本)处在不同的线程中

WKWebView

以Safari app为例,WKWebView会单独分离出一个NetWork线程用于处理添加cookie等逻辑,而且每个网页处在不同的线程当中,所以evil网页是无法通过预测执行攻击手段攻击我们的页面。而且因为NetWork线程也是独立的,所以evil网页也无法通过预测执行攻击手段拿到重要数据,例如cookie。

如果使用UIWebView,所有的web包括NetWork线程都在app的同一个线程中,所以是无法防御预测执行攻击手段的。

Content security policy

Content security policy的封锁功能是处于Network线程中,和web线程是分离的,因此可以防御预测执行攻击手段。

例如web要加载一个广告iframe,但是这个广告iframe被重定向到了一个攻击脚本,如果使用了Content security policy,如下,因为攻击脚本不在信任的frame-src里面,所以会禁止加载。还有一种情况,攻击者的web引入了我们的web,因为设置了frame-ancestors为none,所以会禁止攻击者网站引入我们的web,从而防御攻击。

HTTP response:
:status:200
Content-Security-Policy:
 default-src 'self'; 
 frame-src ad.example
 social.example
 frame-ancestors 'none'

HttpOnly cookies 和 SameSite cookies

HttpOnly cookies 和 SameSite cookies的封锁功能也是处于Network线程中,和web线程是分离的,因此可以防御预测执行攻击手段。HttpOnly cookies能够禁止攻击者通过脚本拿到cookie。SameSite cookies设为strict能够禁止cookie从一个域发送到另一个域。

Cross-Origin-Resource-Policy

Cross-Origin-Resource-Policy的封锁功能也是处于Network线程中,和web线程是分离的,因此可以防御预测执行攻击手段。Cross-Origin-Resource-Policy设置成Same能禁止攻击者的web加载我们网站的资源。

Window Control Attacks

Cross-Origin-Window-Policy

攻击者的页面可以通过window.open这个api在新的窗口打开我们的web,攻击者趁我们不注意的时候,把我们的页面导航到钓鱼页面,然后诱导用户填写用户名和密码,这样就窃取到了用户信息。把HTTP response Header里面的Cross-Origin-Window-Policy设置为Deny,能够禁止攻击者修改我们的web,这样攻击者就无法导航到钓鱼页面。

总结

每种安全措施防御的攻击类型

建议

  • 使用安全的网络传输(例如https,wss)
  • 设置Cookies为HttpOnly和其它安全选项
  • 把UIWebView升级到WKWebView
  • 测试防御措施是否生效,web是否仍然能正常工作。安全措施都具有一定的限制功能,因此测试web是否能正常工作非常重要。例如Content-Securityp-Policy的script-src白名单里少些了一个允许的域名,那么这个域名下的web就无法正常使用了。