首页

关于android开发中如何正确使用Temporary permit Content Providers 安全用法及代码示例

标签:android,Temporary permit Content Providers,安全     发布时间:2017-11-01   

一、前言

1、显式设置exported属性为false。@b@2、指定暂时开放权限访问的路径。@b@3、处理收到的数据,确认其真实性。@b@4、来自授权临时访问的数据可以返回。。@b@5、为临时访问指定URI。@b@6、为临时访问指定访问权限。@b@7、发送intent给临时授权访问的app。@b@8、通过请求临时访问返回intent到应用。

二、原代码示例

1.AndroidManifest.xml(不可以在安卓2.2以下的环境使用)

<?xml version="1.0" encoding="utf-8"?>@b@<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jssec.android.provider.temporaryprovider">@b@ @b@    <application@b@        android:icon="@drawable/ic_launcher"@b@        android:label="@string/app_name" >@b@ @b@    <activity android:name=".TemporaryActiveGrantActivity"@b@        android:label="@string/app_name"@b@        android:exported="true" >@b@        <intent-filter>@b@            <action android:name="android.intent.action.MAIN" />@b@            <category android:name="android.intent.category.LAUNCHER" />@b@        </intent-filter>@b@    </activity>@b@ @b@        <!-- Temporary Content Provider -->@b@        <!-- *** POINT 2 *** Explicitly set the exported attribute to false. -->@b@        <provider@b@            android:name=".TemporaryProvider"@b@            android:authorities="org.jssec.android.provider.temporaryprovider"@b@            android:exported="false" >@b@ @b@        <!-- *** POINT 3 *** Specify the path to grant access temporarily with the grant-uri-permission. -->@b@            <grant-uri-permission android:path="/addresses" />@b@        </provider>@b@ @b@        <activity@b@            android:name=".TemporaryPassiveGrantActivity"@b@            android:label="@string/app_name" android:exported="true" />@b@    </application>@b@</manifest>

2.TemporaryProvider.java

