OWASP kategorisi: MASVS-STORAGE: Depolama
Genel Bakış
ZipSlip olarak da bilinen Zip Path Traversal (Yol Geçişi) güvenlik açığı, sıkıştırılmış arşivlerin işlenmesiyle ilgilidir. Bu sayfada, ZIP biçimini örnek olarak kullanarak bu güvenlik açığını 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 paketlenmiş dosyanın tam nitelikli bir adla depolanmasıdır. Bu durum, 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 alınan 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.
Çözümler
Bu sorunu azaltmak için her girişi ayıklamadan önce hedef yolun, hedef dizinin alt öğesi olduğunu doğrulamanız gerekir. Aşağıdaki kodda, hedef dizinin güvenli olduğu (yalnızca uygulamanız tarafından yazılabilir ve saldırgan kontrolünde olmadığı) varsayılır. 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.
- Dosya yolu manipülasyonu