OWASP कैटगरी: MASVS-CODE: कोड की क्वालिटी
खास जानकारी
FileProvider, ContentProvider का एक सबक्लास है. इसका मकसद, किसी ऐप्लिकेशन ("सर्वर ऐप्लिकेशन") को दूसरे ऐप्लिकेशन के साथ फ़ाइलें शेयर करने का सुरक्षित तरीका उपलब्ध कराना ("क्लाइंट ऐप्लिकेशन") है. हालांकि, अगर क्लाइंट ऐप्लिकेशन, सर्वर ऐप्लिकेशन से मिले फ़ाइल नाम को सही तरीके से हैंडल नहीं करता है, तो हमलावर के कंट्रोल वाला सर्वर ऐप्लिकेशन, अपना नुकसान पहुंचाने वाला FileProvider लागू कर सकता है. इससे क्लाइंट ऐप्लिकेशन के ऐप्लिकेशन के हिसाब से स्टोरेज में मौजूद फ़ाइलों को ओवरराइट किया जा सकता है.
असर
अगर कोई हमलावर किसी ऐप्लिकेशन की फ़ाइलों को बदल सकता है, तो इससे नुकसान पहुंचाने वाले कोड को एक्ज़ीक्यूट किया जा सकता है. इसके लिए, ऐप्लिकेशन के कोड को बदला जाता है. इसके अलावा, ऐप्लिकेशन के व्यवहार में बदलाव किया जा सकता है. उदाहरण के लिए, ऐप्लिकेशन की शेयर की गई प्राथमिकताओं या अन्य कॉन्फ़िगरेशन फ़ाइलों को बदला जा सकता है.
जोखिम कम करने के तरीके
उपयोगकर्ता के इनपुट पर भरोसा न करें
फ़ाइल सिस्टम कॉल का इस्तेमाल करते समय, उपयोगकर्ता के इनपुट के बिना काम करना बेहतर होता है. इसके लिए, स्टोरेज में मिली फ़ाइल लिखते समय, यूनीक फ़ाइल नाम जनरेट करें.
दूसरे शब्दों में: जब क्लाइंट ऐप्लिकेशन, मिली हुई फ़ाइल को स्टोरेज में लिखता है, तो उसे सर्वर ऐप्लिकेशन से मिले फ़ाइल नाम को अनदेखा करना चाहिए. इसके बजाय, उसे फ़ाइल नाम के तौर पर, अपने अंदरूनी तौर पर जनरेट किए गए यूनीक आइडेंटिफ़ायर का इस्तेमाल करना चाहिए.
यह उदाहरण, 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;
}
Sanitize Provided Filenames
स्टोरेज में मिली फ़ाइल को लिखते समय, दिए गए फ़ाइल नाम को सैनिटाइज़ करें.
यह तरीका, पहले वाले तरीके से कम बेहतर है. इसकी वजह यह है कि सभी संभावित मामलों को मैनेज करना मुश्किल हो सकता है. हालांकि: अगर यूनीक फ़ाइलनेम जनरेट करना व्यावहारिक नहीं है, तो क्लाइंट ऐप्लिकेशन को दिए गए फ़ाइलनेम को सैनिटाइज़ करना चाहिए. सैनिटाइज़ेशन में ये शामिल हैं:
- फ़ाइल के नाम में पाथ ट्रेवर्सल के वर्णों को सैनिटाइज़ करना
- कैननिकल बनाने की प्रोसेस की जा रही है, ताकि यह पुष्टि की जा सके कि कोई पाथ ट्रैवर्सल नहीं है
यह उदाहरण कोड, फ़ाइल की जानकारी पाने से जुड़ी गाइडलाइन पर आधारित है:
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
}
योगदान देने वाले लोग: Microsoft Threat Intelligence के दिमित्रियोस वाल्समारस और माइकल पेक
संसाधन
- डर्टी स्ट्रीम अटैक: Android के शेयर टारगेट को अटैक वेक्टर में बदलना
- सुरक्षित तरीके से फ़ाइल शेयर करना
- शेयर की गई फ़ाइल के दस्तावेज़ का अनुरोध करना
- जानकारी वापस पाएं
- FileProvider
- पाथ ट्रैवर्सल
- CWE-73 External Control of Filename or Path