Android浅谈网页打开APP(二)

作者: 浪够_ | 来源:发表于2018-09-05 16:58 被阅读29次

    上一节粗略讲述了URL Scheme如何打开app,这一节对这一机制进行详细说明

    Android安全开发之浅谈网页打开App(一)

    一. 网页打开APP简介

    Android有一个特性,可以通过点击网页内的某个链接打开APP,或者在其他APP中通过点击某个链接打开另外一个APP,一些用户量比较大的APP,已经通过发布其AppLink SDK,开发者需要申请相应的资格,配置相关内容才能使用。这些都是通过用户自定义的URI scheme实现的,不过背后还是Android的Intent机制。Google的官方文档《Android Intents with Chrome》一文,介绍了在Android Chrome浏览器中网页打开APP的两种方法,一种是用户自定义的URI scheme(Custom URI scheme),另一种是“intent:”语法(Intent-based URI)。

    第一种用户自定义的URI scheme形式如下:

    scheme://host/path?parameters

    第二种的Intent-based URI的语法形式如下:

    intent://host/uri_path#Intent;参数;end

    因为第二种形式大体是第一种形式的特例,所以很多文章又将第二种形式叫Intent Scheme URL,但是在Google的官方文档并没有这样的说法。

    注意:使用Custom URI scheme给APP传递数据,只能使用相关参数来传递数据,不能想当然的使用scheme://host/uri_path#intent;参数;end的形式来构造传给APP的intent数据。具体原因看本文3.1章节

    此外,还必须在APP的Androidmanifest文件中配置相关的选项才能产生网页打开APP的效果,具体在下面讲。

    二. Custom Scheme URI打开APP

    2.1 基本用法
    需求:使用网页打开一个APP,并通过URL的参数给APP传递一些数据。
    如自定义的Scheme为:

    wdgz://wdfull/card?card_id=828

    网页端的写法如下:

    <?xml version="1.0 encoding="utf-8?>
    <html>
         <head>
              <title>URL打开App</title>
         </head>
         <body>
              <a href="wdgz://wdfull/card?card_id=828">打开豌豆公主App</a>
         </body>
    </html>
    

    APP端接收来自网页信息的Activity,要在Androidmanifest.xml文件中Activity的intent-filter中声明相应action、category和data的scheme等。

    <activity android:name="MyActivity">
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
    
                    <data
                        android:scheme="wdgz"
                        android:host="wdfull" />
    
                </intent-filter>
    </activity>
    

    在MyActivity中接收intent并且获取相应参数的代码:

     @Override
     protected void onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            val intent = getIntent()
            Log.d("shixiangyu",intent.toURI())
            val uri = intent.getData()
    

    此时,我们可以看一下打印的intent.toURI()信息:


    image.png

    由上图可知Android系统自动为Custom URI scheme添加了默认的 #intent字段,以及category,launchFlags,component等信息。

    另外还有以下几个API来获取相关信息:

    getIntent().getScheme(); //获得Scheme名称

    getIntent().getDataString(); //获得Uri全部路径

    getIntent().getHost(); //获得host

    三. Intent-based URI打开APP

    3.1基本用法
    Intent-based URI语法:

    intent://
    host/uri_path
    #Intent;
    action=[action];
    category=[category];
    component=[component]
    scheme=[scheme];
    end

    注意:第二个Intent的第一个字母一定要大写,不然不会成功调用APP。

    如何正确快速的构造网页端的intent?

    可以先建个Android demo app,按正常的方法构造自己想打开某个组件的Intent对象,然后使用Intent的toUri()方法,会得到Intent对象的Uri字符串表示,并且已经用UTF-8和Uri编码好,直接复制放到网页端即可。

                   Intent i = new Intent();
                   i.setAction("android.intent.action.VIEW");
                   i.addCategory("android.intent.category.BROWSABLE");
                   i.addCategory("android.intent.category.DEFAULT");
                   i.setData(Uri.parse("wdgz://wdfull/card?card_id=828"));
    
                   Log.d("shixiangyu",i.toUri(Intent.URI_INTENT_SCHEME));
    
    

    结果:


    image.png

    如果在demo中的Intent对象不能传递给目标APP的Activity或其他组件,则其Uri形式放在网页端也不可能打开APP的,这样写个demo容易排查错误。

    另外,对于传递的参数的设置,可以直接跟在uri_path里面,然后直接通过intent.setData()的形式进行传递,上面的就是此种形式,也可以通过intent.putExtra()的方式进行传递,如

                   Intent i = new Intent();
                   i.setAction("android.intent.action.VIEW");
                   i.addCategory("android.intent.category.BROWSABLE");
                   i.addCategory("android.intent.category.DEFAULT");
                   i.setData(Uri.parse("wdgz://wdfull/card"));
                   i.putExtra("card_id","828");
                   Log.d("shixiangyu",i.toUri(Intent.URI_INTENT_SCHEME));
    

    我们来看一下此时构造的intent长相如何:


    image.png

    S.card_id跟的就是intent对象的putExtra()方法中的数据。采取这种方式的话,从intent中获取相关信息就有通过intent,getStringExtra()方法了。

    APP端中的Androidmanifest.xml的声明写法同2.1节中的APP端写法完全一样。

    然后服务端的代码便可换成:

    <a href="intent://wdfull/card?cart_id=828#Intent;scheme=wdgz;category=android.intent.category.DEFAULT;category=android.intent.category.BROWSABLE;end">
    打开豌豆公主App
    </a>
    

    接着,我们在被打开的MyActivity中打印intent.toUri()信息:

    image.png

    不要惊呼,没有看错,android系统自动把intent://替换成了wdgz://,另外在后面添加了 component 的相关信息。

    问题来了,为何不能用scheme://host#intent;参数;end的形式来构造传给APP的intent数据,然后直接给网页端呢?

    如对于:

    <a href="wdgz://wdfull/card?cart_id=828#Intent;scheme=wdgz;category=android.intent.category.DEFAULT;category=android.intent.category.BROWSABLE;end">打开豌豆公主App</a>
    

    是的,就是因为Android系统会自动为Custom URI scheme而非Intent-based URI添加默认的#intent等信息。

    四. 风险评估

    常见的用法是在APP获取到来自网页的数据后,重新生成一个intent,然后发送给别的组件使用这些数据。比如使用Webview相关的Activity来加载一个来自网页的url,如果此url来自url scheme中的参数,如:wdgz://wdfull/html?load_url=http://m.wamdougongzhu.cn

    如果在APP中,没有检查获取到的load_url的值,攻击者可以构造钓鱼网站,诱导用户点击加载,就可以盗取用户信息。

    在Webview加载load_url时,结合APP的自身业务采用白名单机制过滤网页端传过来的数据,黑名单容易被绕过。

    相关文章

      网友评论

        本文标题:Android浅谈网页打开APP(二)

        本文链接:https://www.haomeiwen.com/subject/uhsawftx.html