Andreev indeks php kullanıcısı. PHP ve MySQL'de İnanılmaz Basit Bir Kayıt Sistemi Oluşturmak

En başından beri herkes PHP'yi büyük bir hızla kabul etti, ancak bu dilde oldukça büyük projeler oluşturulmaya başlar başlamaz geliştiriciler yeni bir sorunla karşı karşıya kaldılar - PHP global değişken kavramından yoksundu! Yani, belirli bir komut dosyası yürütüldü, oluşturulan sayfa istemciye gönderildi ve bu komut dosyası tarafından kullanılan tüm kaynaklar yok edildi. Örneklemeye çalışacağım: diyelim ki bir sitenin iki sayfası var; index.php ve dothings.php. Bu sayfaların kaynakları şöyle görünür:

index.php dothings.php

Bu iki scripti çalıştırırsak ilk sayfada “index.php'ye atandım” yazısını göreceğiz ve ikinci sayfa boş olacaktır.

Web sitesi geliştiricileri, hiç düşünmeden, global değişkenleri istemci tarafında depolamak için çerezleri kullanmaya başladı. Süreç şuna benziyordu: Kullanıcı sitenin ana sayfasına gelir, bazı işlemler yapar ve bu kullanıcıyla ilişkili, sitenin diğer sayfalarında ihtiyaç duyulabilecek tüm bilgiler tarayıcısında formda saklanacaktır. Çerezlerden. Bu yöntemin oldukça ciddi dezavantajları var, çünkü birçok geliştirici bir anda PHP'den uzaklaştı. Örneğin, bir kullanıcıya sitenin özel (veya özel) bölümlerine erişmesine izin vermesi için yetki vermemiz gerekir. Kullanıcıya, sitedeki sonraki tanımlayıcısı olarak görev yapacak bir çerez göndermeniz gerekecektir. Bu yaklaşım, site kullanıcının davranışı hakkında giderek daha fazla bilgi toplamaya başlar başlamaz çok hantal ve kullanışsız hale gelir, çünkü kullanıcıya gönderilen tüm bilgilerin sahte olamayacak şekilde kodlanması tavsiye edilir. Yakın zamanda sahte çerezler kullanarak birden fazla sohbeti "kırmak" ve hatta bazen başka birinin e-postasına gizlice girmek mümkün oldu. Ayrıca dünyada hala tarayıcısı çerezleri desteklemeyen garip insanlar var.

Oturum mekanizmasının teknolojik sorunlarına girmeyeceğim, yalnızca PHP'deki oturumlarla doğru şekilde nasıl çalışılacağını anlatacağım.

Oturumlarla nasıl çalışılır?

Makaledeki örnekleri (veya komut dosyalarınızı) herhangi bir ticari barındırmada test ederseniz, oturumlarla çalışırken herhangi bir sorun yaşanmayacaktır. Sunucunuzu kendiniz kurarsanız (ister gerçek bir sunucu ister emülatör olsun), aşağıdakine benzer hatalar alabilirsiniz:

"Uyarı: open(/var/state/php/sess_6f71d1dbb52fa88481e752af7f384db0, O_RDWR) başarısız oldu: Böyle bir dosya veya dizin yok (2)."

Bu sadece PHP'nizin yanlış yapılandırıldığı anlamına gelir. Oturumları php.ini dosyasına kaydetmek ve sunucuyu yeniden başlatmak için doğru yolu (mevcut bir dizine) belirterek bu sorunu çözebilirsiniz.

Oturumlardaki değişkenleri (verileri) kullanacak herhangi bir komut dosyası aşağıdaki satırı içermelidir:

Oturum_başlangıcı();

Bu komut, sunucuya belirli bir sayfanın belirli bir kullanıcı (tarayıcı) ile ilişkili tüm değişkenlere ihtiyaç duyduğunu bildirir. Sunucu bu değişkenleri dosyadan alır ve kullanılabilir hale getirir. Kullanıcıya herhangi bir veri gönderilmeden önce oturumun açılması çok önemlidir; pratikte bu, session_start() işlevinin sayfanın en başında çağrılmasının tavsiye edilebileceği anlamına gelir; örneğin şu şekilde:

