برنامه‌نويسی امن با زبان جاوا – قوانين اعتبارسنجی ورودی و پاک‌سازی داده‌ها – قانون IDS04-J

برنامه‌نويسی امن با زبان جاوا – قوانين اعتبارسنجی ورودی و پاک‌سازی داده‌ها – قانون IDS04-J

تاریخ ایجاد

IRCAR201402203
اولين موضوعي كه به طور كلي در برنامه نويسي امن (رجوع شود به مقاله اصول برنامه نويسي امن) و همچنين در برنامه نويسي امن با زبان جاوا مورد توجه قرار مي گيرد مربوط به اعتبار سنجي ورودي و پاكسازي داده ها است. در اين موضوع چهارده قانون معرفي مي گردد كه سطوح امنيتي مختلفي دارند (رجوع شود به مقاله برنامه‌نويسي امن با زبان جاوا – قوانين اعتبارسنجي ورودي و پاكسازي داده‌ها - آشنايي). پنجمين قانون از اين موضوع داراي سطح امنيتي سه (L3) بوده و از اولويت (P2) برخوردار مي باشد.

قانون IDS04-J – فايلها را به صورت امن از طريق ZipInputStream استخراج كنيد.
در هنگام استخراج ورودي ها از java.util.zip.ZipInputStream با احتياط عمل كنيد. دو مشكل كه مشخصاً بايد از آنها اجتناب كنيد عبارتند از: نام فايلهاي ورودي كه بر روي مسيري خارج از دايركتوري هدف استخراج استانداردسازي شده اند (IDS02-J) و ورودي هايي كه منابع سيستمي را به صورت گسترده مورد استفاده قرار مي دهند.
در حالت اول، مهاجم مي تواند از طريق فايل zip، داده دلخواه را بر روي هر دايركتوري كه براي كاربر قابل دسترس است، بنويسد. در حالت دوم زماني كه استفاده از منابع به صورت نامتناسبي بيشتر از حجم داده هاي ورودي است، منجر به انكار سرويس ميشود.
طبيعت الگوريتم zip اجازه به وجود آمدن بمب هاي zip را مي دهد كه در آنها يك فايل كوچك مانند ZIPs، GIFs، و يا محتواي HTTP رمزگذاري شده توسط gzip، بعد از اينكه از حالت فشرده در مي آيند منابع بسيار زيادي را مورد استفاده قرار مي دهند. علت اين امر فشرده سازي زياد است.
الگوريتم zip مي تواند فشرده سازي را با نسبت هاي بسيار بالا انجام دهد. براي مثال يك فايل كه به صورت يك خط در ميان داراي يك خط حرف a و يك خط حرف b است، مي تواند به نسبت 200 به يك فشرده شود. نسبت هاي بالاتر فشرده سازي از طريق ورودي هايي كه براي الگوريتم zip هدفمند شده اند و يا ديگر روش هاي فشرده سازي قابل دسترس است.
هر ورودي كه فايلي را خارج از دايركتوري تعيين شده توسط برنامه، هدف قرار مي دهد (بعد از استاندارد سازي IDS02-J)، لازم است يا استخراج نشود و يا اين كار در يك محل امن انجام پذيرد.
لازم است جلوي نافشرده سازي هر ورودي يك فايل zip كه اندازه غيرفشرده آن بيش از حد مشخصي است، گرفته شود. اندازه حد تعيين شده بستگي به قابليت هاي سيستم دارد.

يك نمونه ناسازگار با قانون
اين نمونه ناسازگار با قانون در اعتبارسنجي نام فايلي كه بايد نافشرده سازي شود، شكست مي خورد. اين برنامه، نام فايل را مستقيماً به سازنده FileOutpitStream ارسال مي كند. اين برنامه همچنين مصرف منابع توسط فايل نافشرده شده را مورد بررسي قرار نمي دهد و اجازه تكميل فرآيند را تا زماني كه اجرا كامل شود و يا منابع محلي به بن بست برسند، مي دهد.

staticfinalintBUFFER = 512;

// ...
publicfinalvoidunzip(String filename) throwsjava.io.IOException{

FileInputStream fis = newFileInputStream(filename);

ZipInputStream zis = newZipInputStream(newBufferedInputStream(fis));

ZipEntry entry;

try{
while((entry = zis.getNextEntry()) != null) {

System.out.println("Extracting: "+ entry);

intcount;
bytedata[] = newbyte[BUFFER];

// Write the files to the disk

FileOutputStream fos = newFileOutputStream(entry.getName());

BufferedOutputStream dest = newBufferedOutputStream(fos, BUFFER);

while((count = zis.read(data, 0, BUFFER)) != -1) {

dest.write(data, 0, count);

}
dest.flush();
dest.close();
zis.closeEntry();
}
} finally{
zis.close();
}
}

راه حل سازگار با قانون
در اين راه حل سازگار برنامه قبل از استخراج هر ورودي، ابتدا نام آن را اعتبارسنجي مي نمايد. در صورتي كه نام نامعتبر باشد، كل عمليات استخراج متوقف مي شود. البته يك برنامه سازگار همچنين مي تواند از آن ورودي خاص صرف نظر كرده و به استخراج ديگر وردي ها ادامه دهد يا حتي مي تواند ورودي ناسازگار را در محلي امن استخراج نمايد.
علاوه بر اين، برنامه از طريق حلقه while، اندازه هر فايل نافشرده شده را در زمان استخراج هر ورودي مورد بررسي قرار مي دهد و زماني كه ورودي استخراج شده بيش از اندازه بزرگ باشد (در اينجا بيش از 100 مگابايت) يك استثناء توليد مي كند. در اينجا از ZipEntry.getsize() استفاده نمي شود زيرا مقدار گزارش شده توسط آن قابل اعتماد نيست. در انتها نيز برنامه تعداد فايلهاي ورودي در آرشيو را شمرده و در صورتي كه بيش از 1024 عدد باشند، يك استثناء را توليد مي نمايد.

staticfinalintBUFFER = 512;

staticfinalintTOOBIG = 0x6400000; // max size of unzipped data, 100MB

staticfinalintTOOMANY = 1024; // max number of files

// ...
privateString validateFilename(String filename, String intendedDir) {

File f = newFile(filename);

String canonicalPath = f.getCanonicalPath();

File iD = newFile(intendedDir);

String canonicalID = iD.getCanonicalPath();

if(canonicalPath.startsWith(canonicalID)) {

returncanonicalPath;
} else{
thrownewIllegalStateException("File is outside extraction target directory.");

}
}
publicfinalvoidunzip(String filename) throwsjava.io.IOException{

FileInputStream fis = newFileInputStream(filename);

ZipInputStream zis = newZipInputStream(newBufferedInputStream(fis));

ZipEntry entry;

intentries = 0;

inttotal = 0;

try{
while((entry = zis.getNextEntry()) != null) {

System.out.println("Extracting: "+ entry);

intcount;
bytedata[] = newbyte[BUFFER];

// Write the files to the disk, but ensure that the filename is valid,

// and that the file is not insanely big

String name = validateFilename(entry.getName(), ".");

FileOutputStream fos = newFileOutputStream(name);

BufferedOutputStream dest = newBufferedOutputStream(fos, BUFFER);

while(total <= TOOBIG && (count = zis.read(data, 0, BUFFER)) != -1) {

dest.write(data, 0, count);

total += count;

}
dest.flush();
dest.close();
zis.closeEntry();
entries++;
if(entries > TOOMANY) {

thrownewIllegalStateException("Too many files to unzip.");

}
if(total > TOOBIG) {

thrownewIllegalStateException("File being unzipped is too big.");

}
}
} finally{
zis.close();
}
}


مطالب مرتبط:
برنامه‌نويسي امن با زبان جاوا - آشنايي
برنامه‌نويسي امن با زبان جاوا – نشت اطلاعات حساس
برنامه‌نويسي امن با زبان جاوا – نشت قابليت ها
برنامه‌نويسي امن با زبان جاوا – انكار سرويس
برنامه‌نويسي امن با زبان جاوا – ارتقاي حق دسترسي
برنامه‌نويسي امن با زبان جاوا – قوانين اعتبارسنجي ورودي و پاكسازي داده‌ها – قانون IDS00-J – قسمت اول

برچسب‌ها