"Kayıtlı Me tutun"

11 Cevap php

Benim web uygulaması onlar giriş yaptıktan sonra kullanıcı hakkında bilgi saklamak için, ve onlar uygulaması içinde sayfadan sayfaya seyahat olarak bu bilgileri korumak için oturumları kullanır. Bu özel uygulamada, bir user_id, first_name ve last_name kişinin depolama var.

Ben sunmak istiyorum bir olmasıyla günlüğüne seçeneğinde "Oturum Açmış Keep Me" onlar app döndüğünüzde aynı ayrıntıları ile oturumu yeniden başlayacağını, iki hafta boyunca kullanıcının makinesinde bir çerez koyacağız.

Bunu yapmak için en iyi yaklaşım nedir? Ben bu kolay bir kullanıcı başka bir kullanıcının kimliğini denemek ve oluşturmak için yapacak gibi görünüyor gibi, çerez onların user_id saklamak istemiyorum.

11 Cevap

Genellikle böyle bir şey yapın:

1) User logs in with 'keep me logged in'
2) Create session
3) Create a cookie called SOMETHING containing: md5(salt+username+ip+salt) and a cookie called somethingElse containing id
4) store cookie in database
5) user does stuff and leaves ----
6) user returns, check for somethingElse cookie, if it exists, get the old hash from the database for that user, check of the contents of cookie SOMETHING match with the hash from the database, which should also match with a newly calculated hash (for the ip) thus: cookieHash==databaseHash==md5(salt+username+ip+salt), if they do, goto 2, if they don't goto 1

ders dışı sadece emin kolayca yaratılmış değil yapmak, ayrıca çerez içeriğini biraz değiştirebilirsiniz farklı cookie isimlerini vb kullanabilirsiniz. Siz kullanıcı oluşturulduğunda ayrıca örneğin bir user_salt oluşturmak ve aynı zamanda çerez koymak olabilir.

Ayrıca yerine md5 sha1 kullanabilirsiniz (ya da hemen hemen herhangi bir algoritma) olabilir

Tamam, bana açık açık koyalım: Eğer kullanıcı verileri ya da bu amaç için bir çerez içine kullanıcı verilerinden türetilen bir şey koyarak yapıyorsanız, yanlış bir şey yapıyoruz.

Orada. Bunu söyledi. Şimdi gerçek cevap için taşıyabilirsiniz.

Kullanıcı verileri karma nesi var, sorabilir miyim? Peki, bu bilinmezlik yoluyla maruziyet yüzeye ve güvenlik aşağı gelir.

Eğer bir saldırgan olduğunuzu bir an için düşünün. Sen bir şifreleme çerez seti görmek hatırlıyorum-Beni senin oturum. Geniş 32 karakter bulunuyor. Gee. Bu bir MD5 olabilir ...

Kullanıcının da onlar kullanılan algoritma biliyoruz ki bir saniye için hayal edelim. Örneğin:

md5(salt+username+ip+salt)

Şimdi, bütün bir saldırgan, kaba kuvvet (gerçekten bir tuz değil ki, ama daha sonra) "tuz" yapması gereken, ve o şimdi onun IP adresi için herhangi bir kullanıcı adı ile istediği tüm sahte belirteçleri üretebilirsiniz! Ama tabi brute-zorlayan bir tuz doğru, zor? Kesinlikle. Ama modern GPU'lar bunda fazlasıyla iyi. Eğer (yeterince büyük yapmak) bunun yeterli rastgeleliğine kullanmadığınız sürece, bu sizin kaleye anahtarları hızla düşecek, ve onunla oluyor.

Kısacası, sizi koruyan tek şey, gerçekten size düşünmek kadar korumak değil tuzdur.

But Wait!

Bütün bunlar saldırgan algoritmayı bildiği esas oldu! Bu sır ve kafa karıştırıcı ise, o zaman doğru, güvenli mi? WRONG. Security Through Obscurity, NEVER üzerine kuruluydu edilmelidir: Bu düşünce şekli bir adı vardır.

The Better Way

Daha iyi bir yolu, bir kullanıcının bilgisi kimliği dışında, sunucuyu terk etmesine asla izin etmektir.

Kullanıcı oturum açtığında, bir büyük (128-256 bit) rasgele belirteç oluşturmak. Kullanıcı kimliği belirteci eşleyen bir veritabanı tablosuna eklemek, ve sonra çerez istemciye göndermek.

Ne saldırganın başka bir kullanıcı rastgele belirteci Bilirse?

Peki, burada bazı matematik yapalım. Biz 128 bit rastgele bir belirteci üreten ediyoruz. O olduğu anlamına gelir:

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