Oturum_başlangıcı(); ?> ... Oturum dosyalarının kaydedileceği dizini ayarlamak için session_save_path() işlevini kullanın: session_save_path($_SERVER["DOCUMENT_ROOT"]."/session"); oturum_başlangıç();

Oturum başladıktan sonra genel değişkenleri ayarlayabilirsiniz. $_SESSION dizisinin herhangi bir alanına herhangi bir değer atarken, aynı ada sahip bir değişken otomatik olarak oturum değişkeni olarak kaydedilir. Bu dizi, oturumu kullanan tüm sayfalarda mevcuttur. Örnek olarak programa bakalım:

index.php Her şey yolunda. Oturum yüklendi! Hadi göz atalım ve orada ne olduğunu görelim: dothings.php

Bu dosyaları sırayla çalıştırdığınızda, ilk "index.php" betiği aşağıdaki sonucu üretecektir:

Her şey yolunda. Oturum yüklendi! Hadi geçelim ve orada ne olduğunu görelim:

Ve ikinci “dothings.php” şudur:

index.php yazmam istendi

$a değişkeni artık belirli bir sitenin oturum başlatan tüm sayfalarında kullanılabilir.

Oturumlarla çalışmaya yönelik diğer yararlı özellikler ve teknikler:

  • unset($_SESSION["a"]) - oturum, belirtilen oturum değişkeninin değerini “unutur”;
  • session_destroy() - oturum yok edilir (örneğin, kullanıcı "oturumu kapat" düğmesini tıklayarak sistemden ayrılırsa);
  • session_set_cookie_params (int life [, string path [, string domain]]) - bu işlevi kullanarak, oturumun "ölüm" zamanını belirleyen bir unix_timestamp ayarlayarak oturumun ne kadar "yaşayacağını" ayarlayabilirsiniz. Varsayılan olarak oturum, istemci tarayıcı penceresini kapatana kadar "canlı" olur.
  • session_write_close() - oturum değişkenlerini yazar ve kapatır. Sayfanın işlenmesi uzun sürüyorsa ve tarayıcınızın oturum dosyasını engellemişse siteyi yeni bir pencerede açmak için bu gereklidir.
Örnekler

Şimdi oturum mekanizmasının pratik uygulamasına dönelim. Burada oldukça basit ve aynı zamanda yararlı birkaç örneğe bakacağız.

Kullanıcı Yetkilendirmesi

PHP oturumlarını kullanarak kullanıcı yetkilendirmesine ilişkin sorular web programlama konferanslarında sürekli olarak sorulmaktadır. Sistemdeki kullanıcıları oturumları kullanarak yetkilendirme mekanizması, güvenlik açısından oldukça iyidir (bkz. bölüm).

Örneğimiz üç dosyadan oluşacaktır: index.php,authorize.php ve secretplace.php. index.php dosyasında kullanıcının kullanıcı adını ve şifresini gireceği bir form bulunur. Bu form, yetkilendirme başarılı olursa kullanıcının secretplace.php dosyasına erişmesine izin verecek, aksi takdirde bir hata mesajı görüntüleyecek olan Authorize.php dosyasına veri iletecektir.

Örnekler: index.php Şifreyi girin Giriş:
Şifre:
Authorize.php Yanlış şifre girdiniz! secretplace.php Merhaba, gizli sayfadasınız!!! :)

Emniyet

Böylece, bir tanımlayıcıyı bir sayfadan (PHP betiği) diğerine (sitemizden bir sonraki çağrıya kadar) aktarabiliyoruz, bu da tüm site ziyaretçilerini birbirinden ayırabildiğimiz anlamına geliyor. Oturum tanımlayıcısı çok büyük bir sayı olduğundan (128 bit), kaba kuvvetle bulunma şansı neredeyse yoktur. Bu nedenle saldırganın elinde aşağıdaki seçenekler kalır:

  • kullanıcının bilgisayarında oturum numaralarını çalan bir Truva atı var;
  • Saldırgan, kullanıcının bilgisayarı ile sunucu arasındaki trafiği keser. Elbette güvenli (şifreli) bir SSL protokolü var ama bunu herkes kullanmıyor;
  • Bir komşu kullanıcımızın bilgisayarına yaklaştı ve oturum numarasını çaldı.

Birinin başka birinden bir şey çalmasına dayanan bu tür durumlar genel olarak programcının yetki alanı dahilinde değildir. Yöneticiler ve kullanıcıların kendileri bununla ilgilenmelidir.

Ancak PHP sıklıkla "kandırılabilir". Kullanıcı yetkilendirme programındaki olası hack noktalarına bakalım:

  • Authorize.php dosyası, üçüncü taraf bir komut dosyası kullanarak bir parolayı tahmin etme girişimidir;
  • Secretplace.php dosyası, tarayıcının adres çubuğuna $logged_user değişkeninin değerlerini girerek programı aldatma girişimidir, örneğin:
    "http://www.yoursite.ru/secretplace.php?logged_user=hacker"

Yani, programımızda iki "delik" açıkça görülüyor, biri küçük ve pek fark edilmiyor, ancak ikincisi çok büyük ve çoğu bilgisayar korsanı gitmesine gerek olmayan yere giriyor.

1 numaralı deliğe nasıl yama yapılır?

Bir IP adresini vb. engellemek için tonlarca kod yazmayacağız, ancak yalnızca isteğin nereden geldiğini veya daha doğrusu isteğin hangi sayfadan geldiğini kontrol edin, eğer sitemizden herhangi bir sayfa ise, o zaman her şey yolunda, ancak diğer tüm durumlarda içeri girmenize izin vermeyeceğiz. Authorize.php dosyasını ayarlayalım:

Authorize.php V2 Yanlış şifreyi girdiniz!
2 numaralı "delikten" nasıl kurtulurum?

Herkesin bir foruma mesaj göndermek için kaydolabileceği bir web siteniz olduğunu varsayalım. Doğal olarak forumda bazı kullanıcıların (yöneticiler, moderatörler) diğerlerinden daha fazla olanağı vardır; örneğin diğer kullanıcıların mesajlarını silebilir. Kullanıcının erişim düzeyini oturumda $user_status değişkeninde saklarsınız; burada $user_status = 10, sisteme tam erişime karşılık gelir. Siteye gelen saldırganın normal şekilde kayıt olması ve ardından tarayıcının adres çubuğuna ?user_status=10 eklemesi yeterlidir. Demek forumunuzda yeni bir yöneticiniz var!

Prensip olarak, herhangi bir komut dosyası değişkeni, komut dosyasına tam adresin ardından basitçe bir soru işareti ve değişkenin adı ve değeri eklenerek adres çubuğu aracılığıyla ayarlanabilir. Bunu önlemek için kodumuzu düzeltelim:

secretplace.php V2 Merhaba, gizli bir sayfadasınız! Sonuçlar

Oturum mekanizması PHP dilinin oldukça iyi bir özelliğidir. Oturumlar basit ve kullanımı çok esnektir. Bu arada, PHP oturumlarının çok az belgelenmiş bir özelliği var (4.0.3 sürümünden itibaren mevcut) - oturumlarda yalnızca değişkenleri değil aynı zamanda nesneleri de saklayabilirsiniz.

Örnekler ?>
// SID'yi bağlantılara otomatik olarak ekleyin. ini_set("session.use_trans_sid", true); oturum_başlangıç(); ?> Buraya tıklayın!
Buraya tıklayın!!

// Oturumlarla çalışmaya bir örnek. oturum_başlangıç(); // Siteyi yeni ziyaret ettiyseniz sayacı sıfırlayın. if (!isset($_SESSION["count"])) $_SESSION["count"] = 0; //Oturumdaki sayacı arttırıyoruz. $_SESSION["count"] = $_SESSION["count"] + 1; ?> Sayaç süreleri.
Sayacı sıfırlamak için tarayıcınızı kapatın.

