“Beni Oturumda Tut” - en iyi yaklaşım


257

Web uygulamam, oturum açtıktan sonra kullanıcı hakkındaki bilgileri depolamak ve uygulama içinde sayfadan sayfaya giderken bu bilgileri korumak için oturumları kullanır. Bu özel uygulamada user_id, first_nameve last_namekişiyi saklıyorum .

Oturum açtığında, kullanıcının makinesine iki hafta boyunca bir çerez yerleştirecek ve uygulamaya geri döndüklerinde aynı ayrıntılarla oturumlarını yeniden başlatacak bir "Beni Oturum Açın" seçeneği sunmak istiyorum.

Bunu yapmak için en iyi yaklaşım nedir? Onların bir user_idçerezde saklamak istemiyorum , çünkü bir kullanıcının başka bir kullanıcının kimliğini denemesini kolaylaştıracak gibi görünüyor.

Yanıtlar:


735

Tamam, bunu açık bir şekilde ifade edeyim: kullanıcı verilerini veya kullanıcı verilerinden türetilen herhangi bir şeyi bu amaçla bir çereze koyarsanız, yanlış bir şey yapıyorsunuz.

Orada. Söyledim. Şimdi asıl cevaba geçebiliriz.

Kullanıcı verilerinin karmaşasında sorun nedir? Bu, belirsizlik yoluyla maruz kalma yüzeyine ve güvenliğe gelir.

Bir saniyeliğine saldırgan olduğunuzu düşünün. Oturumunuzda beni hatırla için ayarlanmış bir kriptografik çerez görüyorsunuz. 32 karakter genişliğindedir. Gee. Bu bir MD5 olabilir ...

Bir saniye için kullandığınız algoritmayı bildiğini düşünelim. Örneğin:

md5(salt+username+ip+salt)

Şimdi, bir saldırganın tek yapması gereken kaba kuvvet "tuz" (ki bu gerçekten bir tuz değil, daha sonra daha fazla) ve şimdi IP adresi için herhangi bir kullanıcı adıyla istediği tüm sahte belirteçleri üretebilir! Fakat kaba bir kaba zorlamak zor, değil mi? Kesinlikle. Ancak günümüzün GPU'ları son derece iyidir. Ve içinde yeterince rasgelelik kullanmadığınız sürece (yeterince büyük yapın), hızlı bir şekilde düşecek ve onunla kalenizin anahtarları olacaktır.

Kısacası, sizi koruyan tek şey, düşündüğünüz kadar sizi gerçekten koruyamayan tuzdur.

Fakat bekle!

Bütün bunlar saldırganın algoritmayı bildiği öngörülüyordu! Gizli ve kafa karıştırıcıysa, güvendesiniz değil mi? YANLIŞ . Bu düşüncenin bir adı vardır: ASLA güvenilmemesi gereken Müstehcenlikle Güvenlik .

Daha İyi Yol

Daha iyi bir yol, bir kullanıcı bilgilerinin kimlik dışında sunucudan ayrılmasına asla izin vermemektir.

Kullanıcı oturum açtığında büyük (128 ila 256 bit) rastgele bir belirteç oluşturun. Simgeyi kullanıcı kimliğiyle eşleyen bir veritabanı tablosuna ekleyin ve ardından tanımlama bilgisindeki istemciye gönderin.

Saldırgan başka bir kullanıcının rastgele jetonunu tahmin ederse ne olur?

Burada biraz matematik yapalım. 128 bit rastgele bir belirteç üretiyoruz. Bu şu demektir:

possibilities = 2^128
possibilities = 3.4 * 10^38

Şimdi, bu sayının ne kadar saçma olduğunu göstermek için, internetteki her sunucunun (bugün 50.000.000 diyelim), bu sayıyı saniyede 1.000.000.000 oranında kaba kuvvetle denemeye çalıştığını düşünelim. Gerçekte, sunucularınız böyle bir yük altında eriyecek, ancak hadi bunu oynayalım.

guesses_per_second = servers * guesses
guesses_per_second = 50,000,000 * 1,000,000,000
guesses_per_second = 50,000,000,000,000,000

Saniyede 50 katrilyon tahmin. Hızlı! Sağ?

time_to_guess = possibilities / guesses_per_second
time_to_guess = 3.4e38 / 50,000,000,000,000,000
time_to_guess = 6,800,000,000,000,000,000,000

6.8 sextillion saniye ...

Bunu daha kolay sayılara indirmeye çalışalım.

215,626,585,489,599 years

Ya da daha iyisi:

47917 times the age of the universe

Evet, bu evrenin yaşının 47917 katı ...

Temel olarak, çatlamayacak.

Özetle:

Tavsiye ettiğim en iyi yaklaşım, çerezi üç parça ile saklamaktır.

function onLogin($user) {
    $token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit
    storeTokenForUser($user, $token);
    $cookie = $user . ':' . $token;
    $mac = hash_hmac('sha256', $cookie, SECRET_KEY);
    $cookie .= ':' . $mac;
    setcookie('rememberme', $cookie);
}

Ardından, doğrulamak için:

function rememberMe() {
    $cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : '';
    if ($cookie) {
        list ($user, $token, $mac) = explode(':', $cookie);
        if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) {
            return false;
        }
        $usertoken = fetchTokenByUserName($user);
        if (hash_equals($usertoken, $token)) {
            logUserIn($user);
        }
    }
}

Not: Veritabanınızdaki bir kaydı aramak için jetonu veya kullanıcı ve jeton kombinasyonunu kullanmayın. Her zaman kullanıcıya dayalı bir kayıt aldığınızdan ve getirilen belirteci daha sonra karşılaştırmak için zamanlama açısından güvenli bir karşılaştırma işlevi kullandığınızdan emin olun. Zamanlama saldırıları hakkında daha fazla bilgi .

