Categoria OWASP: MASVS-CODE: Code Quality
Panoramica
FileProvider, una sottoclasse di ContentProvider, ha lo scopo di fornire un metodo sicuro per un'applicazione ("applicazione server") per condividere file con un'altra applicazione ("applicazione client"). Tuttavia, se l'applicazione client non gestisce correttamente il nome file fornito dall'applicazione server, un'applicazione server controllata da un malintenzionato potrebbe essere in grado di implementare il proprio FileProvider dannoso per sovrascrivere i file nello spazio di archiviazione specifico dell'app dell'applicazione client.
Impatto
Se un malintenzionato può sovrascrivere i file di un'applicazione, ciò può portare all'esecuzione di codice dannoso (sovrascrivendo il codice dell'applicazione) o consentire di modificare in altro modo il comportamento dell'applicazione (ad esempio, sovrascrivendo le preferenze condivise dell'applicazione o altri file di configurazione).
Mitigazioni
Non considerare attendibile l'input utente
Preferisci lavorare senza input dell'utente quando utilizzi chiamate al file system generando un nome file univoco quando scrivi il file ricevuto nell'archiviazione.
In altre parole, quando l'applicazione client scrive il file ricevuto nell'archivio, deve ignorare il nome file fornito dall'applicazione server e utilizzare il proprio identificatore univoco generato internamente come nome file.
Questo esempio si basa sul codice disponibile all'indirizzo https://developer.android.com/training/secure-file-sharing/request-file:
Kotlin
// Code in
// https://developer.android.com/training/secure-file-sharing/request-file#OpenFile
// used to obtain file descriptor (fd)
try {
val inputStream = FileInputStream(fd)
val tempFile = File.createTempFile("temp", null, cacheDir)
val outputStream = FileOutputStream(tempFile)
val buf = ByteArray(1024)
var len: Int
len = inputStream.read(buf)
while (len > 0) {
if (len != -1) {
outputStream.write(buf, 0, len)
len = inputStream.read(buf)
}
}
inputStream.close()
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
Log.e("MainActivity", "File copy error.")
return
}
Java
// Code in
// https://developer.android.com/training/secure-file-sharing/request-file#OpenFile
// used to obtain file descriptor (fd)
FileInputStream inputStream = new FileInputStream(fd);
// Create a temporary file
File tempFile = File.createTempFile("temp", null, getCacheDir());
// Copy the contents of the file to the temporary file
try {
OutputStream outputStream = new FileOutputStream(tempFile))
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
Log.e("MainActivity", "File copy error.");
return;
}
Eliminazione dei nomi file forniti
Sanitizza il nome file fornito durante la scrittura del file ricevuto nello spazio di archiviazione.
Questa mitigazione è meno auspicabile di quella precedente perché può essere difficile gestire tutti i potenziali casi. Tuttavia, se la generazione di un nome file univoco non è pratica, l'applicazione client deve sanificare il nome file fornito. La sanificazione include:
- Pulizia dei caratteri di attraversamento del percorso nel nome file
- Eseguire una canonizzazione per verificare che non ci siano attraversamenti di percorsi
Questo codice di esempio si basa sulle indicazioni per recuperare le informazioni sui file:
Kotlin
protected fun sanitizeFilename(displayName: String): String {
val badCharacters = arrayOf("..", "/")
val segments = displayName.split("/")
var fileName = segments[segments.size - 1]
for (suspString in badCharacters) {
fileName = fileName.replace(suspString, "_")
}
return fileName
}
val displayName = returnCursor.getString(nameIndex)
val fileName = sanitizeFilename(displayName)
val filePath = File(context.filesDir, fileName).path
// saferOpenFile defined in Android developer documentation
val outputFile = saferOpenFile(filePath, context.filesDir.canonicalPath)
// fd obtained using Requesting a shared file from Android developer
// documentation
val inputStream = FileInputStream(fd)
// Copy the contents of the file to the new file
try {
val outputStream = FileOutputStream(outputFile)
val buffer = ByteArray(1024)
var length: Int
while (inputStream.read(buffer).also { length = it } > 0) {
outputStream.write(buffer, 0, length)
}
} catch (e: IOException) {
// Handle exception
}
Java
protected String sanitizeFilename(String displayName) {
String[] badCharacters = new String[] { "..", "/" };
String[] segments = displayName.split("/");
String fileName = segments[segments.length - 1];
for (String suspString : badCharacters) {
fileName = fileName.replace(suspString, "_");
}
return fileName;
}
String displayName = returnCursor.getString(nameIndex);
String fileName = sanitizeFilename(displayName);
String filePath = new File(context.getFilesDir(), fileName).getPath();
// saferOpenFile defined in Android developer documentation
File outputFile = saferOpenFile(filePath,
context.getFilesDir().getCanonicalPath());
// fd obtained using Requesting a shared file from Android developer
// documentation
FileInputStream inputStream = new FileInputStream(fd);
// Copy the contents of the file to the new file
try {
OutputStream outputStream = new FileOutputStream(outputFile))
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
} catch (IOException e) {
// Handle exception
}
Collaboratori: Dimitrios Valsamaras e Michael Peck di Microsoft Threat Intelligence
Risorse
- Dirty Stream Attack: Turning Android Share Targets Into Attack Vectors
- Condivisione sicura dei file
- Richiedere la documentazione di un file condiviso
- Recupera informazioni
- FileProvider
- Path Traversal
- CWE-73 External Control of Filename or Path