Исправление уязвимости обхода Zip Path в Android - PullRequest
3 голосов
/ 25 мая 2019

Я загрузил Мое приложение в Google Play Store, и Google выдал предупреждение «Android Security».

В приложении мы загрузили папку Zip и сохранили эту папку Zip во внутреннем хранилище, а затем распаковали еепапка во внутреннем хранилище устройства.

вот код папки UnZip:

public static void doUnzip(String inputZipFile, String 
   destinationDirectory, ZipProgressListener zipProgressListener) throws 
    IOException, RuntimeException {

 Log.e(TAG, "doUnzip:inputZipFile: " + inputZipFile);
 Log.e(TAG, "doUnzip:destinationDirectory: " + destinationDirectory);

int BUFFER = 6 * 1024;
List zipFiles = new ArrayList();
  File sourceZipFile = FileUtils.createValidFile(inputZipFile);
   File unzipDestinationDirectory = 
  FileUtils.createValidFile(destinationDirectory);
unzipDestinationDirectory.mkdir();
   String newPath = unzipDestinationDirectory.getAbsolutePath() + 
 File.separator + 
     FileUtils.getFileNameWithoutExtension(sourceZipFile.getName());
  new File(newPath).mkdir();

 ZipFile zipFile;
// Open Zip file for reading
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
int entries = zipFile.size();
int total = 0;
Log.e(TAG, "doUnzip: entries Found !!" + entries);

// Create an enumeration of the entries in the zip file
Enumeration zipFileEntries = zipFile.entries();

if (zipProgressListener != null) {
    zipProgressListener.onZipStart();
}

// Process each entry
while (zipFileEntries.hasMoreElements()) {
    // grab a zip file entry
    ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();

    String currentEntry = entry.getName();
    Log.i(TAG, "[doUnzip] " + currentEntry);

    File file = new File(newPath);

    File destFile = new File(newPath, currentEntry);
    Log.i(TAG, "doUnzip getCanonicalPath : " + 
destFile.getCanonicalPath());

    if (Build.VERSION.SDK_INT <= VERSION_CODES.LOLLIPOP) {
        Log.i(TAG, "doUnzip: LOLLIPOP");
        if 
  (!destFile.getCanonicalPath().startsWith(destinationDirectory)) {
            throw new RuntimeException(destFile.getCanonicalPath() + 
  " is outside of targetDirectory: " + destinationDirectory);
        }
    } else {
        Log.i(TAG, "doUnzip: Above ");
        if(!destFile.getCanonicalPath().contains(file.getName()) && 
    !destFile.getCanonicalPath().contains("/")){
            throw new RuntimeException(destFile.getCanonicalPath() + 
     " is outside of targetDirectory: " + destinationDirectory);
          }
      }

      if (currentEntry.endsWith(".zip")) {
        zipFiles.add(destFile.getAbsolutePath());
    }

    // grab file's parent directory structure
    File destinationParent = destFile.getParentFile();
    // create the parent directory structure if needed
    destinationParent.mkdirs();

       try {
        // extract file if not a directory
        if (!entry.isDirectory()) {
            BufferedInputStream is = new 
       BufferedInputStream(zipFile.getInputStream(entry));
            int currentByte;
            // establish buffer for writing file
            byte data[] = new byte[BUFFER];

            // write the current file to disk
            FileOutputStream fos = new FileOutputStream(destFile);
            BufferedOutputStream dest = new BufferedOutputStream(fos, 
        BUFFER);

            // read and write until last byte is encountered
            while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                dest.write(data, 0, currentByte);
            }

            Log.e(TAG, "unzip:outPath: =>" + 
        destFile.getAbsolutePath() + "\nFile size: " + destFile.length() 
         / 1024);
            dest.flush();
            dest.close();
            is.close();
        }

        int progress = 0;
        if (zipProgressListener != null) {
            progress = (total++ * 100 / entries);
            zipProgressListener.onZipProgressUpdate(progress);
        }
        Log.e(TAG, "unzip: PROGRESS::" + progress);
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
       }
       zipFile.close();

      for (Object zipFile1 : zipFiles) {
         String zipName = (String) zipFile1;
         Log.i(TAG, "doUnzip: ");
         doUnzip(zipName, destinationDirectory + File.separator + 
         zipName.substring(0, zipName.lastIndexOf(".zip")),
            zipProgressListener);
     }

   if (zipProgressListener != null) {
    Log.i(TAG, "doUnzip: " + sourceZipFile.getName());

    zipProgressListener.onZipCompleted(destinationDirectory +
            File.separatorChar + sourceZipFile.getName().substring(0, 
  sourceZipFile.getName().lastIndexOf(".zip")));
  }

   }

Вот предупреждение Google:

Эта информация предназначена для разработчиков с приложениями, которые содержат небезопасные шаблоны разархивирования, что потенциально может привести к атаке Zip Path Traversal.Расположение уязвимых классов приложений, содержащих небезопасные шаблоны разархивирования, можно найти в уведомлении Play Console для вашего приложения.

Дополнительные сведения

Zip-файлы могут содержать запись (файл или каталог), содержащую символы обхода пути(«../») в его названии.Если разработчики разархивируют такие записи в ZIP-файле без проверки их имени, это может привести к атаке обхода пути, приводящей к записи в произвольные каталоги или даже к перезаписи файлов в личных папках приложения.

Рекомендуем исправить эту проблему вваше приложение, проверив, находятся ли канонические пути к разархивированным файлам в ожидаемом каталоге.В частности, перед использованием объекта File, созданного с использованием возвращаемого значения метода getName () ZipEntry, всегда проверяйте, принадлежит ли возвращаемое значение File.GetCanonicalPath () указанному пути к каталогу.Например:

InputStream is = new InputStream(untrustedFileName);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
while((ZipEntry ze = zis.getNextEntry()) != null) {
  File f = new File(DIR, ze.getName());
  String canonicalPath = f.getCanonicalPath();
  if (!canonicalPath.startsWith(DIR)) {
    // SecurityException
  }
  // Finish unzipping…
}

Как я могу решить это предупреждение в Android OS-6 выше?

...