Şimdi, kriptografik bir sır olması çok önemlidir SECRET_KEY( /dev/urandomyüksek entropi girdisine benzer ve / veya türetilmiş bir girdiden türetilmiştir). Ayrıca, GenerateRandomToken()(kuvvetli rasgele kaynak olması gerekir mt_rand()neredeyse yeterince güçlü değildir. Gibi kütüphane kullanma RandomLib veya random_compat veya mcrypt_create_iv()birlikte DEV_URANDOM) ...

hash_equals()Önlemektir zamanlama saldırıları . PHP 5.6'nın altında bir PHP sürümü kullanıyorsanız işlev hash_equals()desteklenmez. Bu durumda hash_equals(), timingSafeCompare işleviyle değiştirebilirsiniz :

/**
 * A timing safe equals comparison
 *
 * To prevent leaking length information, it is important
 * that user input is always used as the second parameter.
 *
 * @param string $safe The internal (safe) value to be checked
 * @param string $user The user submitted (unsafe) value
 *
 * @return boolean True if the two strings are identical.
 */
function timingSafeCompare($safe, $user) {
    if (function_exists('hash_equals')) {
        return hash_equals($safe, $user); // PHP 5.6
    }
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

    // mbstring.func_overload can make strlen() return invalid numbers
    // when operating on raw binary strings; force an 8bit charset here:
    if (function_exists('mb_strlen')) {
        $safeLen = mb_strlen($safe, '8bit');
        $userLen = mb_strlen($user, '8bit');
    } else {
        $safeLen = strlen($safe);
        $userLen = strlen($user);
    }

    // Set the result to the difference between the lengths
    $result = $safeLen - $userLen;

    // Note that we ALWAYS iterate over the user-supplied length
    // This is to prevent leaking length information
    for ($i = 0; $i < $userLen; $i++) {
        // Using % here is a trick to prevent notices
        // It's safe, since if the lengths are different
        // $result is already non-0
        $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
    }

    // They are only identical strings if $result is exactly 0...
    return $result === 0;
}

7
Ancak bu yaklaşım, herkesin bu kullanıcı adını ve çerezi alıp başka bir cihazdan bu kullanıcı olarak giriş yapabileceği anlamına gelmiyor mu?
Daha basit

8
lol :-), 47917 yıl tahmin etmek için maksimum zaman olduğunu unutmayın, rastgele belirteç de 1 saat içinde tahmin edilebilir.
storm_buster

33
Garip çünkü kodunuz cevabınızla çelişiyor. "Kullanıcı verilerini bir çereze koyarsanız [...] yanlış bir şey yapıyorsunuz" diyorsunuz, ama tam olarak kodunuz bunu yapıyor! Kullanıcı adını çerezden kaldırmak, karmayı yalnızca jeton üzerinden hesaplamak (ve belki de çerez hırsızlığını önlemek için ip adresini eklemek) ve sonra rememberMe () yerine fetchUsernameByToken yapmak daha iyi değil mi?
Leven

9
PHP 5.6'dan beri, dize karşılaştırmaları yaparken zamanlama saldırılarını önlemek için hash_equals kullanılabilir.
F21

5
@Levit, birinin geçerli bir belirteç almasını ve ona bağlı kullanıcı kimliğini değiştirmesini önler.
ircmaxell

93

Güvenlik Uyarısı : Çerezin belirleyici verilerin MD5 karmasını temel alması kötü bir fikirdir; CSPRNG'den türetilen rastgele bir simge kullanmak daha iyidir. Daha güvenli bir yaklaşım için ircmaxell'in bu soruya verdiği cevaba bakınız .

Genellikle böyle bir şey yaparım:

  1. Kullanıcı 'oturumumu açık tut' ile giriş yapar
  2. Oturum oluştur
  3. Şunu içeren bir çerez oluşturun: md5 (salt + kullanıcı adı + ip + salt) ve bir şey adında bir çerez oluşturun
  4. Çerezi veritabanında depola
  5. Kullanıcı şeyler yapar ve bırakır ----
  6. Kullanıcı tanımlama bilgisi, bir şey olup olmadığını kontrol edinElse çerezi varsa, o kullanıcı için veritabanından eski karmayı alın, yeni bir hesaplanmış karma ( ip) böylece: cookieHash == databaseHash == md5 (tuz + kullanıcı adı + ip + tuz), eğer yaparlarsa, gitmezlerse 2'ye git

Tabii ki farklı çerez adları vb kullanabilirsiniz. Ayrıca çerez içeriğini biraz değiştirebilirsiniz, sadece kolayca oluşturulmadığından emin olun. Örneğin, kullanıcı oluşturulduğunda bir user_salt oluşturabilir ve bunu çereze koyabilirsiniz.

Ayrıca md5 (veya hemen hemen herhangi bir algoritma) yerine sha1 kullanabilirsiniz


30
IP'yi neden karmaya dahil ettiniz? Ayrıca, çerezlere zaman damgası bilgileri eklediğinizden emin olun ve sonsuzluk için iyi bir kimlik belirteci oluşturmamanız için çerez için maksimum yaş belirlemek üzere bu bilgileri kullanın.
Scott Mitchell

4
@Abhishek Dilliwal: Bu oldukça eski bir konu ama ben Mathew ile aynı cevabı aramaya geldim. Ben session_ID her session_start () değiştirdiği için db karma, çerez karma ve geçerli session_ID kontrol edemezsiniz çünkü session_ID kullanarak Pim cevap için çalışacağını sanmıyorum; sadece bunu işaret edeceğimi düşündüm.
Partack