package org.jssec.android.provider.temporaryprovider;@b@ @b@import android.content.ContentProvider;@b@import android.content.ContentUris;@b@import android.content.ContentValues;@b@import android.content.UriMatcher;@b@import android.database.Cursor;@b@import android.database.MatrixCursor;@b@import android.net.Uri;@b@ @b@public class TemporaryProvider extends ContentProvider {@b@    public static final String AUTHORITIY = "org.jssec.android.provider.temporaryprovider";@b@    public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.org.jssec.contenttype";@b@    public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.org.jssec.contenttype";@b@ @b@    // Expose the interface that the Content Provider provides.@b@    public interface Download {@b@        public static final String PATH = "downloads";@b@        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIY + "/" + PATH);@b@    }@b@   @b@    public interface Address {@b@        public static final String PATH = "addresses";@b@        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIY + "/" + PATH);@b@    }@b@ @b@    // UriMatcher@b@    private static final int DOWNLOADS_CODE = 1;@b@    private static final int DOWNLOADS_ID_CODE = 2;@b@    private static final int ADDRESSES_CODE = 3;@b@    private static final int ADDRESSES_ID_CODE = 4;@b@    private static UriMatcher sUriMatcher;@b@    static {@b@        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);@b@        sUriMatcher.addURI(AUTHORITIY, Download.PATH, DOWNLOADS_CODE);@b@        sUriMatcher.addURI(AUTHORITIY, Download.PATH + "/#", DOWNLOADS_ID_CODE);@b@        sUriMatcher.addURI(AUTHORITIY, Address.PATH, ADDRESSES_CODE);@b@        sUriMatcher.addURI(AUTHORITIY, Address.PATH + "/#", ADDRESSES_ID_CODE);@b@    }@b@ @b@    // Since this is a sample program,@b@    // query method returns the following fixed result always without using database.@b@    private static MatrixCursor sAddressCursor = new MatrixCursor(new String[] { "_id", "city" });@b@    static {@b@        sAddressCursor.addRow(new String[] { "1", "New York" });@b@        sAddressCursor.addRow(new String[] { "2", "London" });@b@        sAddressCursor.addRow(new String[] { "3", "Paris" });@b@ @b@}@b@    private static MatrixCursor sDownloadCursor = new MatrixCursor(new String[] { "_id", "path" });@b@    static {@b@        sDownloadCursor.addRow(new String[] { "1", "/sdcard/downloads/sample.jpg" });@b@        sDownloadCursor.addRow(new String[] { "2", "/sdcard/downloads/sample.txt" }); }@b@ @b@    @Override@b@    public boolean onCreate() {@b@        return true;@b@    }@b@ @b@    @Override@b@    public String getType(Uri uri) {@b@ @b@        switch (sUriMatcher.match(uri)) {@b@        case DOWNLOADS_CODE:@b@        case ADDRESSES_CODE:@b@            return CONTENT_TYPE;@b@ @b@        case DOWNLOADS_ID_CODE:@b@        case ADDRESSES_ID_CODE:@b@            return CONTENT_ITEM_TYPE;@b@ @b@        default:@b@            throw new IllegalArgumentException("Invalid URI:" + uri);@b@        }@b@    }@b@ @b@    @Override@b@    public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {@b@ @b@        // *** POINT 4 *** Handle the received request data carefully and securely,@b@        // even though the data comes from the application granted access temporarily.@b@        // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case. // Checking for other parameters are omitted here, due to sample.@b@        // Please refer to "3.2 Handle Input Data Carefully and Securely."@b@ @b@        // *** POINT 5 *** Information that is granted to disclose to the temporary access applications can be returne@b@        // It depends on application whether the query result can be disclosed or not.@b@        switch (sUriMatcher.match(uri)) {@b@        case DOWNLOADS_CODE:@b@        case DOWNLOADS_ID_CODE:@b@            return sDownloadCursor;@b@ @b@        case ADDRESSES_CODE:@b@        case ADDRESSES_ID_CODE:@b@            return sAddressCursor;@b@ @b@        default:@b@            throw new IllegalArgumentException("Invalid URI:" + uri);@b@        }@b@    }@b@ @b@    @Override@b@    public Uri insert(Uri uri, ContentValues values) {@b@ @b@        // *** POINT 4 *** Handle the received request data carefully and securely,@b@        // even though the data comes from the application granted access temporarily.@b@        // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case.@b@        // Checking for other parameters are omitted here, due to sample.@b@        // Please refer to "3.2 Handle Input Data Carefully and Securely."@b@        // *** POINT 5 *** Information that is granted to disclose to the temporary access applications can be returne@b@        // It depends on application whether the issued ID has sensitive meaning or not.@b@        switch (sUriMatcher.match(uri)) {@b@        case DOWNLOADS_CODE:@b@            return ContentUris.withAppendedId(Download.CONTENT_URI, 3);@b@ @b@        case ADDRESSES_CODE:@b@            return ContentUris.withAppendedId(Address.CONTENT_URI, 4);@b@ @b@        default:@b@            throw new IllegalArgumentException("Invalid URI:" + uri);@b@        }@b@    }@b@ @b@    @Override@b@    public int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {@b@ @b@        // *** POINT 4 *** Handle the received request data carefully and securely,@b@        // even though the data comes from the application granted access temporarily.@b@        // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case. // Checking for other parameters are omitted here, due to sample.@b@        // Please refer to "3.2 Handle Input Data Carefully and Securely."@b@        // *** POINT 5 *** Information that is granted to disclose to the temporary access applications can be returne@b@        // It depends on application whether the number of updated records has sensitive meaning or not.@b@        switch (sUriMatcher.match(uri)) {@b@        case DOWNLOADS_CODE:@b@            return 5; // Return number of updated records@b@ @b@        case DOWNLOADS_ID_CODE:@b@            return 1;@b@ @b@        case ADDRESSES_CODE:@b@            return 15;@b@ @b@        case ADDRESSES_ID_CODE:@b@            return 1;@b@ @b@        default:@b@            throw new IllegalArgumentException("Invalid URI:" + uri);@b@ @b@        }@b@    }@b@ @b@ @b@    @Override@b@    public int delete(Uri uri, String selection, String[] selectionArgs) {@b@ @b@        // *** POINT 4 *** Handle the received request data carefully and securely,@b@        // even though the data comes from the application granted access temporarily.@b@        // Here, whether uri is within expectations or not, is verified by UriMatcher#match() and switch case. // Checking for other parameters are omitted here, due to sample.@b@        // Please refer to "3.2 Handle Input Data Carefully and Securely."@b@        // *** POINT 5 *** Information that is granted to disclose to the temporary access applications can be returne@b@d.@b@        // It depends on application whether the number of deleted records has sensitive meaning or not.@b@        switch (sUriMatcher.match(uri)) {@b@        case DOWNLOADS_CODE:@b@            return 10; // Return number of deleted records@b@ @b@        case DOWNLOADS_ID_CODE:@b@            return 1;@b@ @b@        case ADDRESSES_CODE:@b@            return 20;@b@ @b@        case ADDRESSES_ID_CODE:@b@            return 1;@b@ @b@        default:@b@            throw new IllegalArgumentException("Invalid URI:" + uri);@b@        }@b@    }@b@}

3.TemporaryActiveGrantActivity.java

package org.jssec.android.provider.temporaryprovider;@b@ @b@import android.app.Activity;@b@import android.content.ActivityNotFoundException;@b@import android.content.Intent;@b@import android.os.Bundle;@b@import android.view.View;@b@import android.widget.Toast;@b@ @b@public class TemporaryActiveGrantActivity extends Activity {@b@ @b@    // User Activity Information@b@    private static final String TARGET_PACKAGE = "org.jssec.android.provider.temporaryuser";@b@    private static final String TARGET_ACTIVITY = "org.jssec.android.provider.temporaryuser.TemporaryUserActivity";@b@ @b@    @Override@b@    protected void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.active_grant);@b@    }@b@ @b@    // In the case that Content Provider application grants access permission to other application actively.@b@    public void onSendClick(View view) {@b@        try {@b@            Intent intent = new Intent();@b@ @b@            // *** POINT 6 *** Specify URI for the intent to grant temporary access.@b@            intent.setData(TemporaryProvider.Address.CONTENT_URI);@b@ @b@            // *** POINT 7 *** Specify access rights for the intent to grant temporary access.@b@            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);@b@ @b@            // *** POINT 8 *** Send the explicit intent to an application to grant temporary access.@b@            intent.setClassName(TARGET_PACKAGE, TARGET_ACTIVITY);@b@            startActivity(intent);@b@   @b@        } catch (ActivityNotFoundException e) {@b@            Toast.makeText(this, "User Activity not found.", Toast.LENGTH_LONG).show();@b@        }@b@    }@b@}

4.TemporaryPassiveGrantActivity.java

package org.jssec.android.provider.temporaryprovider;@b@ @b@import android.app.Activity;@b@import android.content.Intent;@b@import android.os.Bundle;@b@import android.view.View;@b@ @b@public class TemporaryPassiveGrantActivity extends Activity {@b@    @Override@b@    protected void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.passive_grant);@b@    }@b@ @b@    // In the case that Content Provider application passively grants access permission@b@    // to the application that requested Content Provider access.@b@ @b@    public void onGrantClick(View view) {@b@        Intent intent = new Intent();@b@ @b@        // *** POINT 6 *** Specify URI for the intent to grant temporary access.@b@        intent.setData(TemporaryProvider.Address.CONTENT_URI);@b@ @b@        // *** POINT 7 *** Specify access rights for the intent to grant temporary access.@b@        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);@b@ @b@        // *** POINT 9 *** Return the intent to the application that requests temporary access.@b@        setResult(Activity.RESULT_OK, intent);@b@        finish();@b@    }@b@ @b@    public void onCloseClick(View view) {@b@        finish();@b@    }@b@}

5.安全使用TemporaryUserActivity.java - 不可以发送敏感数据、当收到结果时,处理收到的数据,确认安全性

TemporaryUserActivity.java@b@ @b@package org.jssec.android.provider.temporaryuser;@b@ @b@import android.app.Activity;@b@import android.content.ActivityNotFoundException;@b@import android.content.Intent;@b@import android.content.pm.ProviderInfo;@b@import android.database.Cursor;@b@import android.net.Uri;@b@import android.os.Bundle;@b@import android.view.View;@b@import android.widget.TextView;@b@ @b@public class TemporaryUserActivity extends Activity {@b@ @b@    // Information of the Content Provider's Activity to request temporary content provider access.@b@    private static final String TARGET_PACKAGE = "org.jssec.android.provider.temporaryprovider";@b@    private static final String TARGET_ACTIVITY = "org.jssec.android.provider.temporaryprovider.TemporaryPasiveGrantActivity";@b@ @b@    // Target Content Provider Information@b@    private static final String AUTHORITY = "org.jssec.android.provider.temporaryprovider";@b@    private interface Address {@b@        public static final String PATH = "addresses";@b@        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH);@b@    }@b@ @b@    private static final int REQUEST_CODE = 1;@b@ @b@    public void onQueryClick(View view) {@b@ @b@        logLine("[Query]");@b@        Cursor cursor = null;@b@        try {@b@            if (!providerExists(Address.CONTENT_URI)) {@b@                logLine(" Content Provider doesn't exist.");@b@                return;@b@            }@b@ @b@            // *** POINT 10 *** Do not send sensitive information.@b@            // If no problem when the information is taken by malware, it can be included in the request.@b@            cursor = getContentResolver().query(Address.CONTENT_URI, null, null, null, null);@b@ @b@            // *** POINT 11 *** When receiving a result, handle the result data carefully and securely.@b@            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."@b@            if (cursor == null) {@b@                logLine(" null cursor");@b@            } else {@b@                boolean moved = cursor.moveToFirst();@b@                while (moved) {@b@                    logLine(String.format(" %d, %s", cursor.getInt(0), cursor.getString(1)));@b@                    moved = cursor.moveToNext();@b@                }@b@            }@b@        } catch (SecurityException ex) {@b@            logLine(" Exception:" + ex.getMessage());@b@        }@b@        finally {@b@            if (cursor != null) cursor.close();@b@        }@b@    }@b@ @b@    // In the case that this application requests temporary access to the Content Provider@b@    // and the Content Provider passively grants temporary access permission to this application.@b@    public void onGrantRequestClick(View view) {@b@        Intent intent = new Intent();@b@        intent.setClassName(TARGET_PACKAGE, TARGET_ACTIVITY);@b@        try {@b@            startActivityForResult(intent, REQUEST_CODE);@b@        } catch (ActivityNotFoundException e) {@b@            logLine("Content Provider's Activity not found.");@b@        }@b@    }@b@ @b@    private boolean providerExists(Uri uri) {@b@        ProviderInfo pi = getPackageManager().resolveContentProvider(uri.getAuthority(), 0);@b@        return (pi != null);@b@    }@b@ @b@    private TextView mLogView;@b@ @b@    // In the case that the Content Provider application grants temporary access@b@    // to this application actively.@b@ @b@    @Override@b@    public void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.main);@b@        mLogView = (TextView)findViewById(R.id.logview);@b@    }@b@ @b@    private void logLine(String line) {@b@        mLogView.append(line);@b@        mLogView.append("¥n");@b@    }@b@}

三、安全代码示例

不可以发送敏感数据,当收到结果时,处理收到的数据,确认安全性。

TemporaryUserActivity.java@b@ @b@package org.jssec.android.provider.temporaryuser;@b@ @b@import android.app.Activity;@b@import android.content.ActivityNotFoundException;@b@import android.content.Intent;@b@import android.content.pm.ProviderInfo;@b@import android.database.Cursor;@b@import android.net.Uri;@b@import android.os.Bundle;@b@import android.view.View;@b@import android.widget.TextView;@b@ @b@public class TemporaryUserActivity extends Activity {@b@ @b@    // Information of the Content Provider's Activity to request temporary content provider access.@b@    private static final String TARGET_PACKAGE = "org.jssec.android.provider.temporaryprovider";@b@    private static final String TARGET_ACTIVITY = "org.jssec.android.provider.temporaryprovider.TemporaryPasiveGrantActivity";@b@ @b@    // Target Content Provider Information@b@    private static final String AUTHORITY = "org.jssec.android.provider.temporaryprovider";@b@    private interface Address {@b@        public static final String PATH = "addresses";@b@        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH);@b@    }@b@ @b@    private static final int REQUEST_CODE = 1;@b@ @b@    public void onQueryClick(View view) {@b@ @b@        logLine("[Query]");@b@        Cursor cursor = null;@b@        try {@b@            if (!providerExists(Address.CONTENT_URI)) {@b@                logLine(" Content Provider doesn't exist.");@b@                return;@b@            }@b@ @b@            // *** POINT 10 *** Do not send sensitive information.@b@            // If no problem when the information is taken by malware, it can be included in the request.@b@            cursor = getContentResolver().query(Address.CONTENT_URI, null, null, null, null);@b@ @b@            // *** POINT 11 *** When receiving a result, handle the result data carefully and securely.@b@            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."@b@            if (cursor == null) {@b@                logLine(" null cursor");@b@            } else {@b@                boolean moved = cursor.moveToFirst();@b@                while (moved) {@b@                    logLine(String.format(" %d, %s", cursor.getInt(0), cursor.getString(1)));@b@                    moved = cursor.moveToNext();@b@                }@b@            }@b@        } catch (SecurityException ex) {@b@            logLine(" Exception:" + ex.getMessage());@b@        }@b@        finally {@b@            if (cursor != null) cursor.close();@b@        }@b@    }@b@ @b@    // In the case that this application requests temporary access to the Content Provider@b@    // and the Content Provider passively grants temporary access permission to this application.@b@    public void onGrantRequestClick(View view) {@b@        Intent intent = new Intent();@b@        intent.setClassName(TARGET_PACKAGE, TARGET_ACTIVITY);@b@        try {@b@            startActivityForResult(intent, REQUEST_CODE);@b@        } catch (ActivityNotFoundException e) {@b@            logLine("Content Provider's Activity not found.");@b@        }@b@    }@b@ @b@    private boolean providerExists(Uri uri) {@b@        ProviderInfo pi = getPackageManager().resolveContentProvider(uri.getAuthority(), 0);@b@        return (pi != null);@b@    }@b@ @b@    private TextView mLogView;@b@ @b@    // In the case that the Content Provider application grants temporary access@b@    // to this application actively.@b@ @b@    @Override@b@    public void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.main);@b@        mLogView = (TextView)findViewById(R.id.logview);@b@    }@b@ @b@    private void logLine(String line) {@b@        mLogView.append(line);@b@        mLogView.append("¥n");@b@    }@b@}