【问题标题】:Fixing a SQL Injection Vulnerability on Content Provider Android修复 Content Provider Android 上的 SQL 注入漏洞
【发布时间】:2018-12-11 17:55:48
【问题描述】:

我有不同的应用程序彼此共享一些数据,这是通过内容提供程序完成的,但是当我上传 apk 时,我收到一封电子邮件,说“您的应用程序正在使用包含 SQL 注入漏洞的内容提供程序。”

根据 Google 指南,有几种方法可以解决此问题:

如果受影响的 ContentProvider 需要暴露给其他应用:

  • 您可以通过使用防止 SQL 注入到 SQLiteDatabase.query 带有投影图的严格模式。严格模式可防止 恶意选择条款和投影图防止 恶意投影条款。您必须同时使用这两种功能来确保您的查询是安全的。

  • 您可以通过使用使用“?”的选择子句来防止 SQL 注入到 SQLiteDatabase.update 和 SQLiteDatabase.delete作为可替换参数和单独的选择参数数组。您的选择子句不应由不受信任的输入构成。

但我不清楚如何继续使用任何解决方案,我不知道如何使用投影图或使用使用“?”的选择子句更改代码。我的意思是,我已经看过一些关于 ProjectionMap 的示例,但是查询所需的键/值是什么?我需要明确写出我想要的值吗?但是如果它是一个通用方法并且我不知道在那部分代码中我想要得到什么?或者如何将任何查询转换为 ProjectionMap?

我希望我是在解释自己。

这是我的代码:

     @Override
public boolean onCreate() {
    gOpenHelper = new GameDBHelper(getContext());
    return true;
}

/**
 * Builds a UriMatcher that is used to determine witch database request is being made.
 */
public static UriMatcher buildUriMatcher(){
    String content = GamesContract.CONTENT_AUTHORITY;

    // All paths to the UriMatcher have a corresponding code to return
    // when a match is found (the ints above).
    UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    matcher.addURI(content, GamesContract.PATH_GAME, GAME);
    matcher.addURI(content, GamesContract.PATH_GAME + "/#", GAME_ID);

    return matcher;
}

@Override
public String getType(Uri uri) {

    switch(sUriMatcher.match(uri))
    {
        case GAME:
            return GameEntry.CONTENT_TYPE;
        case GAME_ID:
            return GameEntry.CONTENT_ITEM_TYPE;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }
}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    final SQLiteDatabase db = gOpenHelper.getWritableDatabase();
    Cursor retCursor;
    switch(sUriMatcher.match(uri))
    {
        case GAME:
            retCursor = db.query(
                    GameEntry.TABLE_NAME,
                    projection,
                    selection,
                    selectionArgs,
                    null,
                    null,
                    sortOrder
            );
            break;
        case GAME_ID:
            long _id = ContentUris.parseId(uri);

            retCursor = db.query(
                    GameEntry.TABLE_NAME,
                    projection,
                    GameEntry._ID + " = ?",
                    new String[]{String.valueOf(_id)},
                    null,
                    null,
                    sortOrder
            );
            break;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }
    retCursor.setNotificationUri(getContext().getContentResolver(), uri);
    return retCursor;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
    final SQLiteDatabase db = gOpenHelper.getWritableDatabase();
    long _id;
    Uri returnUri;

    switch(sUriMatcher.match(uri))
    {
        case GAME:
            _id = db.insert(GameEntry.TABLE_NAME, null, values);

            if(_id > 0){
                returnUri = GameEntry.BuildGameUri(_id);
            } else{
                throw new UnsupportedOperationException("Unable to insert rows into: " + uri);
            }
            break;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }

    getContext().getContentResolver().notifyChange(uri, null);
    return returnUri;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    final SQLiteDatabase db = gOpenHelper.getWritableDatabase();
    int rows; // Number of rows effected

    switch(sUriMatcher.match(uri))
    {
        case GAME:
            rows = db.delete(GameEntry.TABLE_NAME, selection, selectionArgs);
            break;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }

    // Because null could delete all rows:
    if(selection == null || rows != 0){
        getContext().getContentResolver().notifyChange(uri, null);
    }

    return rows;
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    final SQLiteDatabase db = gOpenHelper.getWritableDatabase();
    int rows;

    switch(sUriMatcher.match(uri))
    {
        case GAME:
            rows = db.update(GameEntry.TABLE_NAME, values, selection, selectionArgs);
            break;
        default:
            throw new UnsupportedOperationException("Unknown uri: " + uri);
    }

    if(rows != 0){
        getContext().getContentResolver().notifyChange(uri, null);
    }

    return rows;
}

}

【问题讨论】:

  • 有这个相关的问题,但没有明确的答案stackoverflow.com/questions/51631320/…
  • 我猜分析器不喜欢GameEntry._ID + " = ?" 选择。您可以尝试以另一种方式编写它。我目前无法确认,所以请尝试并回来告诉我们
  • 是的,我看到了这个问题,但确实没有明确的答案。谢谢!,我会检查那个选择。
  • 嗨@EduardoArroyo,您找到解决方案了吗?如果有,你介意分享一下吗?
  • @BarryBostwick 对不起,我的一个朋友帮我解决了这个问题,我忘记了帖子,但我只是发布了我找到的解决方案,希望对您有所帮助。

标签: android android-contentprovider


【解决方案1】:

对于任何可以寻找答案的人来说,这很简单,我认为这对我来说越来越难了,因为我不熟悉 Android 上有关 sql 的许多概念。

问题在于需要“where”子句的查询;在我的情况下更新和删除,为了修复它我需要做的事情是不使用选择参数,我需要做类似的事情:

GamesContract.Games.GAME_NAME + " = ?"

db.delete(GamesContract.Games.TABLE_NAME, GamesContract.Games.GAME_NAME + " = ?", selectionArgs);

基本上是表名+“=?”选择。

这样你就强制只更新需要的表,并防止 sql 注入通过相同的方法修改数据库中的任何位置。

【讨论】:

  • 您能否更新问题中的代码以包含此解决方案?我仍然无法绕过它。谢谢
【解决方案2】:

我使用 SQLiteQueryBuilder 修复了它

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
        builder.setTables(TABLE_NAME);
        builder.setProjectionMap(buildProjectionMap());
        builder.setStrict(true);
        count = builder.delete(db, selection, selectionArgs);
    }else {
        count = db.delete(TABLE_NAME, selection, selectionArgs);
    } 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-05
    • 2019-05-09
    • 1970-01-01
    • 2020-08-21
    • 2014-12-19
    • 1970-01-01
    • 2019-05-03
    相关资源
    最近更新 更多