3
Donuk olduğum için üzgünüm ama ikinci çerezin amacı nedir? Bu durumda kimlik nedir? Kullanıcının oturumumu açık tut özelliğini kullanmak isteyip istemediğini göstermek yalnızca basit bir "doğru / yanlış" değeri midir? Eğer öyleyse, neden sadece SOMETHING çerezi olup olmadığını kontrol etmiyorsunuz? Kullanıcı girişinin devam etmesini istemiyorsa, SOMETHING çerezi ilk etapta orada olmazdı değil mi? Son olarak, hash'ı tekrar dinamik olarak oluşturuyor ve ekstra güvenlik önlemi olarak çerez ve DB'ye karşı kontrol ediyor musunuz?
itsmequinn

4
Jeton RANDOM olmalı, kullanıcı / IP'si / kullanıcısı / hiçbir şeyle bağlantılı olmamalıdır. Bu büyük bir güvenlik açığı.
pamil

4
Neden iki tuz kullanıyorsunuz? md5 (tuz + kullanıcı adı + ip + tuz)
Aaron Kreider

77

Giriş

“Beni Oturum Açın” başlığınız - en iyi yaklaşım nereden başlayacağımı bilmemi zorlaştırır çünkü en iyi yaklaşıma bakıyorsanız aşağıdakileri dikkate almanız gerekir:

  • Kimlik
  • Güvenlik

Kurabiye

Çerezler savunmasızdır, Yaygın tarayıcı çerez-hırsızlığı güvenlik açıkları ve siteler arası komut dosyası saldırıları arasında çerezlerin güvenli olmadığını kabul etmeliyiz. Güvenliği artırmaya yardımcı olması için php setcookies,

bool setcookie (string $ name [, string $ value [, int $ expire = 0 [, string $ path [, string $ domain [, bool $ secure = false [, bool $ httponly = false]]]]]])

  • güvenli (HTTPS bağlantısını kullanma)
  • httponly (XSS saldırısı ile kimlik hırsızlığını azaltın)

Tanımlar

  • Token (N uzunluğunda öngörülemeyen rastgele dize, ör. / Dev / urandom)
  • Referans (n ​​uzunlukta öngörülemeyen rastgele dize, ör. / Dev / urandom)
  • İmza (HMAC yöntemini kullanarak anahtarlı bir karma değer oluşturun)

Basit Yaklaşım

Basit bir çözüm:

  • Kullanıcı Beni Hatırla ile oturum açtı
  • Jeton ve İmza ile verilen Giriş Çerezi
  • Geri döndüğünde İmza kontrol edilir
  • İmza uygunsa, veritabanında kullanıcı adı ve jeton aranır
  • geçerli değilse .. giriş sayfasına dön
  • Geçerliyse otomatik giriş

Yukarıdaki vaka çalışması, bu sayfada verilen tüm örnekleri özetlemektedir, ancak dezavantajları

  • Çerezlerin çalındığını bilmenin bir yolu yok
  • Saldırgan, şifre değişikliği gibi kişisel işlemlere veya kişisel bilgiler ve pişirme bilgileri gibi verilere erişebilir.
  • Güvenliği ihlal edilmiş çerez, çerez ömrü boyunca hala geçerli olur

Daha İyi Çözüm

Daha iyi bir çözüm

  • Kullanıcı oturum açtı ve beni hatırla seçili
  • Jeton ve imza oluşturun ve çerezde saklayın
  • Belirteçler rastgele ve yalnızca tek yetkilendirme için geçerlidir
  • Jeton, siteye yapılan her ziyarette değiştirilir
  • Oturum açmamış bir kullanıcı siteyi ziyaret ettiğinde imza, simge ve kullanıcı adı doğrulanır
  • Beni hatırla giriş kısıtlı erişime sahip olmalı ve şifre, kişisel bilgilerin vb. Değiştirilmesine izin vermemelidir.

Örnek Kod

// Set privateKey
// This should be saved securely 
$key = 'fc4d57ed55a78de1a7b31e711866ef5a2848442349f52cd470008f6d30d47282';
$key = pack("H*", $key); // They key is used in binary form

// Am Using Memecahe as Sample Database
$db = new Memcache();
$db->addserver("127.0.0.1");

try {
    // Start Remember Me
    $rememberMe = new RememberMe($key);
    $rememberMe->setDB($db); // set example database

    // Check if remember me is present
    if ($data = $rememberMe->auth()) {
        printf("Returning User %s\n", $data['user']);

        // Limit Acces Level
        // Disable Change of password and private information etc

    } else {
        // Sample user
        $user = "baba";

        // Do normal login
        $rememberMe->remember($user);
        printf("New Account %s\n", $user);
    }
} catch (Exception $e) {
    printf("#Error  %s\n", $e->getMessage());
}

Kullanılan Sınıf

class RememberMe {
    private $key = null;
    private $db;

    function __construct($privatekey) {
        $this->key = $privatekey;
    }

    public function setDB($db) {
        $this->db = $db;
    }

    public function auth() {

        // Check if remeber me cookie is present
        if (! isset($_COOKIE["auto"]) || empty($_COOKIE["auto"])) {
            return false;
        }

        // Decode cookie value
        if (! $cookie = @json_decode($_COOKIE["auto"], true)) {
            return false;
        }

        // Check all parameters
        if (! (isset($cookie['user']) || isset($cookie['token']) || isset($cookie['signature']))) {
            return false;
        }

        $var = $cookie['user'] . $cookie['token'];

        // Check Signature
        if (! $this->verify($var, $cookie['signature'])) {
            throw new Exception("Cokies has been tampared with");
        }

        // Check Database
        $info = $this->db->get($cookie['user']);
        if (! $info) {
            return false; // User must have deleted accout
        }

        // Check User Data
        if (! $info = json_decode($info, true)) {
            throw new Exception("User Data corrupted");
        }

        // Verify Token
        if ($info['token'] !== $cookie['token']) {
            throw new Exception("System Hijacked or User use another browser");
        }

        /**
         * Important
         * To make sure the cookie is always change
         * reset the Token information
         */

        $this->remember($info['user']);
        return $info;
    }