4. Aynı veritabanına bir tablo eklemeniz gerekir. Giriş yaparken hata yapan IP adreslerini saklar. Bu şekilde, yaklaşık 15 dakika boyunca art arda üç defadan fazla hata yapanların erişimini sınırlandırabiliriz. Sanırım şifre seçen programların uzun süre düzeltme yapması gerekecek.
Phpmyadmin'e gidip 3 alanlı yeni bir tablo oluşturalım:


ip - IP adresi.
tarih - bu ip ile kullanıcının son 15 dakikadaki başarısız oturum açma tarihi. col - bu IP'ye sahip kullanıcı için son 15 dakikadaki hataların sayısı.
Harika! Bitti, şimdi kullanıcı adı ve şifre doğrulama dosyasını değiştirelim çünkü artık şifremiz şifrelenmiştir. Testreg.php'yi açın ve kullanıcı adı ve şifredeki boşlukları kaldırmanın ötesinde her şeyi silin. Daha sonra aşağıdaki kodu ekliyoruz:

//ekstra boşlukları kaldır
$giriş = trim($giriş);
$şifre = trim($şifre);

// yenisiyle değiştirin************************************************ *******
//veritabanına bağlanıyoruz
include("bd.php");// bd.php dosyası diğerleriyle aynı klasörde olmalıdır, değilse sadece yolu değiştirin
// şifre seçimi için mini kontrol
$ip=getenv("HTTP_X_FORWARDED_FOR");
if (empty($ip) || $ip=="unknown") ( $ip=getenv("REMOTE_ADDR"); )//ip'i çıkart
mysql_query ("DELETE FROM oshibka WHERE UNIX_TIMESTAMP() - UNIX_TIMESTAMP(date) > 900");//15 dakika sonra giriş yaparken hata yapan kullanıcıların IP adreslerini silin.
$result = mysql_query("SELECT col FROM oshibka WHERE ip="$ip"",$db); // belirli bir IP'ye sahip bir kullanıcı için son 15 içindeki başarısız oturum açma denemelerinin sayısını veritabanından alın
$myrow = mysql_fetch_array($result);
if ($satırım["sütun"] > 2) (
//eğer ikiden fazla yani üç hata varsa mesaj veriyoruz.
çıkış("Kullanıcı adınızı veya şifrenizi 3 kez hatalı girdiniz. Lütfen tekrar denemeden önce 15 dakika bekleyin.");
}
$şifre = md5($şifre);//şifreyi şifrele
$şifre = strrev($şifre);// güvenilirlik için tersini ekleyin
$şifre = $şifre."b3p6f";
//kendi karakterinizden birkaçını kendi zevkinize göre ekleyebilirsiniz örneğin "b3p6f" yazarak. Eğer bu şifre aynı md5 sunucusunda kaba kuvvetle hacklenirse, o zaman elbette bundan iyi bir şey çıkmayacak. Ama diğer karakterleri belki satırın başına ya da ortasına koymanızı tavsiye ederim.
//Bu durumda veritabanındaki şifre alanının uzunluğunun arttırılması gerekmektedir. Şifrelenmiş şifre çok daha büyük olabilir.

