სესიის ID დაცვა PHP-ში. PHP-ში სესიების გამოყენების პრობლემები PHP სესიის გამოყენებით მნიშვნელობის ან მასივის გადაცემა

მოგესალმებით, ძვირფასო საზოგადოება.

პირველ რიგში, მინდა მადლობა გადაგიხადოთ ძალიან სასარგებლო რესურსისთვის. არაერთხელ ვიპოვე აქ ბევრი საინტერესო იდეა და პრაქტიკული რჩევა.

ამ სტატიის მიზანია ხაზი გავუსვა PHP-ში სესიების გამოყენების ხარვეზებს. რა თქმა უნდა, არსებობს PHP დოკუმენტაცია და უამრავი მაგალითი და ეს სტატია არ არის გამიზნული, რომ იყოს სრული სახელმძღვანელო. ის შექმნილია იმისთვის, რომ გამოავლინოს სესიებთან მუშაობის ზოგიერთი ნიუანსი და დაიცვას დეველოპერები დროის ზედმეტი დაკარგვისგან.

სესიების გამოყენების ყველაზე გავრცელებული მაგალითი, რა თქმა უნდა, მომხმარებლის ავტორიზაციაა. დავიწყოთ ყველაზე ძირითადი განხორციელებით, რათა თანდათან განვავითაროთ ახალი ამოცანების გაჩენისთანავე.

(სივრცისა და დროის დაზოგვის მიზნით, ჩვენ შევზღუდავთ ჩვენს მაგალითებს მხოლოდ სესიის ფუნქციებით, ნაცვლად იმისა, რომ აქ შევქმნათ სრულფასოვანი სატესტო აპლიკაცია ლამაზი კლასის იერარქიით, შეცდომების ყოვლისმომცველი დამუშავებით და სხვა კარგი ნივთებით).