    public function remember($user) {
        $cookie = [
                "user" => $user,
                "token" => $this->getRand(64),
                "signature" => null
        ];
        $cookie['signature'] = $this->hash($cookie['user'] . $cookie['token']);
        $encoded = json_encode($cookie);

        // Add User to database
        $this->db->set($user, $encoded);

        /**
         * Set Cookies
         * In production enviroment Use
         * setcookie("auto", $encoded, time() + $expiration, "/~root/",
         * "example.com", 1, 1);
         */
        setcookie("auto", $encoded); // Sample
    }

    public function verify($data, $hash) {
        $rand = substr($hash, 0, 4);
        return $this->hash($data, $rand) === $hash;
    }

    private function hash($value, $rand = null) {
        $rand = $rand === null ? $this->getRand(4) : $rand;
        return $rand . bin2hex(hash_hmac('sha256', $value . $rand, $this->key, true));
    }

    private function getRand($length) {
        switch (true) {
            case function_exists("mcrypt_create_iv") :
                $r = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
                break;
            case function_exists("openssl_random_pseudo_bytes") :
                $r = openssl_random_pseudo_bytes($length);
                break;
            case is_readable('/dev/urandom') : // deceze
                $r = file_get_contents('/dev/urandom', false, null, 0, $length);
                break;
            default :
                $i = 0;
                $r = "";
                while($i ++ < $length) {
                    $r .= chr(mt_rand(0, 255));
                }
                break;
        }
        return substr(bin2hex($r), 0, $length);
    }
}

Firefox ve Chrome'da test etme

resim açıklamasını buraya girin

avantaj

  • Daha iyi güvenlik
  • Saldırgan için sınırlı erişim
  • Çerez çalındığında yalnızca tek erişim için geçerlidir
  • Orijinal kullanıcı siteye bir sonraki eriştiğinde, hırsızlığı otomatik olarak algılayabilir ve kullanıcıya bildirebilirsiniz.

dezavantaj

  • Birden çok tarayıcı (Mobil ve Web) üzerinden kalıcı bağlantıyı desteklemez
  • Kullanıcı bir sonraki girişten sonra bildirimi aldığından çerez hala çalınabilir.

Hızlı düzeltme

  • Kalıcı bağlantısı olması gereken her sistem için onay sisteminin tanıtılması
  • Kimlik doğrulaması için birden çok çerez kullanın

Çoklu Çerez Yaklaşımı

Bir saldırgan çerezleri çalmak üzereyken sadece belirli bir web sitesine veya alana odaklanın. example.com

Ancak gerçekten 2 farklı alan adından bir kullanıcının kimliğini doğrulayabilirsiniz ( adından example.com ve fakeaddsite.com ) doğrulayabilir ve "Advert Cookie" gibi görünmesini sağlayabilirsiniz

  • Kullanıcı example.com ile oturum açmış
  • Kullanıcı adını, jetonu, referansı çerezde sakla
  • Kullanıcı adını, jetonu, referansı Veritabanında saklayın örn. memcache
  • Get ve iframe yoluyla referans kimliği gönder Fakeaddsite.com adresine refrence gönderin
  • fakeaddsite.com, veritabanından kullanıcı ve jeton almak için referansı kullanır
  • fakeaddsite.com imzayı saklar
  • Bir kullanıcı fakeaddsite.com sitesinden iframe ile imza bilgilerini getirirken
  • Verileri birleştirin ve doğrulama yapın
  • ..... kalanını biliyorsun

Bazı insanlar 2 farklı kurabiyeyi nasıl kullanabileceğinizi merak edebilirler. Bu mümkün, hayal edin example.com = localhostve fakeaddsite.com = 192.168.1.120. Çerezleri incelerseniz şöyle görünür

resim açıklamasını buraya girin

Yukarıdaki görüntüden

  • Ziyaret edilen mevcut site localhost
  • Ayrıca 192.168.1.120'den ayarlanan çerezleri içerir

192.168.1.120

  • Yalnızca tanımlanmış kabuller HTTP_REFERER
  • Yalnızca belirtilen bağlantıyı kabul eder REMOTE_ADDR
  • JavaScript yok, İçerik yok, bilgi imzalamak ve çerez eklemek veya çerezden almak yerine hiçbir şey içermiyor

avantaj

  • Saldırganı kandırdığın zamanın% 99'u
  • Saldırganın ilk denemesinde hesabı kolayca kilitleyebilirsiniz
  • Diğer yöntemler gibi bir sonraki girişten önce bile saldırı önlenebilir

dezavantaj

  • Yalnızca tek bir giriş için sunucuya Çoklu İstek

Gelişme

  • Kullanım iframe kullanımı bitti ajax

5
@İrcmaxell teoriyi iyi tanımlamış olsa da, bu yaklaşımı tercih ediyorum, çünkü kullanıcı kimliğini (istenmeyen bir açıklama olacaktır) depolamaya gerek kalmadan mükemmel çalışıyor ve aynı zamanda yalnızca kullanıcı kimliği ve hash'ı tanımlamak için daha fazla parmak izi içeriyor. kullanıcı, tarayıcı gibi. Bu, bir saldırganın çalınan bir çerezden faydalanmasını daha da zorlaştırır. Şimdiye kadar gördüğüm en iyi ve en güvenli yaklaşım. +1
Marcello Mönkemeyer

6

Bu sorunun bir açısını sordum Burada ve cevaplar, ihtiyacınız olan tüm jeton tabanlı zaman aşımı çerez bağlantılarına götürecektir.

Temel olarak, kullanıcı kimliğini çerezde saklamıyorsunuz. Kullanıcının eski giriş oturumunu almak için kullandığı bir kerelik jetonu (büyük dize) depolarsınız. Sonra gerçekten güvenli hale getirmek için, ağır işlemler için bir parola istersiniz (parolanın kendisini değiştirmek gibi).


6

Eski iş parçacığı, ama yine de geçerli bir endişe. Güvenlik ve 'müstehcenlik yoluyla güvenlik' kullanmaktan kaçınmak için iyi yanıtlar fark ettim, ancak verilen gerçek teknik yöntemler gözlerimde yeterli değildi. Metoduma katkıda bulunmadan önce söylemem gerekenler:

  • ASLA bir şifreyi açık metin olarak saklamayın ... HİÇ!
  • ASLABir kullanıcının karma şifresini veritabanınızda birden fazla konumda saklamayın. Sunucu arka ucunuz her zaman karma parolayı kullanıcılar tablosundan alabilir. Fazladan verileri ek DB işlemleri yerine depolamak daha verimli değildir, tersi doğrudur.
  • Hiçbir iki kullanıcıları böylece Kişisel Oturum kimlikleri, benzersiz olmalıdır şimdiye , bir kimliği dolayısıyla amacını (sizin Ehliyet kimlik numarası hiç başka kişileri maç olabilir? Hayır.) Bu 2 dayanan, iki parçalı eşsiz bir kombinasyon oluşturur bir kimlik paylaşmak benzersiz dizeler. Oturumlar tablonuzun kimliği PK olarak kullanması gerekir. Otomatik olarak oturum açmak için birden fazla cihaza güvenilmesine izin vermek üzere, güvenilir cihazlar için, onaylanmış tüm cihazların listesini içeren (aşağıdaki örneğime bakın) ve kullanıcı adı kullanılarak eşlenen başka bir tablo kullanın.
  • Bilinen verileri bir çereze toplamak hiçbir amaca hizmet etmez, çerez kopyalanabilir. Aradığımız, kullanıcının makinesini tehlikeye atan bir saldırgan olmadan elde edilemeyen gerçek bilgileri sağlamak için uygun bir kullanıcı cihazıdır (yine örneğime bakın). Bununla birlikte, bu, makinesinin statik bilgilerini (yani MAC adresi, cihaz ana bilgisayar adı, tarayıcı tarafından kısıtlanmışsa kullanıcı vb.) Tutarlı kalmasını (veya ilk etapta taklit etmesini) yasaklayan meşru bir kullanıcının yapamayacağı anlamına gelir. bu özelliği kullanın. Ancak bu bir endişe kaynağıysa, kullanıcılara otomatik olarak oturum açmayı teklif ettiğiniz kendilerini benzersiz şekilde tanımlayanBu nedenle, MAC'larını taklit ederek, kullanıcılarını taklit ederek, ana bilgisayar adlarını taklit ederek / değiştirerek, proxy'lerin arkasında saklanarak vb. bilinmemeyi reddederse, tanımlanamazlar ve otomatik bir hizmet için asla doğrulanmamalıdır. Bunu istiyorsanız, kullanılan aygıt için kimlik kuran istemci tarafı yazılımla birlikte gelen akıllı kart erişimine bakmanız gerekir.

Bununla birlikte, sisteminizde otomatik olarak oturum açmanın iki harika yolu vardır.

İlk olarak, hepsini bir başkasına koyan ucuz ve kolay yol. Sitenizin, örneğin google + hesabınızla giriş yapmasını desteklerseniz, muhtemelen Google'da zaten oturum açmışlarsa kullanıcıyı oturum açacak aerodinamik bir google + düğmeniz vardır (bunu her zaman olduğu gibi bu soruyu cevaplamak için yaptım) Google'da oturum açtı). Kullanıcının zaten güvenilir ve desteklenen bir kimlik doğrulayıcı ile oturum açtıysa ve bunu yapmak için kutuyu işaretlediyse otomatik olarak oturum açmasını istiyorsanız, istemci tarafı komut dosyalarınızın yüklemeden önce ilgili 'oturum açma' düğmesinin arkasındaki kodu gerçekleştirmesini isteyin , sunucunun kullanıcı için kullanılan kullanıcı adı, oturum kimliği ve kimlik doğrulayıcıya sahip otomatik oturum açma tablosunda benzersiz bir kimlik depoladığından emin olun. Bu oturum açma yöntemleri AJAX kullandığından, yine de bir yanıt bekliyorsunuz, ve bu yanıt ya onaylanmış bir yanıt ya da bir rettir. Doğrulanmış bir yanıt alırsanız, normal olarak kullanın, ardından oturum açmış kullanıcıyı normal şekilde yüklemeye devam edin. Aksi takdirde, oturum açma başarısız oldu, ancak kullanıcıya söyleme, oturum açmadıkça devam et, fark edecekler. Bu, çerezleri çalan bir saldırganın (veya ayrıcalıkları yükseltme girişiminde bulunarak sahte) kullanıcının sitede otomatik olarak oturum açtığını öğrenmesini önlemektir.

Bu ucuzdur ve bazıları tarafından kirli olarak kabul edilebilir, çünkü Google ve Facebook gibi yerlerde zaten oturum açmış olduğunuz potansiyelinizi size söylemeden doğrulamaya çalışır. Ancak, sitenizde otomatik olarak oturum açmasını istemeyen kullanıcılarda kullanılmamalıdır ve bu özel yöntem yalnızca Google+ veya FB gibi harici kimlik doğrulaması içindir.

Harici bir kimlik doğrulayıcı sunucuya perde arkasında bir kullanıcının doğrulanıp onaylanmadığını söylemek için kullanıldığından, bir saldırgan tek başına işe yaramayan benzersiz bir kimlik dışında bir şey alamaz. Ayrıntılara gireceğim:

  • 'Joe' kullanıcısı siteyi ilk kez ziyaret etti, Oturum Kimliği çerez 'oturumuna' yerleştirildi.
  • 'Joe' kullanıcısı oturum açar, ayrıcalıkları yükseltir, yeni Oturum Kimliği alır ve çerez 'oturumunu' yeniler.
  • 'Joe' kullanıcısı google + kullanarak otomatik olarak oturum açmayı seçer, 'keepmesignedin' çerezine yerleştirilmiş benzersiz bir kimlik alır.
  • 'Joe' kullanıcısı, Google'ın oturumunuzu açık tutmasını sağlayarak sitenizin arka uçunuzda google'ı kullanarak kullanıcının otomatik olarak oturum açmasına olanak tanır.
  • Saldırgan, sistematik olarak 'keepmesignedin' için benzersiz kimlikler dener (bu her kullanıcıya dağıtılan genel bilgidir) ve başka hiçbir yerde oturum açmaz; 'joe' için verilen benzersiz kimliği dener.
  • Sunucu 'joe' için benzersiz bir kimlik alır, bir google + hesabı için DB'de eşleşme alır.
  • Sunucu, saldırganı google'a giriş yapmak için AJAX isteği çalıştıran giriş sayfasına gönderir.
  • Google sunucusu istek alır, Saldırganın şu anda giriş yapmadığını görmek için API'sını kullanır.
  • Google, bu bağlantı üzerinden şu anda oturum açmış bir kullanıcı bulunmadığına dair yanıt gönderir.
  • Saldırganın sayfası yanıt alır, komut dosyası giriş sayfasına otomatik olarak URL'de kodlanmış bir POST değeriyle yönlendirir.
  • Giriş sayfası POST değerini alır, otomatik bir girişimi caydırmak için 'keepmesignedin' tanımlama bilgisini boş bir değere ve 1-1-1970 tarihine kadar geçerli bir çerez göndererek Saldırganın tarayıcısının sadece çerezi silmesine neden olur.
  • Saldırgana normal ilk giriş sayfası verilir.

Ne olursa olsun, bir saldırgan var olmayan bir kimlik kullansa bile, onaylanmış bir yanıt alınma durumu dışında tüm denemelerde başarısız olmalıdır.

Bu yöntem, harici bir kimlik doğrulayıcı kullanarak sitenize giriş yapanlar için dahili kimlik doğrulayıcınızla birlikte kullanılabilir ve kullanılmalıdır.

=========

Şimdi, kullanıcıları otomatik olarak imzalayabilen kendi kimlik doğrulayıcı sisteminiz için şu şekilde yapıyorum:

DB'de birkaç tablo vardır:

TABLE users:
UID - auto increment, PK
username - varchar(255), unique, indexed, NOT NULL
password_hash - varchar(255), NOT NULL
...

Kullanıcı adının 255 karakter uzunluğunda olabileceğini unutmayın. Sunucu programım, sistemimdeki kullanıcı adlarını 32 karakterle sınırlandırdı, ancak harici doğrulayıcılar @ domain.tld ile kullanıcı adlarına sahip olabilir, bundan daha büyük olabilir, bu nedenle maksimum uyumluluk için sadece bir e-posta adresinin maksimum uzunluğunu destekliyorum.

TABLE sessions:
session_id - varchar(?), PK
session_token - varchar(?), NOT NULL
session_data - MediumText, NOT NULL

Oturum açıldığında kullanıcı adı oturum verilerinde olduğundan ve program boş verilere izin vermediğinden, bu tabloda kullanıcı alanı olmadığına dikkat edin. Session_id ve session_token rastgele md5 karmaları, sha1 / 128/256 karmaları, rasgele dizeleri eklenmiş tarih-saat damgaları kullanılarak oluşturulabilir veya sonra ne istersen, ama çıktının entropisi tolere edilebilir kadar yüksek kalmalıdır. kaba kuvvet saldırılarının zeminden bile düşmesini önleyin ve oturum sınıfınız tarafından oluşturulan tüm karmaların, eklenmeye çalışmadan önce oturum tablosundaki eşleşmeler açısından kontrol edilmesi gerekir.

TABLE autologin:
UID - auto increment, PK
username - varchar(255), NOT NULL, allow duplicates
hostname - varchar(255), NOT NULL, allow duplicates
mac_address - char(23), NOT NULL, unique
token - varchar(?), NOT NULL, allow duplicates
expires - datetime code

MAC adreslerinin doğası gereği BENZERSİZ olduğu varsayılır, bu nedenle her girişin benzersiz bir değeri olduğu mantıklıdır. Diğer yandan, ana bilgisayar adları ayrı ağlarda yasal olarak çoğaltılabilir. Bilgisayar isimlerinden biri olarak "Ev-PC" yi kaç kişi kullanıyor? Kullanıcı adı, sunucu arka ucu tarafından oturum verilerinden alınır, dolayısıyla manipüle edilmesi imkansızdır. Jetona gelince, kullanıcının otomatik oturum açması için çerezlerde jeton oluşturmak için sayfalar için oturum jetonları oluşturmak için kullanılan aynı yöntem kullanılmalıdır. Son olarak, datetime kodu kullanıcının kimlik bilgilerini yeniden doğrulaması gerektiğinde eklenir. Bu tarih saatini birkaç gün içinde tutarak kullanıcı girişinde güncelleyin veya son girişten bağımsız olarak, tasarımınız ne olursa olsun, yalnızca bir ay kadar saklamak için süresinin dolmasını sağlayın.