$result = mysql_query("SELECT * FROM user WHERE giriş=$giriş" AND şifre=$şifre"",$db); //girilen kullanıcı adı ve şifreyle veritabanından kullanıcı hakkındaki tüm verileri al
$myrow = mysql_fetch_array($result);
if (empty($myrow["id"]))
{
//girilen kullanıcı adı ve şifreye sahip kullanıcı mevcut değilse
//Bu ipin giriş yapamadığını kayıt altına alıyoruz.
$select = mysql_query("oshibka'dan ip SEÇİN NEREDE ip =$ip"");
$tmp = mysql_fetch_row($select);
if ($ip == $tmp) (//kullanıcının "oshibka" tablosunda olup olmadığını kontrol edin
$result52 = mysql_query("oshibka'dan WHERE ip = $ip"",$db);
$myrow52 = mysql_fetch_array($result52);
$col = $myrow52 + 1;//bir başarısız giriş denemesi daha ekle
mysql_query("GÜNCELLEME hatası SET col=$col,date=NOW() WHERE ip=$ip"");
}
başka(
mysql_query("oshibka (ip,date,col) VALUES'A EKLEYİN ("$ip",NOW(),"1")");
//son 15 dakikada herhangi bir hata olmadıysa "oshibka" tablosuna yeni bir giriş ekleyin
}

çıkış("Üzgünüz, girdiğiniz kullanıcı adı veya şifre yanlış.");
}
başka(
nbsp; //şifreler eşleşirse kullanıcı için bir oturum başlatırız! Onu tebrik edebilirsiniz, içeri girdi!
$_SESSION["şifre"]=$satırım["şifre"];
$_SESSION["giriş"]=$myrow["giriş"];
$_SESSION["id"]=$myrow["id"];//bu veriler çok sık kullanılıyor, dolayısıyla oturum açmış kullanıcı onu “yanında taşıyacak”

//Daha sonra, sonraki oturum açma işlemleri için verileri çerezlere kaydediyoruz.
//DİKKAT!!! VERİLER ŞİFRELENMEDEN ÇEREZLERDE SAKLANDIĞINDAN BUNU TAKDİRİNİZE GÖRE YAPIN
if ($_POST["kaydet"] == 1) (
//Kullanıcı verilerinin sonraki oturum açma işlemleri için kaydedilmesini isterse, bu verileri tarayıcı çerezlerine kaydederiz
setcookie("giriş", $_POST["giriş"], time()+9999999);
setcookie("şifre", $_POST["şifre"], time()+9999999);
}}
echo ""; // kullanıcıyı başarılı bir giriş yaptığını bildireceğimiz ana sayfaya yönlendiriyoruz
?>

5. Ana sayfayı tamamen değiştireceğiz. Üzerinde kullanıcının avatarını görüntülemek, hesaptan çıkış yapmak için bir bağlantı görüntülemek ve giriş yaparken şifreyi hatırlamak için bir onay kutusu eklemek gerekir.
Index.php




Ana sayfa


Ana sayfa



6. Oturum açmış kullanıcıların çıkış yapmasını mümkün kılmak gerekir. Ana sayfada çıkış için bir bağlantı zaten vardı. Ancak bu dosya henüz mevcut değil. O halde şu kodla yeni bir çıkış.php dosyası oluşturalım:

Tamam artık her şey bitti! Sağlığınız için tadını çıkarın! İyi şanlar!

Soru-Cevap bölümünde aynı şeyi 100.500 kez yanıtlamaktan yorulduğum için bu notu yazmaya karar verdim.

Birçok acemi web programcısı er ya da geç web sitelerine insan tarafından okunabilen bağlantılar (HUR) ekleme göreviyle karşı karşıya kalır. CNC'nin uygulanmasından önce, tüm bağlantılar /myscript.php veya hatta /myfolder/myfolder2/myscript3.php gibi görünüyordu; bu, hatırlanması zor ve SEO açısından daha da kötüydü. CNC'nin uygulanmasından sonra bağlantılar /statiya-o-php veya hatta Kiril /article-o-php biçimini alır.

SEO'dan bahsetmişken. İnsanların okuyabileceği bağlantılar GERÇEKTEN kolay ezberlemek için değil, esas olarak sitenin dizine eklenebilirliğini artırmak için icat edildi, çünkü arama sorgusu ile URL'nin bir kısmının çakışması, arama sıralamasında iyi bir avantaj sağlar.

Acemi bir PHP programcısının gelişimi aşağıdaki adımlarla ifade edilebilir:

  • Düz PHP kodunu ayrı dosyalara yerleştirmek ve bu dosyalara /myfolder/myscript.php gibi bağlantılar aracılığıyla erişmek
  • Tüm komut dosyalarının önemli bir ortak noktaya sahip olduğunu anlamak (örneğin, veritabanına bağlantı oluşturmak, yapılandırmayı okumak, oturum başlatmak vb.) ve bunun sonucunda ortak bir başlangıç ​​“giriş” noktası oluşturmak, bazı komut dosyalarının TÜM istekleri kabul eder ve ardından hangisinin dahili komut dosyasına bağlanacağını seçer. Genellikle bu komut dosyası index.php olarak adlandırılır ve kökte bulunur, bunun sonucunda tüm istekler (diğer adıyla URL'ler) şu şekilde görünür: /index.php?com=myaction&com2=mysubaction
  • Bir yönlendirici uygulama ihtiyacı ve insan tarafından okunabilen bağlantılara geçiş.
  • Çoğu programcının 2. ve 3. noktalar arasında bariz bir hata yaptığını not ediyorum. Buna programcıların yaklaşık %95'inin değeri dersem yanlış olmaz. Bilinen çerçevelerin çoğu bile bu hatayı içerir. Ve aşağıdakilerden oluşur.

    Bağlantıları işlemenin temelde yeni bir yolunu uygulamak yerine, mod_rewrite kullanarak birçok yönlendirme kuralı oluşturmaktan oluşan .htaccess'e dayalı "yamalar ve yönlendirmeler" kavramı yanlışlıkla yapılmıştır. Bu satırlar, URL'yi bazı normal ifadelerle karşılaştırır ve eğer bir eşleşme varsa, URL'den çıkarılan değerleri GET değişkenlerine iter ve ardından aynı index.php'yi çağırır.

    #Yanlış CNC yöntemi RewriteEngine On RewriteRule ^\/users\/(.+)$ index.php?module=users&id=$1 #....Çok daha fazla benzer kural...

    Bu konseptin birçok dezavantajı var. Bunlardan biri, kural oluşturmanın zorluğudur; tespit edilmesi zor kuralları eklerken büyük oranda insan hatası meydana gelir, ancak bunlar 500 sunucu hatasına yol açar.

    Diğer bir dezavantaj ise genellikle sunucu yapılandırmasına göre düzenlenmesidir ki bu başlı başına saçmalıktır. Ve Apache'de yapılandırma .htaccess kullanılarak "yama" yapılabiliyorsa, popüler nginx'te böyle bir seçenek yoktur, her şey sistem bölgesindeki ortak bir yapılandırma dosyasında bulunur.

    Ve muhtemelen en önemlisi olan bir dezavantaj daha, bu yaklaşımla yönlendiriciyi dinamik olarak yapılandırmanın, yani "anında", istenen komut dosyasını seçme kurallarını algoritmik olarak değiştirmenin ve genişletmenin imkansız olmasıdır.

    Aşağıda önerilen yöntem tüm bu dezavantajları ortadan kaldırmaktadır. Zaten çok sayıda modern çerçevede kullanılmaktadır.

    Sonuç olarak, ilk istek her zaman $_SERVER['REQUEST_URI'] değişkeninde saklanır, yani index.php içinde okunabilir ve tüm hata işleme, dinamik yönlendirmeler vb. ile PHP kullanılarak bir dize olarak ayrıştırılabilir.

    Bu durumda, yapılandırma dosyasında tüm istekleri var olmayan dosya veya klasörlere yönlendirecek tek bir statik kural oluşturabilirsiniz. index.php.

    RewriteCond %(REQUEST_FILENAME) üzerinde RewriteEngine !-f #Dosya yoksa RewriteCond %(REQUEST_FILENAME) !-d #Ve eğer klasör yoksa RewriteRule ^.*$ index.php

    Üstelik bu kural hem .htaccess'e hem de Apache'nin ana yapılandırma dosyasına yerleştirilebilir.

    Nginx için ilgili kural şöyle görünecektir:

    Konum / ( if (!-e $request_filename) ( rewrite ^/(.*)$ /index.php last; ))

    Basit.

    Şimdi index.php'deki bağlantıları analiz eden ve hangi betiğin çalıştırılacağına karar veren PHP kodunun bir parçasına bakalım.

    /bölüm1/bölüm2/bölüm3

    Akla gelen ilk şey, patlayıcıyı ('/', $uri) kullanarak parçalamak ve isteğin her parçasını analiz eden anahtar/durum bazlı karmaşık bir yönlendirici oluşturmaktır. Böyle yapma! Bu karmaşıktır ve kodun korkunç derecede anlaşılmaz ve yapılandırılamaz görünmesine neden olur!

    Daha kısa bir yol öneriyorum. Bunu kelimelerle anlatmak yerine kodu hemen göstermek daha iyidir.