ფუნქცია startSession() ( // თუ სესია უკვე დაწყებულია, შეაჩერეთ შესრულება და დააბრუნეთ TRUE // (php.ini პარამეტრების ფაილში session.auto_start პარამეტრი უნდა იყოს გამორთული - ნაგულისხმევი მნიშვნელობა) თუ (session_id()) დააბრუნებს true; else return session_start(); // შენიშვნა: 5.3.0 ვერსიამდე, session_start() ფუნქცია უბრუნდება TRUE, მაშინაც კი, თუ შეცდომა მოხდა. // თუ იყენებთ 5.3.0-მდე ვერსიას, შეასრულეთ დამატებითი შემოწმება session_id() // დარეკვის შემდეგ session_start() ) ფუნქციის განადგურებაSession() ( if (session_id()) ( // თუ არის აქტიური სესია, წაშალეთ სესიის ქუქიები, setcookie(session_name(), session_id(), time( )-60*60*24); // და გაანადგურე სესიის session_unset(); session_destroy(); ) )

შენიშვნა: ვარაუდობენ, რომ მკითხველს აქვს საბაზისო ცოდნა PHP სესიების შესახებ, ამიტომ ჩვენ აქ არ გავაშუქებთ session_start() და session_destroy() ფუნქციების მუშაობის პრინციპს. შესვლის ფორმის განლაგებისა და მომხმარებლის ავტორიზაციის ამოცანები არ არის დაკავშირებული სტატიის თემასთან, ამიტომ მათ ასევე გამოვტოვებთ. შეგახსენებთ, რომ მომხმარებლის იდენტიფიცირებისთვის ყოველი მომდევნო მოთხოვნაში, წარმატებული შესვლის მომენტში, ჩვენ უნდა შევინახოთ მომხმარებლის იდენტიფიკატორი სესიის ცვლადში (მაგალითად, სახელწოდებით userid), რომელიც ხელმისაწვდომი იქნება ყველა მომდევნო მოთხოვნაში. სესიის ცხოვრება. ასევე აუცილებელია ჩვენი startSession() ფუნქციის შედეგის დამუშავება. თუ ფუნქცია დააბრუნებს FALSE-ს, ბრაუზერში აჩვენეთ შესვლის ფორმა. თუ ფუნქციამ დააბრუნა TRUE, და სესიის ცვლადი, რომელიც შეიცავს ავტორიზებული მომხმარებლის იდენტიფიკატორს (ჩვენს შემთხვევაში - userid), არსებობს - აჩვენეთ ავტორიზებული მომხმარებლის გვერდი (შეცდომის დამუშავების შესახებ დამატებითი ინფორმაციისთვის იხილეთ დამატება 2013-06-ით. 07 სესიის ცვლადების განყოფილებაში).

ჯერჯერობით ყველაფერი ნათელია. კითხვები იწყება მაშინ, როდესაც გჭირდებათ მომხმარებლის უმოქმედობის კონტროლის დანერგვა (სესიის ვადის ამოწურვა), ნება დართეთ რამდენიმე მომხმარებელს ერთდროულად იმუშაონ ერთ ბრაუზერში და ასევე დაიცვათ სესიები არაავტორიზებული გამოყენებისგან. ეს ქვემოთ იქნება განხილული.

მომხმარებლის უმოქმედობის კონტროლი ჩაშენებული PHP ინსტრუმენტების გამოყენებით პირველი კითხვა, რომელიც ხშირად ჩნდება მომხმარებლებისთვის სხვადასხვა კონსოლების დეველოპერებს შორის, არის სესიის ავტომატური შეწყვეტა მომხმარებლის მხრიდან უმოქმედობის შემთხვევაში. არაფერია ადვილი, ვიდრე ამის გაკეთება PHP-ის ჩაშენებული შესაძლებლობების გამოყენებით. (ეს ვარიანტი არ არის განსაკუთრებით სანდო ან მოქნილი, მაგრამ ჩვენ განვიხილავთ მას სისრულისთვის).

ფუნქცია startSession() ( // მომხმარებლის უმოქმედობის ვადა (წამებში) $sessionLifetime = 300; if (session_id()) დაბრუნდება true; // დააყენეთ ქუქი-ფაილის სიცოცხლის ხანგრძლივობა ini_set("session.cookie_lifetime", $sessionLifetime); // თუ მომხმარებელი უმოქმედობის დრო დაყენებულია, დააყენეთ სესიის ხანგრძლივობა სერვერზე // შენიშვნა: წარმოების სერვერისთვის რეკომენდებულია ამ პარამეტრების წინასწარ დაყენება php.ini ფაილში, თუ ($sessionLifetime) ini_set("session.gc_maxlifetime", $sessionLifetime) ; if (session_start( )) (setcookie(session_name(), session_id(), time()+$sessionLifetime); დაბრუნება true; ) სხვა შემთხვევაში დაბრუნება false; )

რამდენიმე განმარტება. მოგეხსენებათ, PHP განსაზღვრავს რომელი სესიის გაშვებას საჭიროებს ბრაუზერის მიერ გაგზავნილი ქუქიების სახელით მოთხოვნის სათაურში. ბრაუზერი, თავის მხრივ, იღებს ამ ქუქი-ფაილს სერვერიდან, სადაც მას ათავსებს session_start() ფუნქცია. თუ ბრაუზერის ქუქი-ფაილს ვადა გაუვიდა, ის არ გაიგზავნება მოთხოვნაში, რაც ნიშნავს, რომ PHP ვერ განსაზღვრავს რომელი სესიის დაწყებას და ამას განიხილავს როგორც ახალი სესიის შექმნას. PHP პარამეტრების პარამეტრი session.gc_maxlifetime, რომელიც დაყენებულია ჩვენი მომხმარებლის უმოქმედობის ვადის ტოლფასად, ადგენს PHP სესიის ხანგრძლივობას და აკონტროლებს სერვერს. სესიის სიცოცხლის ხანგრძლივობის კონტროლი მუშაობს შემდეგნაირად (აქ განვიხილავთ სესიების შენახვის მაგალითს დროებით ფაილებში, როგორც ყველაზე გავრცელებულ და ნაგულისხმევ ვარიანტს PHP-ში).

როდესაც იქმნება ახალი სესია, ფაილი სახელად sess_ იქმნება დირექტორიაში დაყენებულ დირექტორიაში სესიების შესანახად PHP პარამეტრების პარამეტრში session.save_path, სადაც არის სესიის იდენტიფიკატორი. შემდეგ, ყოველ მოთხოვნაში, უკვე არსებული სესიის დაწყების დროს, PHP აახლებს ამ ფაილის მოდიფიკაციის დროს. ამრიგად, ყოველი მომდევნო მოთხოვნისას, PHP-ს, მიმდინარე დროისა და სესიის ფაილის ბოლო მოდიფიკაციის დროს შორის სხვაობის მიხედვით, შეუძლია განსაზღვროს სესია აქტიურია თუ მისი სიცოცხლის ვადა უკვე ამოიწურა. (ძველი სესიის ფაილების წაშლის მექანიზმი უფრო დეტალურად განიხილება შემდეგ ნაწილში.)

შენიშვნა: აქვე უნდა აღინიშნოს, რომ session.gc_maxlifetime პარამეტრი ვრცელდება ყველა სესიაზე ერთ სერვერზე (უფრო ზუსტად, ერთი ძირითადი PHP პროცესის ფარგლებში). პრაქტიკაში, ეს ნიშნავს, რომ თუ რამდენიმე საიტი მუშაობს სერვერზე და თითოეულ მათგანს აქვს მომხმარებლის უმოქმედობის დრო, მაშინ ამ პარამეტრის დაყენება ერთ-ერთ საიტზე გამოიწვევს მის დაყენებას სხვა საიტებისთვის. იგივე ეხება საერთო ჰოსტინგს. ამ სიტუაციის თავიდან ასაცილებლად, ცალკეული სესიების დირექტორიები გამოიყენება თითოეული საიტისთვის იმავე სერვერზე. სესიების დირექტორიაში გზის დაყენება ხდება session.save_path პარამეტრის გამოყენებით php.ini პარამეტრების ფაილში, ან ini_set() ფუნქციის გამოძახებით. ამის შემდეგ, თითოეული საიტის სესიები შეინახება ცალკეულ დირექტორიაში და ერთ-ერთ საიტზე მითითებული session.gc_maxlifetime პარამეტრი მხოლოდ მისი სესიისთვის იქნება მოქმედი. ჩვენ არ განვიხილავთ ამ შემთხვევას დეტალურად, მით უმეტეს, რომ ჩვენ გვაქვს უფრო მოქნილი ვარიანტი მომხმარებლის უმოქმედობის მონიტორინგისთვის.

მომხმარებლის უმოქმედობის კონტროლი სესიის ცვლადების გამოყენებით, როგორც ჩანს, წინა ვარიანტი, მთელი თავისი სიმარტივით (კოდის მხოლოდ რამდენიმე დამატებითი ხაზი), გვაძლევს ყველაფერს, რაც გვჭირდება. მაგრამ რა მოხდება, თუ ყველა მოთხოვნა არ შეიძლება ჩაითვალოს მომხმარებლის აქტივობის შედეგად? მაგალითად, გვერდს აქვს ტაიმერი, რომელიც პერიოდულად აკეთებს AJAX მოთხოვნას სერვერისგან განახლებების მისაღებად. ასეთი მოთხოვნა არ შეიძლება ჩაითვალოს მომხმარებლის აქტივობად, რაც ნიშნავს, რომ სესიის ხანგრძლივობის ავტომატურად გახანგრძლივება ამ შემთხვევაში არასწორია. მაგრამ ჩვენ ვიცით, რომ PHP ავტომატურად ანახლებს სესიის ფაილის მოდიფიკაციის დროს ყოველ ჯერზე, როდესაც გამოიძახება session_start() ფუნქცია, რაც ნიშნავს, რომ ნებისმიერი მოთხოვნა გამოიწვევს სესიის სიცოცხლის ხანგრძლივობის გაგრძელებას და მომხმარებლის უმოქმედობის დრო არასოდეს მოხდება. გარდა ამისა, წინა განყოფილების ბოლო შენიშვნა სესია.gc_maxlifetime პარამეტრის სირთულეების შესახებ შეიძლება ზედმეტად დამაბნეველი და რთული განსახორციელებელი ჩანდეს ზოგიერთისთვის.

ამ პრობლემის გადასაჭრელად, ჩვენ უარს ვიტყვით ჩაშენებული PHP მექანიზმების გამოყენებაზე და შემოგთავაზებთ სესიის რამდენიმე ახალ ცვლადს, რომელიც საშუალებას მოგვცემს თავად გავაკონტროლოთ მომხმარებლის უმოქმედობის დრო.

ფუნქცია startSession($isUserActivity=true) ($sessionLifetime = 300; if (session_id()) დაბრუნდება true; // დააყენეთ ქუქი-ფაილის სიცოცხლე ბრაუზერის დახურვამდე (ჩვენ გავაკონტროლებთ ყველაფერს სერვერის მხარეს) ini_set("session. cookie_lifetime", 0) ; if (! session_start()) return false; $t = time(); if ($sessionLifetime) ( // თუ მომხმარებლის უმოქმედობის ვადა დაყენებულია, // შეამოწმეთ მომხმარებლის ბოლო აქტივობიდან გასული დრო // (ბოლო მოთხოვნის დრო, როდესაც განახლდა ბოლო აქტივობის სესიის ცვლადი) if (isset($_SESSION["lastactivity"]) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) ( // თუ დრო გავიდა მას შემდეგ მომხმარებლის ბოლო აქტივობა, // მეტია უმოქმედობის ვადაზე, რაც ნიშნავს, რომ სესიას ამოიწურა და თქვენ უნდა შეწყვიტოთ სესია deathSession(); return false; ) else ( // თუ ვადა ჯერ არ დასრულებულა, // და თუ მოთხოვნა მოვიდა მომხმარებლის აქტივობის შედეგად, // განაახლეთ ბოლო აქტივობის ცვლადი მიმდინარე ერთი დროის მნიშვნელობით, // ამით გაახანგრძლივეთ სესიის დრო სხვა sessionLifetime წამით, თუ ($isUserActivity) $_SESSION["lastactivity"] = $t; ) ) დაბრუნება true; )

შევაჯამოთ. თითოეულ მოთხოვნაში ვამოწმებთ, მიღწეულია თუ არა დროის ამოწურვა მომხმარებლის ბოლო აქტივობიდან მიმდინარე მომენტამდე და თუ მიღწეულია, ვანადგურებთ სესიას და ვწყვეტთ ფუნქციის შესრულებას, ვაბრუნებთ FALSE-ს. თუ დრო არ არის მიღწეული და $isUserActivity პარამეტრი TRUE მნიშვნელობით გადაეცემა ფუნქციას, ჩვენ ვაახლებთ მომხმარებლის ბოლო აქტივობის დროს. საკმარისია გამოძახების სკრიპტში განვსაზღვროთ, არის თუ არა მოთხოვნა მომხმარებლის აქტივობის შედეგი და თუ არა, გამოვიძახოთ startSession ფუნქცია $isUserActivity პარამეტრით დაყენებული FALSE.

დამატება 2013-06-07-დან სესიაStart() ფუნქციის შედეგის დამუშავება

კომენტარებში აღნიშნულია, რომ FALSE-ის დაბრუნება არ იძლევა შეცდომის მიზეზის სრულ გაგებას და ეს აბსოლუტურად სამართლიანია. მე არ გამოვაქვეყნე დეტალური შეცდომების დამუშავება აქ (სტატიის სიგრძე უკვე საკმაოდ დიდია), რადგან ეს პირდაპირ არ არის დაკავშირებული სტატიის თემასთან. მაგრამ კომენტარებიდან გამომდინარე, დავაზუსტებ.

როგორც ხედავთ, sessionStart ფუნქციას შეუძლია დააბრუნოს FALSE ორ შემთხვევაში. ან სესიის დაწყება ვერ მოხერხდა ზოგიერთი შიდა სერვერის შეცდომის გამო (მაგალითად, არასწორი სესიის პარამეტრები php.ini-ში), ან სესიის ვადა ამოიწურა. პირველ შემთხვევაში, ჩვენ უნდა გადამისამართოთ მომხმარებელი შეცდომით გვერდზე, რომელშიც ნათქვამია, რომ სერვერზე პრობლემებია და მხარდაჭერასთან დაკავშირების ფორმა. მეორე შემთხვევაში, მომხმარებელი უნდა გადავიტანოთ შესვლის ფორმაში და მასში გამოვაჩინოთ შესაბამისი შეტყობინება, რომ სესიას ვადა გაუვიდა. ამისათვის უნდა შევიტანოთ შეცდომის კოდები და FALSE-ის ნაცვლად დავაბრუნოთ შესაბამისი კოდი და გამოძახების მეთოდში შევამოწმოთ და შესაბამისად ვიმოქმედოთ.

ახლა, მაშინაც კი, თუ სესია სერვერზე ჯერ კიდევ არსებობს, ის განადგურდება პირველად მასზე წვდომისას, თუ მომხმარებლის უმოქმედობის ვადა ამოიწურა. და ეს მოხდება მიუხედავად იმისა, თუ რა სესიის ხანგრძლივობაა დაყენებული გლობალურ PHP პარამეტრებში.

შენიშვნა: რა მოხდება, თუ ბრაუზერი დაიხურა და სესიის სახელის ქუქი ავტომატურად განადგურდა? სერვერის მოთხოვნა ბრაუზერის შემდეგ გახსნისას არ შეიცავს სესიის ქუქი-ფაილს და სერვერი ვერ შეძლებს სესიის გახსნას და მომხმარებლის უმოქმედობის დროის ამოწურვის შემოწმებას. ჩვენთვის ეს ახალი სესიის შექმნის ტოლფასია და არანაირად არ მოქმედებს ფუნქციონირებაზე ან უსაფრთხოებაზე. მაგრამ ჩნდება სამართლიანი კითხვა - ვინ გაანადგურებს შემდეგ ძველ სესიას, თუ აქამდე ვანგრევდით მას ვადის ამოწურვის შემდეგ? ან ის ახლა სამუდამოდ დაკიდება სესიების დირექტორიაში? PHP-ში ძველი სესიების გასასუფთავებლად არსებობს მექანიზმი, რომელსაც ნაგვის შეგროვება ეწოდება. ის მუშაობს სერვერზე შემდეგი მოთხოვნის დროს და ასუფთავებს ყველა ძველ სესიას სესიის ფაილების ბოლო ცვლილების თარიღზე დაყრდნობით. მაგრამ ნაგვის შეგროვების მექანიზმი არ იწყება სერვერზე ყველა მოთხოვნით. გაშვების სიხშირე (უფრო სწორად, ალბათობა) განისაზღვრება ორი პარამეტრის პარამეტრებით session.gc_probability და session.gc_divisor. პირველი პარამეტრის მეორეზე გაყოფის შედეგია ნაგვის შეგროვების მექანიზმის ამოქმედების ალბათობა. ამრიგად, იმისათვის, რომ სესიის გაწმენდის მექანიზმი ამოქმედდეს სერვერზე ყოველი მოთხოვნით, ეს პარამეტრები უნდა იყოს დაყენებული თანაბარ მნიშვნელობებზე, მაგალითად "1". ეს მიდგომა უზრუნველყოფს სუფთა სესიის დირექტორიას, მაგრამ აშკარად ძალიან ძვირია სერვერისთვის. ამიტომ, წარმოების სისტემებზე, session.gc_divisor-ის ნაგულისხმევი მნიშვნელობა დაყენებულია 1000-ზე, რაც ნიშნავს, რომ ნაგვის შეგროვების მექანიზმი იმუშავებს 1/1000 ალბათობით. თუ თქვენს php.ini ფაილში ამ პარამეტრებს ატარებთ ექსპერიმენტებს, შეიძლება შეამჩნიოთ, რომ ზემოთ აღწერილ შემთხვევაში, როდესაც ბრაუზერი იხურება და ასუფთავებს ყველა ქუქი-ფაილს, ცოტა ხნით ისევ რჩება ძველი სესიები სესიების დირექტორიაში. მაგრამ ეს არ უნდა შეგაწუხოთ, რადგან... როგორც უკვე აღვნიშნეთ, ეს არანაირად არ იმოქმედებს ჩვენი მექანიზმის უსაფრთხოებაზე.

განახლება 2013-06-07 სკრიპტების გაყინვის თავიდან აცილება სესიის ფაილის დაბლოკვის გამო

კომენტარებმა წამოჭრეს სკრიპტების ერთდროულად გაშვების საკითხი სესიის ფაილის დაბლოკვის გამო (ყველაზე გასაოცარი ვარიანტი გრძელი გამოკითხვაა).

დასაწყისისთვის, მე აღვნიშნავ, რომ ეს პრობლემა პირდაპირ არ არის დამოკიდებული სერვერის დატვირთვაზე ან მომხმარებელთა რაოდენობაზე. რა თქმა უნდა, რაც მეტი მოთხოვნაა, მით უფრო ნელა სრულდება სკრიპტები. მაგრამ ეს არაპირდაპირი დამოკიდებულებაა. პრობლემა ჩნდება მხოლოდ ერთი სესიის განმავლობაში, როდესაც სერვერი იღებს რამდენიმე მოთხოვნას ერთი მომხმარებლის სახელით (მაგალითად, ერთი მათგანი გრძელი გამოკითხვაა, დანარჩენი კი რეგულარული მოთხოვნაა). თითოეული მოთხოვნა ცდილობს წვდომას ერთი და იგივე სესიის ფაილზე და თუ წინა მოთხოვნამ არ განბლოკა ფაილი, მაშინ შემდგომი მოლოდინში იქნება.

სესიის ფაილის ჩაკეტვის მინიმუმამდე შესანარჩუნებლად, მკაცრად რეკომენდირებულია სესიის დახურვა session_write_close() ფუნქციის გამოძახებით დაუყოვნებლივ მას შემდეგ, რაც სესიის ცვლადებთან დაკავშირებული ყველა მოქმედება დასრულდება. პრაქტიკაში, ეს ნიშნავს, რომ თქვენ არ უნდა შეინახოთ ყველაფერი სესიის ცვლადებში და შეხვიდეთ მათზე სკრიპტის შესრულების განმავლობაში. და თუ გჭირდებათ გარკვეული სამუშაო მონაცემების შენახვა სესიის ცვლადებში, მაშინ წაიკითხეთ ისინი მაშინვე, როდესაც სესია დაიწყება, შეინახეთ ისინი ლოკალურ ცვლადებში შემდგომი გამოყენებისთვის და დახურეთ სესია (იგულისხმება სესიის დახურვა session_write_close ფუნქციის გამოყენებით და არა მისი განადგურება session_destroy-ის გამოყენებით. ).

ჩვენს მაგალითში ეს ნიშნავს, რომ სესიის გახსნისთანავე, მისი სიცოცხლის ხანგრძლივობის და ავტორიზებული მომხმარებლის არსებობის შემოწმებისთანავე, ჩვენ უნდა წავიკითხოთ და შევინახოთ აპლიკაციის მიერ მოთხოვნილი ყველა დამატებითი სესიის ცვლადი (ასეთის არსებობის შემთხვევაში), შემდეგ დავხუროთ სესია ზარის გამოყენებით session_write_close() და გააგრძელეთ სკრიპტის შესრულება, იქნება ეს ხანგრძლივი გამოკითხვა თუ რეგულარული მოთხოვნა.

სესიების დაცვა არაავტორიზებული გამოყენებისგან მოდით წარმოვიდგინოთ სიტუაცია. თქვენი ერთ-ერთი მომხმარებელი იღებს ტროას, რომელიც ძარცვავს ბრაუზერის ქუქი-ფაილებს (რომელშიც ჩვენი სესია ინახება) და აგზავნის მითითებულ ელფოსტაზე. თავდამსხმელი იღებს ქუქი-ფაილს და იყენებს მას ჩვენი ავტორიზებული მომხმარებლის სახელით მოთხოვნის გასაყალბებლად. სერვერი წარმატებით იღებს და ამუშავებს ამ მოთხოვნას, თითქოს ის ავტორიზებული მომხმარებლისგან იყოს. თუ IP მისამართის დამატებითი გადამოწმება არ განხორციელდება, ასეთი შეტევა გამოიწვევს მომხმარებლის ანგარიშის წარმატებულ გატეხვას ყველა შემდგომი შედეგით.

რატომ იყო ეს შესაძლებელი? ცხადია, რადგან სახელი და სესიის იდენტიფიკატორი ყოველთვის ერთი და იგივეა სესიის მთელი სიცოცხლის განმავლობაში და თუ ამ მონაცემებს მიიღებთ, შეგიძლიათ მარტივად გაგზავნოთ მოთხოვნები სხვა მომხმარებლის სახელით (რა თქმა უნდა, ამ სესიის განმავლობაში). ეს შეიძლება არ იყოს ყველაზე გავრცელებული ტიპის თავდასხმა, მაგრამ თეორიულად, როგორც ჩანს, სავსებით შესაძლებელია, განსაკუთრებით იმის გათვალისწინებით, რომ ასეთ ტროას ადმინისტრატორის უფლებებიც კი არ სჭირდება მომხმარებლის ბრაუზერის ქუქი-ფაილების მოსაპარად.

როგორ შეგიძლიათ დაიცვათ თავი ამ ტიპის თავდასხმებისგან? ისევ, ცხადია, სესიის იდენტიფიკატორის სიცოცხლის ხანგრძლივობის შეზღუდვით და იმავე სესიის ფარგლებში იდენტიფიკატორის პერიოდული შეცვლით. ჩვენ ასევე შეგვიძლია შევცვალოთ სესიის სახელი ძველის მთლიანად წაშლით და ახალი სესიის შექმნით, სესიის ყველა ცვლადის ძველიდან მასში კოპირებით. მაგრამ ეს არ იმოქმედებს მიდგომის არსზე, ამიტომ სიმარტივისთვის ჩვენ შემოვიფარგლებით მხოლოდ სესიის იდენტიფიკატორით.

ნათელია, რომ რაც უფრო მოკლეა სესიის ID-ის სიცოცხლე, მით ნაკლები დრო მოუწევს თავდამსხმელს ქუქიების მოპოვება და გამოყენება მომხმარებლის მოთხოვნის გასაყალბებლად. იდეალურ შემთხვევაში, ყოველი მოთხოვნისთვის გამოყენებული უნდა იყოს ახალი იდენტიფიკატორი, რაც მინიმუმამდე დააყენებს სხვისი სესიის გამოყენების შესაძლებლობას. მაგრამ ჩვენ განვიხილავთ ზოგად შემთხვევას, როდესაც სესიის იდენტიფიკატორის რეგენერაციის დრო თვითნებურად არის დაყენებული.

(გამოვტოვებთ კოდის იმ ნაწილს, რომელიც უკვე იყო განხილული).

ფუნქცია startSession($isUserActivity=true) (// სესიის იდენტიფიკატორის სიცოცხლის ხანგრძლივობა $idLifetime = 60; ... if ($idLifetime) ( // თუ სესიის იდენტიფიკატორის სიცოცხლის ხანგრძლივობა დაყენებულია, // შეამოწმეთ სესიის დასრულებიდან გასული დრო შეიქმნა ან ბოლო რეგენერაცია // (ბოლო მოთხოვნის დრო, როდესაც განახლდა სესიის ცვლადი დაწყების დრო) if (isset($_SESSION["starttime"])) (if ($t-$_SESSION["starttime"] >= $ idLifetime) ( // სესიის იდენტიფიკატორის ვადის გასვლის დრო // ახალი იდენტიფიკატორის გენერირება session_regenerate_id(true); $_SESSION["starttime"] = $t; ) ) სხვა ( // აქ მივიღებთ, თუ სესიას ახლახან აქვს შეიქმნა // დააყენეთ სესიის იდენტიფიკატორის გენერირების დრო მიმდინარე დროზე $_SESSION["starttime"] = $t; ) ) return true; )

ასე რომ, ახალი სესიის შექმნისას (რაც ხდება მომხმარებლის წარმატებით შესვლისას), ჩვენ ვაყენებთ სესიის ცვლად დაწყებას, რომელიც ინახავს ჩვენთვის სესიის იდენტიფიკატორის ბოლო თაობის დროს, მნიშვნელობით, რომელიც ტოლია სერვერის მიმდინარე დროს. შემდეგ, თითოეულ მოთხოვნაში, ჩვენ ვამოწმებთ, გავიდა თუ არა საკმარისი დრო (idLifetime) იდენტიფიკატორის ბოლო თაობიდან და თუ ასეა, ვაგენერირებთ ახალს. ამრიგად, თუ იდენტიფიკატორის დაყენებული მოქმედების მანძილზე თავდამსხმელს, რომელმაც მიიღო ავტორიზებული მომხმარებლის ქუქი-ფაილები, არ ექნება დრო, გამოიყენოს იგი, ყალბი მოთხოვნა სერვერის მიერ ჩაითვლება არაავტორიზებულად და თავდამსხმელი გადაიყვანება შესვლის გვერდზე. .

შენიშვნა: ახალი სესიის ID ხვდება ბრაუზერის ქუქი-ფაილში, როდესაც გამოიძახება session_regenerate_id() ფუნქცია, რომელიც აგზავნის ახალ ქუქი-ფაილს, ფუნქციის session_start() მსგავსი, ასე რომ, ჩვენ არ გვჭირდება ქუქი-ფაილის განახლება.

თუ გვინდა, რომ ჩვენი სესიები მაქსიმალურად უსაფრთხო გავხადოთ, საკმარისია დავაყენოთ იდენტიფიკატორის სიცოცხლის ხანგრძლივობა ერთი ან თუნდაც ამოიღოთ session_regenerate_id() ფუნქცია ფრჩხილებიდან და ამოიღოთ ყველა შემოწმება, რაც გამოიწვევს იდენტიფიკატორის რეგენერაციას თითოეულში. მოთხოვნა. (მე არ გამომიცდია ამ მიდგომის გავლენა შესრულებაზე და მხოლოდ იმის თქმა შემიძლია, რომ session_regenerate_id(true) ფუნქცია არსებითად ასრულებს მხოლოდ 4 მოქმედებას: ახალი იდენტიფიკატორის გენერირება, სათაურის შექმნა სესიის ქუქი-ფაილით, ძველის წაშლა და შექმნა. ახალი სესიის ფაილი).

ლირიკული დიგრესია: თუ ტროას ისეთი ჭკვიანი აღმოჩნდება, რომ არ უგზავნის ქუქი-ფაილებს თავდამსხმელს, მაგრამ აწყობს წინასწარ მომზადებული ყალბი მოთხოვნის გაგზავნას ქუქი-ფაილის მიღებისთანავე, ზემოთ აღწერილი მეთოდი დიდი ალბათობით ვერ შეძლებს დაიცავით ასეთი შეტევისგან, რადგან ტროას მიერ ქუქი-ფაილის მიღებასა და ყალბი მოთხოვნის გაგზავნას შორის პრაქტიკულად არანაირი განსხვავება არ იქნება და დიდია ალბათობა იმისა, რომ ამ მომენტში სესიის იდენტიფიკატორი არ აღდგება.

ერთ ბრაუზერში რამდენიმე მომხმარებლის სახელით ერთდროული მუშაობის შესაძლებლობა ბოლო ამოცანა, რომელიც მინდა განვიხილო არის ერთ ბრაუზერში რამდენიმე მომხმარებლის მიერ ერთდროული მუშაობის შესაძლებლობა. ეს ფუნქცია განსაკუთრებით სასარგებლოა ტესტირების ეტაპზე, როდესაც გჭირდებათ მომხმარებლების ერთდროული მუშაობის ემულაცია და მიზანშეწონილია ამის გაკეთება თქვენს საყვარელ ბრაუზერში, ვიდრე გამოიყენოთ მთელი ხელმისაწვდომი არსენალი ან ბრაუზერის რამდენიმე ინსტანციის გახსნა ინკოგნიტო რეჟიმში. .

ჩვენს წინა მაგალითებში, ჩვენ აშკარად არ დავაკონკრეტეთ სესიის სახელი, ამიტომ გამოყენებული იყო ნაგულისხმევი PHP სახელი (PHPSESSID). ეს ნიშნავს, რომ ყველა სესიამ, რომელიც ჩვენ აქამდე შევქმენით, ბრაუზერში გაგზავნილია ქუქი-ფაილი სახელწოდებით PHPSESSID. ცხადია, თუ ქუქი-ფაილის სახელი ყოველთვის ერთი და იგივეა, მაშინ არ არსებობს გზა ერთი და იგივე სახელით ორი სესიის ორგანიზება იმავე ბრაუზერში. მაგრამ თუ გამოვიყენებდით ჩვენი სესიის სახელს თითოეული მომხმარებლისთვის, პრობლემა მოგვარდება. მოდი ასე მოვიქცეთ.

ფუნქცია startSession($isUserActivity=true, $prefix=null) (... if (session_id()) დააბრუნებს true; // თუ მომხმარებლის პრეფიქსი გადაცემულია პარამეტრებში, // დააყენეთ უნიკალური სესიის სახელი, რომელიც მოიცავს ამას პრეფიქსი, // სხვაგვარად დააყენეთ საერთო სახელი ყველა მომხმარებლისთვის (მაგალითად, MYPROJECT) session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set("session.cookie_lifetime", 0); თუ (! session_start()) დააბრუნებს false; ...)

ახლა რჩება მხოლოდ დავრწმუნდეთ, რომ გამოძახების სკრიპტი გადასცემს უნიკალურ პრეფიქსს თითოეული მომხმარებლისთვის startSession() ფუნქციისთვის. ეს შეიძლება გაკეთდეს, მაგალითად, თითოეული მოთხოვნის GET/POST პარამეტრებში პრეფიქსის გადაცემით ან დამატებითი ქუქი-ფაილის მეშვეობით.

დასკვნა დასასრულს, მე შემოგთავაზებთ ჩვენი ფუნქციების სრულ საბოლოო კოდს PHP სესიებთან მუშაობისთვის, ზემოთ განხილული ყველა ამოცანის ჩათვლით.

ფუნქცია startSession($isUserActivity=true, $prefix=null) ( $sessionLifetime = 300; $idLifetime = 60; if (session_id()) დააბრუნებს true; session_name("MYPROJECT".($prefix ? "_".$prefix: "")); ini_set ("session.cookie_lifetime", 0); if (! session_start()) დააბრუნებს false; $t = დრო(); if ($sessionLifetime) (if (isset($_SESSION["lastactivity"] ) && $t-$_SESSION["lastactivity"] >= $sessionLifetime) (structSession(); return false; ) other ( if ($isUserActivity) $_SESSION["lastactivity"] = $t; ) ) თუ ($idLifetime ) ( if (isset($_SESSION["დაწყების დრო"])) (if ($t-$_SESSION["starttime"] >= $idLifetime) ( session_regenerate_id(true); $_SESSION["starttime"] = $t; ) ) else ($_SESSION["დაწყების დრო"] = $t; ) ) დააბრუნებს true; ) ფუნქცია deathSession() (if (session_id()) ( session_unset(); setcookie(session_name(), session_id(), time() -60*60*24); session_destroy(); ) )

ვიმედოვნებ, რომ ეს სტატია დაზოგავს დროს მათ, ვინც არასდროს ჩაუღრმავდა სესიის მექანიზმს და საკმარისად მისცემს ამ მექანიზმს მათთვის, ვინც ახლახან იწყებს PHP-ის გაცნობას.

გჭირდებათ მომხმარებლის სახელი და პაროლი?

სტატიების ონლაინ გასაგზავნად და გაგზავნილი სტატიების სტატუსის შესამოწმებლად, თქვენ უნდა დარეგისტრირდეთ და შეხვიდეთ თქვენს ანგარიშზე.

საკონტროლო სია სტატიის დასამზადებლად

როგორც სტატიის წარდგენის პროცესის ნაწილი, ავტორებმა უნდა შეამოწმონ, რომ მათი სტატია აკმაყოფილებს ყველა შემდეგ პუნქტს; სტატიები შეიძლება დაუბრუნდეს ავტორებს, თუ ისინი არ აკმაყოფილებენ ამ მოთხოვნებს.

სტატია მომზადებულია მოთხოვნების შესაბამისად

საავტორო უფლებების გადაცემის პირობები

ავტორები ინარჩუნებენ საავტორო უფლებებს ნამუშევარზე და ანიჭებენ ჟურნალს პირველი გამოქვეყნების უფლებებს ნაწარმოებთან ერთად, ხოლო ლიცენზირდებიან Creative Commons Attribution License პირობებით, რაც საშუალებას აძლევს სხვებს გაავრცელონ ეს ნამუშევარი სავალდებულო მინიჭებით ნაწარმოების ავტორისთვის და ბმული. ამ ჟურნალის თავდაპირველ პუბლიკაციაზე.

კონფიდენციალურობის განცხადება

ამ ჟურნალის ვებსაიტზე შეტანილი სახელები და ელ.ფოსტის მისამართები გამოყენებული იქნება მხოლოდ ამ ჟურნალის მიერ განსაზღვრული მიზნებისთვის და არ იქნება გამოყენებული სხვა მიზნებისთვის ან მიწოდებული სხვა პირისთვის ან ერთეულისთვის.

სისტემაში დარეგისტრირებამდე მომხმარებელი ეთანხმება პერსონალური მონაცემების დამუშავებისა და შენახვის პოლიტიკას.

ავტორის გადახდები

1500 სიმბოლო ინტერვალით: 300.00 (RUB)

ხელნაწერის 1 გვერდის გამოცემა (1500 სიმბოლო) - 300 მანეთი. გრაფიკული მასალები/მაგიდები გადახდილია ცალკე - 50 რუბლი/1 ცალი. ავტორის ასლი, რუსეთში მიწოდების ჩათვლით, გადახდილია ავტორის მოთხოვნით - 400 რუბლი. მიწოდება საზღვარგარეთ - 800 რუბლი. მასალის მიღების მოწმობის გაგზავნის ღირებულება გამოქვეყნებისთვის არის 150 რუბლი.

თანმხლები ინფორმაციის (სრული სახელი, ავტორების მუშაობის ადგილი; სათაური; რეზიუმე; საკვანძო სიტყვები) თარგმნა ინგლისურად 0,5 რუბლი თითოეული სიმბოლოსთვის, მათ შორის სივრცეები.

ყურადღება! ავტორები (კანდიდატები და მეცნიერებათა დოქტორები), რომლებსაც, elibrary.ru-ს მიხედვით, აქვთ 300 ან მეტი ციტატა (თვითციტატების წილი უნდა იყოს არაუმეტეს 30%), ქვეყნდება უფასოდ. თუ თქვენ გაქვთ უფასო გამოქვეყნების უფლება, მასალის გაგზავნისას კომენტარების ველში მიუთითეთ ბმული თქვენი ბიბლიოთეკის პროფილზე ციტატების რაოდენობით. კოლექციისთვის ტრანსპორტირების ხარჯები იხდის ცალკე.

ვებსაიტის უსაფრთხოება ეფუძნება სესიების მენეჯმენტს. როდესაც მომხმარებელი უერთდება უსაფრთხო საიტს, ისინი აწვდიან სერთიფიკატებს, როგორც წესი, მომხმარებლის სახელისა და პაროლის სახით. ვებ სერვერს წარმოდგენა არ აქვს, რომელი მომხმარებელია უკვე შესული ან როგორ გადაადგილდებიან გვერდიდან გვერდზე. სესიის მექანიზმი ხელს უშლის მომხმარებლებს პაროლის შეყვანა ყოველ ჯერზე, როდესაც მათ სურთ შეასრულონ ახალი მოქმედება ან გადავიდნენ ახალ გვერდზე.

არსებითად, სესიის მენეჯმენტი უზრუნველყოფს, რომ ამჟამად დაკავშირებული მომხმარებელი არის ის, ვინც დამოწმებულია. სამწუხაროდ, სესიები გახდა აშკარა სამიზნე ჰაკერებისთვის, რადგან მათ შეუძლიათ დაუშვან ვებ სერვერზე წვდომა ავთენტიფიკაციის საჭიროების გარეშე.

მომხმარებლის ავტორიზაციის შემდეგ, ვებ სერვერი მას აძლევს სესიის ID-ს. ეს ID ინახება ბრაუზერში და მისი ჩანაცვლება ხდება, როცა საჭიროა ავტორიზაცია. ეს საშუალებას გაძლევთ თავიდან აიცილოთ განმეორებითი შესვლის/პაროლის შეყვანის პროცესები. ეს ყველაფერი ფონზე ხდება და მომხმარებლისთვის დისკომფორტს არ უქმნის. წარმოიდგინეთ, თუ შეიყვანეთ თქვენი მომხმარებლის სახელი და პაროლი ყოველ ჯერზე, როდესაც ნახულობთ ახალ გვერდს!

ამ სტატიაში შევეცდები გამოვყო ყველა გზა, რაც ვიცი PHP-ში სესიის ID-ის დასაცავად.

ქუქიების გამოყენება ნაგულისხმევად, სესიის ყველა ინფორმაცია, ID-ის ჩათვლით, იგზავნება ქუქიში. მაგრამ ეს ყოველთვის არ ხდება. ზოგიერთი მომხმარებელი გამორთავს ქუქი-ფაილებს თავის ბრაუზერებში. ამ შემთხვევაში, ბრაუზერი გადასცემს სესიის ID-ს URL-ში.

აქ ID იგზავნება მკაფიო ტექსტით, განსხვავებით სესიისგან ქუქი-ფაილის საშუალებით, როდესაც ინფორმაცია იმალება HTTP სათაურში. ამისგან დაცვის უმარტივესი გზა იქნება სესიის იდენტიფიკატორის გადაცემის აკრძალვა მისამართების ზოლის მეშვეობით. ეს შეიძლება გაკეთდეს Apache სერვერის .htaccess კონფიგურაციის ფაილში შემდეგი ჩაწერით:

Php_flag session.use_only_cookies ჩართულია

დაშიფვრის გამოყენება თუ თქვენმა საიტმა უნდა დაამუშავოს მგრძნობიარე ინფორმაცია, როგორიცაა საკრედიტო ბარათის ნომრები (გამარჯობა Sony-სგან), უნდა გამოიყენოთ SSL3.0 ან TSL1.0 დაშიფვრა. ამისათვის, ქუქი-ფაილის დაყენებისას, თქვენ უნდა მიუთითოთ true უსაფრთხოების პარამეტრისთვის.

თუ სესიის პაროლს ინახავთ $_SESSION ცვლადში (უმჯობესია გამოიყენოთ sql), მაშინ არ უნდა შეინახოთ იგი წმინდა ტექსტში.

თუ ($_SESSION["პაროლი"] == $userpass) ( // კოდი )

ზემოაღნიშნული კოდი არ არის დაცული, რადგან პაროლი ინახება როგორც უბრალო ტექსტი სესიის ცვლადში. ამის ნაცვლად, გამოიყენეთ md5 დაშიფვრა, მსგავსი რამ:

თუ ($_SESSION["md5password"] == md5($userpass)) ( // კოდი )

ბრაუზერის შემოწმება სხვა ბრაუზერის (კომპიუტერიდან) სესიის გამოყენების შესაძლებლობის თავიდან ასაცილებლად, უნდა შეიყვანოთ მომხმარებლის აგენტის HTTP სათაურის ველის შემოწმება:

სესიის_დაწყება(); if (isset($_SESSION["HTTP_USER_AGENT"])) (if ($_SESSION["HTTP_USER_AGENT"] != md5($_SERVER["HTTP_USER_AGENT"])) ( // კოდი ) ) სხვა ($_SESSION["HTTP_USER_USER"_USER ] = md5 ($_SERVER["HTTP_USER_AGENT"]);

სესიის ვადის გასვლა შეზღუდეთ სესიის ხანგრძლივობა, ასევე ქუქიების ვადის გასვლის დრო. ნაგულისხმევად, სესიის ხანგრძლივობაა 1440 წამი. თქვენ შეგიძლიათ შეცვალოთ ეს მნიშვნელობა php.ini და .htaccess მეშვეობით. მაგალითი .htaccess-ისთვის:

# სესიის ხანგრძლივობა წამებში
php_value session.gc_maxlifetime 3600
# ქუქი-ფაილის სიცოცხლე წამებში
php_value session.cookie_lifetime 3600

დაკავშირება IP მისამართით გარკვეულ სიტუაციებში (არა ყოველთვის), თქვენ უნდა დააკავშიროთ IP მისამართით. ძირითადად, როცა მომხმარებელთა რაოდენობა შეზღუდულია და აქვთ სტატიკური IP-ები. შემოწმება შეიძლება დაფუძნებული იყოს დაშვებული IP მისამართების სიაზე,

Include ("ip_list.php"); //$ip_white_list = მასივი ("admin1" => "111.222.333.444", "admin2" => "555.666.777.888"); if(!empty(array_search($_SERVER["REMOTE_ADDR"],$ip_white_list))) ( header ("Location: admin.php"); ) else (echo "ACCESS DENY!";)

ან IP მისამართით თითოეული მოთხოვნისთვის (მხოლოდ სტატიკური IP-სთვის):

If(isset($_SESSION["ip"]) და $_SESSION["ip"] == $_SERVER["REMOTE_ADDR"]) ( header ("მდებარეობა: admin.php"); ) else ( session_unset (); $ _SESSION["ip"] = $_SERVER["REMOTE_ADDR"];)

უნდა იცოდეთ, რომ ჰაკერების სრულად აცილება შეუძლებელია. თქვენ შეგიძლიათ ეს ჰაკი რაც შეიძლება გაართულოთ ნებისმიერი ცნობილი საშუალებით. ამასთან, თქვენ ასევე არ უნდა დაივიწყოთ თქვენი კანონიერი მომხმარებლები, რათა არ გაართულოთ მათი ცხოვრება ასეთი დაცვით.

ეს სტატია დაიწერა 2009 წელს და რჩება ჩვენს ერთ-ერთ ყველაზე პოპულარულ პოსტად. თუ გსურთ გაიგოთ მეტი PHP-ისა და MySQL-ის შესახებ, შეიძლება ეს დიდი ინტერესი აღმოჩნდეთ.

შენიშვნა: ეს სტატია ახლად განახლდა, ​​რათა იმუშაოს PHP 4.2 ან უფრო გვიან!

ცოტა ხნის წინ მომეცა საშუალება მემუშავა მცირე პროექტზე ადამიანთა ჯგუფთან ერთად. ადრევე დავადგინეთ, რომ მხოლოდ ელ.ფოსტა არ იქნებოდა საკმარისი იმისათვის, რომ ყველამ შეგვენახა, ამიტომ მე დავალებული მქონდა პროექტისთვის პატარა ვებსაიტის აგება. ის შეიცავს უბრალო შეტყობინებების დაფას, ადგილს, სადაც ჩვენ შეგვიძლია ავტვირთოთ დოკუმენტები და სხვა ფაილები გუნდის დანარჩენი წევრებისთვის და საკონტაქტო ინფორმაცია გუნდის სხვადასხვა წევრებისთვის.

იმისთვის, რომ ამ ფუნქციებიდან ბევრი იმუშაოს, ვიცოდი, რომ მჭირდება მომხმარებლების შესვლა საიტის შესაბამის ნაწილებზე წვდომამდე. რაც მე მჭირდებოდა იყო სისტემა, რომელიც მომხმარებლებს მისცემდა დარეგისტრირდნენ მომხმარებლის ID-სთვის საიტზე შესვლისთვის, შემდეგ დაუყოვნებლივ გამოიყენონ ეს ID ჩემი მხრიდან ყოველგვარი ჩარევის გარეშე.

ამ სტატიაში მე შემოგთავაზებთ ჩემს მიერ შემუშავებული სისტემის მიმოხილვას, დაწყებული პირველი ნახევრიდან მომხმარებლის რეგისტრაციის პროცესით. მეორე ნახევარში მე ყურადღებას გავამახვილებ თავად საიტზე, თუ როგორ მოითხოვს ის მომხმარებლებს შესვლას და შემდეგ ინარჩუნებს შესვლის სტატუსს მთელი ვიზიტის განმავლობაში. მე განსაკუთრებულ ყურადღებას მივაქცევ PHP-ში სესიის მართვის ფუნქციების გამოყენებას. საბოლოო ჯამში, თქვენ უნდა გქონდეთ ყველა ინფორმაცია, რომელიც გჭირდებათ საკუთარი მსგავსი სისტემის დასანერგად.

მთელი ამ სტატიის განმავლობაში, მე ვივარაუდებ, რომ თქვენ გაქვთ საბაზისო ცოდნა PHP ენასთან, PHP სკრიპტზე ინფორმაციის წარდგენის ფორმების გამოყენებასთან და როგორ შეიძლება გამოყენებულ იქნას PHP MySQL მონაცემთა ბაზასთან ურთიერთობისთვის. თუ რომელიმე ეს თქვენთვის უცხო ცნებაა, უნდა დაიწყოთ ჩემი წინა სტატიის წაკითხვით.

ნაწილი პირველი: რეგისტრაციის პროცესი რეგისტრაციის ფორმა

ბუნებრივი ადგილი, რომ დაიწყოთ საიტის აშენება, რომელიც მომხმარებლებს დასჭირდება დარეგისტრირებას წვდომისთვის, არის თავად რეგისტრაციის პროცესი. როგორც მოსალოდნელია, უბრალო ვებ-ზე დაფუძნებული ფორმა ამას გააკეთებს. აი, როგორ გამოიყურება:

და აქ არის კოდი ამ ფორმისთვის:




ახალი მომხმარებლის რეგისტრაცია



ახალი მომხმარებლის რეგისტრაციის ფორმა

* მიუთითებს საჭირო ველზე


როდესაც მიზანი უკვე ნათელია, მე გაგაცნობთ კოდს accesscontrol.php-ისთვის. დაიწყეთ თქვენი ორი მოსახერხებელი ფაილის ჩათვლით: