OWASP kategorisi: MASVS-STORAGE: Depolama
Genel Bakış
Zip Path Traversal (Yol Geçişi) güvenlik açığı (ZipSlip olarak da bilinir), sıkıştırılmış arşivlerin işlenmesiyle ilgilidir. Bu sayfada, bu güvenlik açığını örnek olarak ZIP biçimini kullanarak gösteriyoruz ancak TAR, RAR veya 7z gibi diğer biçimleri işleyen kitaplıklarda da benzer sorunlar ortaya çıkabilir.
Bu sorunun temel nedeni, ZIP arşivlerinde her bir paketlenmiş dosyanın tam nitelikli bir adla saklanmasıdır. Bu ad, eğik çizgi ve nokta gibi özel karakterlere izin verir. java.util.zip
paketindeki varsayılan kitaplık, arşiv girişlerinin adlarında dizin geçişi karakterleri (../
) olup olmadığını kontrol etmez. Bu nedenle, arşivden çıkarılan ad ile hedeflenen dizin yolu birleştirilirken özel dikkat gösterilmelidir.
Harici kaynaklardan gelen ZIP çıkarma kod snippet'lerini veya kitaplıklarını doğrulamanız çok önemlidir. Bu tür kitaplıkların çoğu, Zip Path Traversal (Yol Geçişi) güvenlik açığına karşı savunmasızdır.
Etki
Zip Path Traversal (Yol Geçişi) güvenlik açığı, rastgele dosya üzerine yazma işlemi yapmak için kullanılabilir. Koşullara bağlı olarak etki değişebilir ancak bu güvenlik açığı çoğu durumda kod yürütme gibi büyük güvenlik sorunlarına yol açabilir.
Risk azaltma önlemleri
Bu sorunu azaltmak için her girişi ayıklamadan önce hedef yolun her zaman hedef dizinin alt öğesi olduğunu doğrulamanız gerekir. Aşağıdaki kod, hedef dizinin güvenli olduğunu (yalnızca uygulamanız tarafından yazılabilir ve saldırgan kontrolünde değildir) varsayar. Aksi takdirde uygulamanız, sembolik bağlantı saldırıları gibi diğer güvenlik açıklarına karşı savunmasız kalabilir.
Kotlin
companion object {
@Throws(IOException::class)
fun newFile(targetPath: File, zipEntry: ZipEntry): File {
val name: String = zipEntry.name
val f = File(targetPath, name)
val canonicalPath = f.canonicalPath
if (!canonicalPath.startsWith(
targetPath.canonicalPath + File.separator)) {
throw ZipException("Illegal name: $name")
}
return f
}
}
Java
public static File newFile(File targetPath, ZipEntry zipEntry) throws IOException {
String name = zipEntry.getName();
File f = new File(targetPath, name);
String canonicalPath = f.getCanonicalPath();
if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) {
throw new ZipException("Illegal name: " + name);
}
return f;
}
Mevcut dosyaların yanlışlıkla üzerine yazılmasını önlemek için ayıklama işlemine başlamadan önce hedef dizinin boş olduğundan da emin olmanız gerekir. Aksi takdirde, uygulamanızın kilitlenmesi veya aşırı durumlarda uygulamanın güvenliğinin ihlal edilmesi riskiyle karşılaşırsınız.
Kotlin
@Throws(IOException::class)
fun unzip(inputStream: InputStream?, destinationDir: File) {
if (!destinationDir.isDirectory) {
throw IOException("Destination is not a directory.")
}
val files = destinationDir.list()
if (files != null && files.isNotEmpty()) {
throw IOException("Destination directory is not empty.")
}
ZipInputStream(inputStream).use { zipInputStream ->
var zipEntry: ZipEntry
while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
val targetFile = File(destinationDir, zipEntry.name)
// ...
}
}
}
Java
void unzip(final InputStream inputStream, File destinationDir)
throws IOException {
if(!destinationDir.isDirectory()) {
throw IOException("Destination is not a directory.");
}
String[] files = destinationDir.list();
if(files != null && files.length != 0) {
throw IOException("Destination directory is not empty.");
}
try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
final File targetFile = new File(destinationDir, zipEntry);
…
}
}
}
Kaynaklar
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Dizin geçişi (Path Traversal)