Bu, birinin otomatik oturum açtığını bildiği bir kullanıcı için MAC ve ana bilgisayar adını sistematik olarak taklit etmesini önler. ASLAKullanıcının şifresini, açık metnini veya başka bir şekilde bir çerezi tutmasını sağlayın. Tıpkı oturum belirtecinde yaptığınız gibi, her sayfa gezinmesinde jetonun yeniden oluşturulmasını sağlayın. Bu, bir saldırganın geçerli bir belirteç çerezi alma ve bunu oturum açmak için kullanma olasılığını büyük ölçüde azaltır. Bazı insanlar bir saldırganın kurbandan gelen çerezleri çalabileceğini ve giriş yapmak için bir oturum tekrar saldırısı yapabileceğini söylemeye çalışacaktır. Bir saldırgan çerezleri çalabilirse (ki bu mümkün olabilir), tüm cihazı kesinlikle tehlikeye atacaktı, yani cihazı zaten giriş yapmak için kullanabilirler, bu da çerezleri tamamen çalma amacını bozar. Siteniz HTTPS üzerinde (şifreler, CC numaraları veya diğer giriş sistemleriyle uğraşırken) çalıştığı sürece, kullanıcıya bir tarayıcıda yapabileceğiniz tüm korumayı sağladınız.

Akılda tutulması gereken bir şey: otomatik oturum açma özelliğini kullanırsanız oturum verilerinin süresi dolmamalıdır. Oturumu hatalı bir şekilde devam ettirme yeteneğini sona erdirebilirsiniz, ancak sisteme doğrulamanın, oturumlar arasında devam etmesi beklenen kalıcı verilerse oturum verilerini sürdürmesi gerekir. Hem kalıcı hem de kalıcı olmayan oturum verileri istiyorsanız, kalıcı oturum verileri için PK olarak kullanıcı adıyla başka bir tablo kullanın ve sunucunun normal oturum verisinde olduğu gibi almasını sağlayın, başka bir değişken kullanın.

Bu şekilde oturum açıldığında, sunucunun oturumu yine de doğrulaması gerekir. Burası çalınan veya güvenliği ihlal edilen sistemlere ilişkin beklentileri kodlayabileceğiniz yerdir; oturum verilerinin oturum açma kalıpları ve diğer beklenen sonuçları genellikle erişim elde etmek için bir sistemin ele geçirildiği veya çerezlerin sahte olduğu sonucuna yol açabilir. ISS Tech'iniz, bir kullanıcının otomatik olarak oturum açma sisteminden bir hesap kilitlenmesini veya otomatik olarak kaldırılmasını tetikleyecek kurallar koyabildiği ve saldırganların saldırganın nasıl başarılı olduğunu ve nasıl kesileceğini belirleyebileceği kadar uzun süre uzak tutabileceği yerdir.

Kapanış notu olarak, eşiği aşan herhangi bir kurtarma girişimi, parola değişikliği veya oturum açma hatasının, kullanıcı doğru bir şekilde onaylanana ve bunun gerçekleşinceye kadar otomatik oturum açmanın devre dışı bırakılmasına neden olduğundan emin olun.

Cevabımda kimse kodun verilmesini bekleseydi özür dilerim, bu burada olmayacak. Sitelerimi çalıştırmak için PHP, jQuery ve AJAX kullandığımı ve Windows'u asla sunucu olarak kullanmadığımı söyleyeceğim.



4

Belki sadece bildiğiniz bir sır ile bir karma oluşturun, daha sonra kullanıcıyla ilişkilendirilebilmesi için DB'nizde saklayın. Oldukça iyi çalışmalı.


Bu, kullanıcı oluşturulduğunda oluşturulan benzersiz bir tanımlayıcı mıdır yoksa kullanıcı yeni bir "Oturum Açma Tut" çerezi oluşturduğunda değişir mi?
Matthew

1
Tim Jansson'ın cevabı, şifreyi
içermezse

2

Benim çözümüm böyle. % 100 kurşun geçirmez değil, ancak çoğu durumda sizi kurtaracağını düşünüyorum.

Kullanıcı başarıyla oturum açtığında bu bilgileri içeren bir dize oluşturun:

$data = (SALT + ":" + hash(User Agent) + ":" + username 
                     + ":" + LoginTimestamp + ":"+ SALT)

Şifrele $data, türü HttpOnly olarak ayarla ayarlayın ve ayarlayın.

Kullanıcı sitenize geri döndüğünde, şu adımları uygulayın:

  1. Çerez verilerini alın. Çerezin içindeki tehlikeli karakterleri kaldırın. Şununla patlat::Karakterle .
  2. Geçerliliği kontrol edin. Çerez X günden eskiyse kullanıcıyı giriş sayfasına yönlendirin.
  3. Çerez eski değilse; Veritabanından en son şifre değiştirme zamanını alın. Kullanıcının son girişinden sonra şifre değiştirilirse kullanıcıyı giriş sayfasına yönlendirir.
  4. Geçiş son zamanlarda değiştirilmediyse; Kullanıcının mevcut tarayıcı aracısını edinin. (CurrentUserAgentHash == cookieUserAgentHash) öğesinin olup olmadığını kontrol edin. Ajanları aynıysa bir sonraki adıma geçin, aksi takdirde giriş sayfasına yönlendirin.
  5. Tüm adımlar başarıyla tamamlanırsa kullanıcı adını yetkilendirin.

Kullanıcı oturum açarsa, bu çerezi kaldırın. Kullanıcı yeniden oturum açarsa yeni çerez oluşturun.


2

