Android PhoneGap源碼分析——白名單

來源:互聯網
上載者:User

標籤:android   java   http   tar   ext   com   

對於單獨的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);        }    }

 

    我們可以看到,它用Regex解析後將白名單中的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;    }

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.