zoukankan      html  css  js  c++  java
  • Android PhoneGap源码分析——白名单

    对于单独的Web app应用来说,加载进来的url一般不能保证它的安全性。那么如何来处理url安全性的问题呢。

        让我们来看看PhoneGap是如何做的。

        PhoneGap采用了白名单的形式,认为在白名单中的url认为是安全的,不在白名单中的url是不安全的。对于安全的url,PhoneGap的Web app会直接打开,对于不安全的url,会通过浏览器打开。

        那么怎么增加白名单呢?PhoneGap是需要在配置文件res/xml/config.xml中设置,如下:

     <cordova>
    - <!--     access elements control the Android whitelist.
        Domains are assumed blocked unless set otherwise
         
      --> 
      <access origin="http://127.0.0.1*" /> 
    - <!--  allow local pages 
      --> 
    - <!--  <access origin="https://example.com" /> allow any secure requests to example.com 
      --> 
    - <!--  <access origin="https://example.com" subdomains="true" /> such as above, but including subdomains, such as www 
      --> 
      <access origin=".*" /> 
      <log level="DEBUG" /> 
      <preference name="useBrowserHistory" value="false" /> 
      <preference name="exit-on-suspend" value="false" /> 
    - <plugins>
      <plugin name="App" value="org.apache.cordova.App" /> 
      <plugin name="Geolocation" value="org.apache.cordova.GeoBroker" /> 
      <plugin name="Device" value="org.apache.cordova.Device" /> 
      <plugin name="Accelerometer" value="org.apache.cordova.AccelListener" /> 
      <plugin name="Compass" value="org.apache.cordova.CompassListener" /> 
      <plugin name="Media" value="org.apache.cordova.AudioHandler" /> 
      <plugin name="Camera" value="org.apache.cordova.CameraLauncher" /> 
      <plugin name="Contacts" value="org.apache.cordova.ContactManager" /> 
      <plugin name="File" value="org.apache.cordova.FileUtils" /> 
      <plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager" /> 
      <plugin name="Notification" value="org.apache.cordova.Notification" /> 
      <plugin name="Storage" value="org.apache.cordova.Storage" /> 
      <plugin name="Temperature" value="org.apache.cordova.TempListener" /> 
      <plugin name="FileTransfer" value="org.apache.cordova.FileTransfer" /> 
      <plugin name="Capture" value="org.apache.cordova.Capture" /> 
      <plugin name="Battery" value="org.apache.cordova.BatteryListener" /> 
      <plugin name="SplashScreen" value="org.apache.cordova.SplashScreen" /> 
      <plugin name="Echo" value="org.apache.cordova.Echo" /> 
      <plugin name="Globalization" value="org.apache.cordova.Globalization" /> 
      </plugins>
      </cordova>

        其中,<access origin="http://127.0.0.1*" />就是加的白名单,我们只需要将网址后面加上*,上格式加进配置文件即可。

        那么PhoneGap又是如何实现白名单的呢?让我们看一下源代码:CordovaWebView.java。CordovaWebView是显示的WebView的基类。它在初始化时,会加载配置文件的配置项,源代码如下:

        /**
         * Load Cordova configuration from res/xml/cordova.xml.
         * Approved list of URLs that can be loaded into DroidGap
         *      <access origin="http://server regexp" subdomains="true" />
         * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR)
         *      <log level="DEBUG" />
         */
        private void loadConfiguration() {
            int id = getResources().getIdentifier("config", "xml", this.cordova.getActivity().getPackageName());
            if(id == 0)
            {
                id = getResources().getIdentifier("cordova", "xml", this.cordova.getActivity().getPackageName());   
                Log.i("CordovaLog", "config.xml missing, reverting to cordova.xml");
            }
            if (id == 0) {
                LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
                return;
            }
            XmlResourceParser xml = getResources().getXml(id);
            int eventType = -1;
            while (eventType != XmlResourceParser.END_DOCUMENT) {
                if (eventType == XmlResourceParser.START_TAG) {
                    String strNode = xml.getName();
                    if (strNode.equals("access")) {
                        String origin = xml.getAttributeValue(null, "origin");
                        String subdomains = xml.getAttributeValue(null, "subdomains");
                        if (origin != null) {
                            this.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
                        }
                    }
                    else if (strNode.equals("log")) {
                        String level = xml.getAttributeValue(null, "level");
                        LOG.i("CordovaLog", "Found log level %s", level);
                        if (level != null) {
                            LOG.setLogLevel(level);
                        }
                    }
                    else if (strNode.equals("preference")) {
                        String name = xml.getAttributeValue(null, "name");
                        String value = xml.getAttributeValue(null, "value");
    
                        LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
                        Log.d("CordovaLog", "Found preference for " + name + "=" + value);
    
                        // Save preferences in Intent
                        this.cordova.getActivity().getIntent().putExtra(name, value);
                    }
                }
                try {
                    eventType = xml.next();
                } catch (XmlPullParserException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            // Init preferences
            if ("true".equals(this.getProperty("useBrowserHistory", "false"))) {
                this.useBrowserHistory = true;
            }
            else {
                this.useBrowserHistory = false;
            }
    
            if ("true".equals(this.getProperty("fullscreen", "false"))) {
                this.cordova.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
                this.cordova.getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
            }
        }

        在解析xml文件时,会将origin标签中的内容加入白名单,调用的是addWhiteListEntry方法。下面我们来看看addWhiteListEntry方法的源代码:

        public void addWhiteListEntry(String origin, boolean subdomains) {
            try {
                // Unlimited access to network resources
                if (origin.compareTo("*") == 0) {
                    LOG.d(TAG, "Unlimited access to network resources");
                    this.whiteList.add(Pattern.compile(".*"));
                } else { // specific access
                    // check if subdomains should be included
                    // TODO: we should not add more domains if * has already been added
                    if (subdomains) {
                        // XXX making it stupid friendly for people who forget to include protocol/SSL
                        if (origin.startsWith("http")) {
                            this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\.)?")));
                        } else {
                            this.whiteList.add(Pattern.compile("^https?://(.*\.)?" + origin));
                        }
                        LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
                    } else {
                        // XXX making it stupid friendly for people who forget to include protocol/SSL
                        if (origin.startsWith("http")) {
                            this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://")));
                        } else {
                            this.whiteList.add(Pattern.compile("^https?://" + origin));
                        }
                        LOG.d(TAG, "Origin to allow: %s", origin);
                    }
                }
            } catch (Exception e) {
                LOG.d(TAG, "Failed to add origin %s", origin);
            }
        }

        我们可以看到,它用正则表达式解析后将白名单中的url加入到whiteList这个属性中,而whiteList是个ArrayList类型的属性。

        那么PhoneGap的Web app在显示网页时又是如何利用白名单的呢?让我们继续来看下面的源代码,这是加载网页时会调用的方法:

        /**
         * Load the specified URL in the Cordova webview or a new browser instance.
         *
         * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
         *
         * @param url           The url to load.
         * @param openExternal  Load url in browser instead of Cordova webview.
         * @param clearHistory  Clear the history stack, so new page becomes top of history
         * @param params        DroidGap parameters for new app
         */
        public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap<String, Object> params) {
            LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory);
    
            // If clearing history
            if (clearHistory) {
                this.clearHistory();
            }
    
            // If loading into our webview
            if (!openExternal) {
    
                // Make sure url is in whitelist
                if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || isUrlWhiteListed(url)) {
                    // TODO: What about params?
    
                    // Clear out current url from history, since it will be replacing it
                    if (clearHistory) {
                        this.urls.clear();
                    }
    
                    // Load new URL
                    this.loadUrl(url);
                }
                // Load in default viewer if not
                else {
                    LOG.w(TAG, "showWebPage: Cannot load URL into webview since it is not in white list.  Loading into browser instead. (URL=" + url + ")");
                    try {
                        Intent intent = new Intent(Intent.ACTION_VIEW);
                        intent.setData(Uri.parse(url));
                        cordova.getActivity().startActivity(intent);
                    } catch (android.content.ActivityNotFoundException e) {
                        LOG.e(TAG, "Error loading url " + url, e);
                    }
                }
            }
    
            // Load in default view intent
            else {
                try {
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse(url));
                    cordova.getActivity().startActivity(intent);
                } catch (android.content.ActivityNotFoundException e) {
                    LOG.e(TAG, "Error loading url " + url, e);
                }
            }
        }

        我们可以看到,里面会用isUrlWhiteListed等方法判断该url是否在白名单中,或者是否安全,然后对安全的url直接通过loadUrl来 加载进该Web app,对于PhoneGap认为不安全的url会通过发Intent的形式打开浏览器加载该网页。

        下面再贴一段isUrlWhiteListed方法的源代码:

        /**
         * Determine if URL is in approved list of URLs to load.
         *
         * @param url
         * @return 
         */
        public boolean isUrlWhiteListed(String url) {
    
            // Check to see if we have matched url previously
            if (this.whiteListCache.get(url) != null) {
                return true;
            }
    
            // Look for match in white list
            Iterator<Pattern> pit = this.whiteList.iterator();
            while (pit.hasNext()) {
                Pattern p = pit.next();
                Matcher m = p.matcher(url);
    
                // If match found, then cache it to speed up subsequent comparisons
                if (m.find()) {
                    this.whiteListCache.put(url, true);
                    return true;
                }
            }
            return false;
        }
  • 相关阅读:
    使用highcharts.js插件,在ie7浏览器、文本模式为quirks模式下,鼠标移动到折线图中时,弹出框有拖影现象的解决办法
    【转载】Redis在windows下安装过程
    Asp.Net Mvc+Localdb数据库项目在IIS部署的配置
    C# 反射只获取自己定义的属性,不获取父类的属性
    【转载】C#根据当前时间获取周,月,季度,年度等时间段的起止时间
    excel导入sql server 文本被截断,或者一个或多个字符在目标代码页中没有匹配项 错误处理
    一个表中的字段值用作另一个表的In查询条件
    解决UEditor将div标签换成p标签的问题
    将table中的值转换成json格式传到后台接收处理。
    EF CodeFirst 命令步骤
  • 原文地址:https://www.cnblogs.com/xiaochao1234/p/3806278.html
Copyright © 2011-2022 走看看