Bilgisayar korsanlığınızı yapmanız gereken şifreli bir sürüm olduğunda, şifreli şeyleri bir çerezde saklama kavramını anlamıyorum. Eğer bir şey eksiksem, lütfen yorum yap.

Bu yaklaşımı 'Beni Hatırla'ya almayı düşünüyorum. Herhangi bir sorun görebiliyorsanız, lütfen yorum yapın.

  1. "Beni Hatırla" verilerini depolamak için bir tablo oluşturun - birden çok cihazdan oturum açabilmem için kullanıcı tablosundan ayrı.

  2. Başarılı girişte (Beni Hatırla işaretli olarak):

    a) Bu makinede UserID olarak kullanılacak benzersiz bir rastgele dize oluşturun: bigUserID

    b) Benzersiz bir rastgele dize oluşturun: bigKey

    c) Bir çerez saklayın: bigUserID: bigKey

    d) "Beni Hatırla" tablosuna aşağıdakileri içeren bir kayıt ekleyin: UserID, IP Address, bigUserID, bigKey

  3. Giriş gerektiren bir şeye erişmeye çalışıyorsanız:

    a) Çerezi kontrol edin ve eşleşen bir IP adresiyle bigUserID ve bigKey'i arayın

    b) Eğer bulursanız, herhangi bir tehlikeli işlem için tam bir giriş isteyebilmeniz için, kişiyi giriş yapın, ancak "yumuşak giriş" kullanıcı tablosunda bir bayrak ayarlayın.

  4. Çıkışta, söz konusu kullanıcının tüm "Beni Hatırla" kayıtlarını süresi dolmuş olarak işaretleyin.

Görebildiğim tek güvenlik açıkları;

  • birinin dizüstü bilgisayarını ele geçirebilir ve IP adresini çerezle taklit edebilirsiniz.
  • her seferinde farklı bir IP adresi taklit edebilir ve her şeyi tahmin edebilirsiniz - ama maç için iki büyük dize ile, bu ... yukarıdakine benzer bir hesaplama yapmak ... Hiçbir fikrim yok ... büyük olasılıklar?

Merhaba, bu cevap için teşekkürler, beğendim. Yine de bir soru: neden 2 rastgele dizge oluşturmak zorundasınız - bigUserID ve bigKey? Neden sadece 1 üretip kullanmıyorsunuz?
Jeremy Belolo

2
bigKey, önceden tanımlanmış bir süre sonra sona erer, ancak bigUserID bunu yapmaz. bigUserID, aynı IP adresindeki farklı cihazlarda birden fazla oturum açmanıza izin vermektir. Mantıklı geliyor - Bir an düşünmek zorunda kaldım :)
Enigma Plus

Hmac yardımcı olabilecek bir şey, hmac kurcalanmış bulduysanız, birisinin çerez çalmaya çalıştığını kesinlikle biliyorsunuz, o zaman her giriş durumunu sıfırlayabilirsiniz. Haklı mıyım?
Suraj Jain

2

Tüm cevapları okudum ve yapmam gereken şeyi çıkarmakta zorlandım. Bir resim 1k kelimeye bedelse, umarım bu başkalarının Barry Jaspan'ın Geliştirilmiş Kalıcı Giriş Çerezi En İyi Uygulamasına dayanan güvenli bir kalıcı depolama uygulaması yapmasına yardımcı olur.

resim açıklamasını buraya girin

Sorularınız, geri bildirimleriniz veya önerileriniz varsa, güvenli ve kalıcı bir giriş yapmaya çalışan acemi için diyagramı güncellemeye çalışacağım.


0

"Beni Oturum Açma" özelliğini uygulamak, kullanıcı için tam olarak ne anlama geldiğini tanımlamanız gerektiği anlamına gelir. En basit durumda, bunu oturumun çok daha uzun bir zaman aşımına uğraması anlamına gelir: 2 saat yerine 2 gün (diyelim). Bunu yapmak için, muhtemelen bir veritabanında kendi oturum depolama alanınıza ihtiyacınız olacaktır, böylece oturum verileri için özel süre sonu süreleri ayarlayabilirsiniz. Ardından, tarayıcıyı kapattıklarında sona ermek yerine birkaç gün (veya daha uzun) süre boyunca yapışacak bir çerez ayarladığınızdan emin olmanız gerekir.

"Neden 2 gün? Neden 2 hafta değil?" Diye sorduğunu duyabiliyorum. Bunun nedeni, PHP'de bir oturum kullanmak sürenin otomatik olarak sona ermesine neden olacaktır. Bunun nedeni PHP'de bir oturumun süresinin dolmasının aslında boşta kalma bir zaman aşımı olmasıdır.

Şimdi, bunu söyledikten sonra, muhtemelen oturumun kendisinde ve 2 hafta kadar sakladığım daha zor bir zaman aşımı değeri uygularım ve bunu görmek ve oturumu zorla geçersiz kılmak için kod eklerdim. Ya da en azından oturumu kapatmak için. Bu, kullanıcıdan düzenli olarak oturum açması isteneceği anlamına gelir. Yahoo! bunu yapar.


1
Sunucu kaynaklarını boşa
harcadığı

0

Bence bunu yapabilirsin:

$cookieString = password_hash($username, PASSWORD_DEFAULT);

DB'de $cookiestringsaklayın ve bir çerez olarak ayarlayın. Ayrıca kişinin kullanıcı adını çerez olarak ayarlayın. Bir karma işlemin tüm amacı, tersine mühendislik yapılamamasıdır.

Bir kullanıcı geldiğinde, kullanıcı adını bir çerezden $cookieStringdiğerinden almak. $cookieStringDB'de depolananla eşleşirse , kullanıcının kimliği doğrulanır. Password_hash her seferinde farklı bir tuz kullandığından, açık metnin ne olduğu ile ilgisi yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.