Şimdi, bu sayı ne kadar saçma sapan büyük göstermek için, her bir saniyede 1000000000 oranında bu sayı kaba-kuvvet çalışıyor (en 50,000,000 bugün diyelim) adlı internet üzerindeki her bir sunucu düşünelim. Gerçekte sizin sunucuları, yük altında eriyip, ama en bu dışarı oynayalım istiyorum.

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 Yani tahmin. Bu hızlı! Değil mi?

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

Yani 6.8 sekstilyon saniye ...

En fazla dostu numaralara aşağı getirmek için çalışalım.

215,626,585,489,599 years

Veya daha da iyisi:

47917

Evet, bu evrenin 47917 kez yaşı ...

Temelde, kırık olacak değil.

Yani özetlemek için:

Ben tavsiye iyi bir yaklaşım üç parça ile çerez 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 ($mac !== hash_hmac('sha256', $user . ':' . $token, SECRET_KEY)) {
            return false;
        }
        $usertoken = fetchTokenByUserName($user);
        if (timingSafeCompare($usertoken, $token)) {
            logUserIn($user);
        }
    }
}

Şimdi, SECRET_KEY (/dev/random ve / veya yüksek-entropi girişten türetilen gibi bir şey tarafından oluşturulan) bir kriptografik gizli olması very önemlidir. Ayrıca, GenerateRandomToken() güçlü bir rasgele bir kaynak olması gerekir (mt_rand() yeteri kadar güçlü değil. DEV_URANDOM ile bir kütüphane veya mcrypt kullanın) ...

Ve timingSafeCompare önlemektir timing attacks. Böyle bir şey:

/**
 * 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) {
    // Prevent issues if string length is 0
    $safe .= chr(0);
    $user .= chr(0);

    $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;
}

Introduction

Sizin title “Keep Me Logged In” - the best approach zor size en iyi yaklaşım arıyorsanız o zaman dikkate şu olurdu bana nerede çünkü başlatmak için bilmek için yapmak:

  • Kimlik
  • Güvenlik

Cookies

Çerezler ortak bir tarayıcı çerez hırsızlık güvenlik açıkları ve cross-site scripting saldırıları arasında biz çerezleri güvenli olmadığını kabul etmeliyiz, savunmasız. Güvenliğini artırmak yardım etmek için php setcookies gibi ek işlevler olduğunu belirtmeliyiz

bool setcookie (string $ isim [, string $ değer [$ = 0 sona int [, string $ yol [, string $ domain [, bool {[(1)] false} [, bool {= [(2)]} = false]]]]]])

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

Definitions

  • Token (n uzunluğu örneğin Kestirilemeyen rasgele dize. / Dev / urandom)
  • Referans (n ​​uzunluğu örneğin Kestirilemeyen rasgele dize. / Dev / urandom)
  • İmza (HMAC yöntemini kullanarak bir anahtarlı karma değeri oluşturmak)

Simple Approach

Basit bir çözüm olacaktır:

  • Kullanıcı ile oturum Beni Hatırla
  • Cookie belirteç & verilecektir Oturum Imza
  • Dönerken, İmza kontrol edilir
  • İmza ok .. Eğer o kullanıcı adı ve belirteç veritabanında aranır
  • eğer geçerli değilse .. Giriş sayfasına dönmek
  • Geçerli otomatik olarak giriş yaparsanız

Yukarıdaki vaka çalışması bu sayfada verilen tüm örnek özetleyen ama onlar olduğunu dezavantajları

  • Çerezleri çalıntı olup olmadığını bilmek için hiçbir yolu yoktur
  • Saldırgan parola ya da vb kişisel ve pişirme bilgileri gibi verilerin değişikliği gibi erişim hassas operasyonlar olabilir
  • Tehlikeye çerez hala çerez ömrü için geçerli olacağını

Better Solution

Daha iyi bir çözüm olacaktır

  • Kullanıcı giriş ve beni seçilir hatırladın mı
  • Üret Token & çerez imza ve mağaza
  • Belirteçleri rastgele ve tek Doğrulaması için geçerlidir
  • Belirteç siteye her ziyaret yerini alır
  • Olmayan bir oturum açmış kullanıcının siteyi imza ziyaret ettiğinizde, jeton ve kullanıcı adı doğrulanmış
  • Login vs şifre değişikliği, kişisel bilgileri izin kısıtlı erişimi ve olmamalıdır beni hatırla

Example Code

// 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());
}

Class Used

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);
    }
}

Testing in Firefox & Chrome

enter görüntü açıklaması here

Advantage

  • Better Güvenlik
  • Saldırganın için sınırlı erişim
  • Çerez tek erişim için onun sadece geçerli çalındığında
  • Yanında özgün kullanıcı sitesine eriştiğinizde otomatik olarak algılar ve hırsızlık kullanıcıya bildirebilirsiniz

Disadvantage

  • Birden fazla tarayıcı üzerinden sürekli bağlantı desteklemiyor (Mobil ve Web)
  • Kullanıcı sadece sonraki açılıştan sonra bildirimini alır çünkü çerez hala çalıntı olabilir.

Quick Fix

  • Kalıcı bir bağlantı olması gerekir, her sistem için onay sisteminin tanıtımı
  • Kimlik doğrulaması için birden fazla çerez kullanın

Multiple Cookie Approach

Bir saldırganın yalnızca belirli bir web sitesi veya etki alanı örneğin odaklanmaya Cookes çalmak üzereyken. example.com

Ama gerçekten 2 farklı etki bir kullanıcı kimlik doğrulaması (example.com ve fakeaddsite.com) ve "Reklam Cookie" gibi görünmesini sağlayabilirsiniz

  • example.com ile devam Kayıtlı kullanıcı beni hatırla
  • Cookie mağaza adınızı, jeton, referans
  • Mağaza adınızı, jeton, Veritabanı örneğin referans. Memcache
  • fakeaddsite.com için get ve iframe yoluyla refrence id gönder
  • fakeaddsite.com kullanıcı almak için başvuruyu kullanır ve Veritabanından belirteç
  • fakeaddsite.com imza saklar
  • Bir kullanıcı fakeaddsite.com gelen iframe ile imza bilgi alıp dönerken
  • O verileri birleştirin ve doğrulama yapmak
  • ..... Sen kalan biliyor

Bazı insanlar nasıl 2 farklı çerezleri kullanabilir merak edebilirsiniz? Peki olası, example.com = localhost ve fakeaddsite.com = 192.168.1.120 düşünün. Eğer çerezleri kontrol eğer böyle olmazdı

enter görüntü açıklaması here

Yukarıdaki resimden

  • Ziyaret Geçerli site localhost'dur
  • Ayrıca 192.168.1.120 ayarlanır tanımlama bilgilerini içeren

192.168.1.120

  • Sadece tanımlanmış kabul HTTP_REFERER
  • Yalnızca belirtilen REMOTE_ADDR gelen bağlantı kabul eder
  • Hayır JavaScript, Hiçbir içerik şey oluşmalıdır ziyade bilgiyi imzalamak ve eklemek veya çerez onu almak

Advantage

  • Eğer saldırgan kandırdın zaman% 99 oranında
  • Kolayca saldırganın ilk denemede hesabı kilitleyebilir
  • Saldırı hatta diğer yöntemleri gibi bir sonraki oturum önce önlenebilir

Disadvantage

  • Sadece tek bir oturum için sunucuya çoklu Talebi

Improvement

  • Bitti kullanım iframe kullanımı ajax

Iki çok ilginç yazılar "hatırlamak-me"-sorun için mükemmel bir çözüm ararken buldum, vardır:

Ben bu soruya here biri açısını sordu ve cevaplar ihtiyacınız olan tüm belirteci tabanlı zamanlama-out cookie bağlantıları götürecektir.

Temelde, çerez UserID tutmayın. Siz kullanıcı pick-up için eski bir oturum açma oturumu kullanan bir kerelik belirteç (büyük dize) saklayın. O zaman gerçekten güvenli hale getirmek için, (parola kendisini değiştirme gibi) ağır işlemler için bir şifre soracaktır.

Ben Stefan (yani Improved Persistent Login Cookie Best Practice yönergeleri izleyin) ve ayrıca kurabiyeler emin olun öneririz tarafından belirtilen yaklaşımı öneriyoruz HttpOnly cookies yüzden, JavaScript potansiyel kötü niyetli erişilebilir değil .

Bu kullanıcı ile ilişkili olabilir belki bilirsin, sadece bir sır ile, bir karma oluşturmak, o zaman DB depolamak. Oldukça iyi çalışması gerekir.

"Çerez" hash çeşit, adını + kullanın md5($ip, $username, $password)? Bu benim önerim olurdu.

Ve if(md5($_SERVER['REMOTE_ADDR'], $username, $password) = $cookiehash)? :)

Benim çözüm bu gibi. % 100 kurşun geçirmez değil ama vakaların çoğu için tasarruf düşünüyorum.

Kullanıcı giriş yaptığında başarıyla bu bilgileri içeren bir dize oluşturun:

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

HttpOnly ve set çerez kakmak $data, set türü.

Kullanıcı sitenize geri geldiğinde, bu adımları Üyeler:

  1. Çerez verilerini alın. Cookie içinde tehlikeli karakterleri kaldırın. : karakteri ile patlayabilir.
  2. Geçerliliğini kontrol edin. Cookie X günden eski ise Giriş sayfasına yönlendirir.
  3. Çerez eski değilse; Veritabanından son şifre değiştirme zaman alın. Şifre sayfasına giriş kullanıcının son giriş yönlendirme kullanıcı sonra değiştirilir.
  4. Geçişli en son değiştirilen değilse; Kullanıcının geçerli tarayıcı ajan alın. (CurrentUserAgentHash == cookieUserAgentHash) olup olmadığını kontrol edin. Maddeleri bir sonraki adıma gidin aynıdır IF, başka bir giriş sayfasını yönlendirmek.
  5. Tüm adımlar Geçerse başarıyla adınızı yetki.

Kullanıcı signouts, bu çerezi kaldırmak. Yeni çerez oluşturmak kullanıcı yeniden giriş eğer.

Ben size hack yapmanız gereken bunun şifreli sürümü olduğunda bir tanımlama şifreli şeyler depolama kavramını anlamıyorum. Ben bir şey eksik varsa, yorum lütfen.

Ben 'Beni Hatırla' Bu yaklaşım hakkında düşünüyorum. Eğer herhangi bir sorun görürseniz, yorum lütfen.

  1. Ben birden fazla cihazdan giriş böylece kullanıcı tabloya ayırmak - saklamak "Remember Me" verilerini de bir tablo oluşturun.

  2. Başarılı bir girişten (İşaretli Beni Hatırla ile) On:

    bigUserID: a) Bu makinede kullanıcı kimliği olarak kullanılacak benzersiz bir rasgele bir dize oluşturun

    bigKey: b) benzersiz rasgele bir dize oluşturun

    c) bir çerez Mağaza: bigUserID: bigKey

    UserID, IP Adresi, bigUserID, bigKey: d) "Beni hatırla" tablosunda, bir kayıt eklemek

  3. Giriş gerektiren bir şey erişmeye çalışıyor ise:

    a) çerez için kontrol edin ve bigUserID & aramak eşleşen bir IP adresi ile bigKey

    b) Eğer bulursanız, kişiyi login ancak herhangi bir tehlikeli operasyonlar için, tam bir giriş soracak böylece kullanıcı tablosundaki "yumuşak login" bir bayrak set.

  4. Logout, Mark tüm dolmuş olarak bu kullanıcı için kayıtları "Beni Hatırla".

Görebildiğim tek açıkları olduğunu;

  • Birinin laptop ele almak ve çerez ile kendi IP adresini sızdırarak olabilir.
  • Eğer farklı bir IP adresi her zaman taklit etmek ve her şeyi tahmin olabilir - ama iki büyük dize eşleştirmek için, yukarıda benzer bir hesaplama yapıyor ... olurdu ... Hiçbir fikrim yok ... büyük ihtimal?

Uygulayan bir özelliği olduğunu kullanıcıya demek tam olarak ne olduğunu tanımlamak gerekiyor demektir "Oturum Açmış Keep Me". Yerine 2 saat 2 gün (diyelim ki): basit durumda, ben oturumu çok daha uzun bir zaman aşımı vardır demek için kullanmak istiyorsunuz. Bunu yapmak için, muhtemelen bir veritabanında, kendi oturum depolama ihtiyacınız olacak, bu nedenle oturum verisi için özel bitiş zamanlarını ayarlayabilirsiniz. Sonra emin bir kaç gün (veya daha uzun) ayrılmamak, yerine onlar tarayıcınızı kapattığınızda sona erecek bir cookie set yapmak gerekir.

Ben "neden 2 gün neden olmasın? 2 hafta?" Diye sorduğunuzu duyabiliyorum. PHP bir oturumu kullanarak otomatik olarak geri bitiminden itecektir çünkü bu. PHP bir oturumun bitiş aslında bir boş zaman aşımı olmasıdır.

Şimdi, muhtemelen 2 hafta ya da öylesine ben oturumda kendisi saklamak daha zor bir zaman aşımı değerini uygulamak, ve dışarı ve görmek için ve zorla oturumu geçersiz kod eklemek istiyorum, dedi sahip. Ya da en azından onları oturum. Bu kullanıcı periyodik giriş istenecek anlamına gelecektir. Yahoo! yapar.