문제

내 웹 애플리케이션은 세션을 사용하여 사용자가 로그인한 후 사용자에 대한 정보를 저장하고 앱 내에서 페이지 간 이동 시 해당 정보를 유지합니다.이 특정 애플리케이션에서는 user_id, first_name 그리고 last_name 사람의.

로그인 시 "로그인 상태 유지" 옵션을 제공하고 싶습니다. 그러면 사용자 컴퓨터에 2주 동안 쿠키가 저장되고, 앱으로 돌아올 때 동일한 세부 정보로 세션이 다시 시작됩니다.

이를 수행하는 가장 좋은 방법은 무엇입니까?나는 그것들을 저장하고 싶지 않다 user_id 쿠키에 포함하면 한 사용자가 다른 사용자의 신원을 쉽게 위조할 수 있는 것처럼 보입니다.

도움이 되었습니까?

해결책

좋아,이 문제를 해결하겠습니다. 사용자 데이터를 넣거나 사용자 데이터에서 파생 된 것이이 목적을 위해 쿠키에 도출된다면, 당신은 뭔가 잘못하고 있습니다.

거기. 내가 말했어. 이제 우리는 실제 답변으로 넘어갈 수 있습니다.

해싱 사용자 데이터에 어떤 문제가 있습니까? 글쎄, 그것은 모호함을 통해 노출 표면과 보안으로 이어집니다.

당신이 공격자라고 잠시 동안 상상해보십시오. 세션에서 기억에 대 한 암호화 쿠키 세트가 표시됩니다. 너비는 32 자입니다. 말. 그것은 MD5 일 수 있습니다 ...

또한 그들이 사용한 알고리즘을 알고 있다고 잠시 상상해 봅시다. 예를 들어:

md5(salt+username+ip+salt)

이제 공격자가해야 할 모든 공격자는 "소금"(실제로 소금이 아니라 나중에는 더 많은 소금)을 무차별 한 힘으로, 이제 그는 자신의 IP 주소에 대한 사용자 이름으로 원하는 모든 가짜 토큰을 생성 할 수 있습니다! 그러나 소금을 촉진하는 무차별은 어렵습니다. 전적으로. 그러나 현대 GPU는 매우 능숙합니다. 그리고 당신이 그것에 충분한 무작위성을 사용하지 않으면 (충분히 크게 만드십시오), 그것은 빨리 떨어질 것이고, 그것은 당신의 성의 열쇠를 사용합니다.

요컨대, 당신을 보호하는 유일한 것은 소금입니다. 소금은 실제로 당신을 생각하는 것만 큼 당신을 보호하지 않습니다.

하지만 기다려!

이 모든 것은 공격자가 알고리즘을 알고 있다는 사실을 전제로했습니다! 그것이 비밀스럽고 혼란 스럽다면 안전합니다. 잘못된. 그 사고 라인에는 이름이 있습니다. 모호함을 통한 보안,해야 할 절대 의존해야합니다.

더 나은 방법

더 좋은 방법은 ID를 제외하고 사용자의 정보가 서버를 떠나지 않도록하는 것입니다.

사용자가 로그인하면 큰 (128 ~ 256 비트) 임의의 토큰을 생성합니다. 토큰을 userID에 매핑하는 데이터베이스 테이블에 추가 한 다음 쿠키의 클라이언트에게 보내십시오.

공격자가 다른 사용자의 임의의 토큰을 추측하면 어떻게됩니까?

글쎄, 여기서 수학을 해보자. 우리는 128 비트 랜덤 토큰을 생성하고 있습니다. 그것은 다음이 있음을 의미합니다.

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

이제 그 숫자가 터무니없이 얼마나 큰지를 보여주기 위해, 인터넷의 모든 서버 (오늘 50,000,000이라고 가정 해 봅시다)가 초당 1,000,000,000의 속도로 그 숫자를 무차별하려고 시도해 보자. 실제로 귀하의 서버는 그러한 부하 하에서 녹을 것이지만, 이것을 재생합시다.

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

따라서 초당 50 만 명의 추측입니다. 빠릅니다! 오른쪽?

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 초 ...

더 친근한 숫자로 가져 오도록합시다.

215,626,585,489,599 years

또는 더 나은 :

47917 times the age of the universe

예, 그것은 우주의 나이의 47917 배입니다 ...

기본적으로 갈라지지 않을 것입니다.

요약하려면 :

내가 추천하는 더 좋은 접근법은 쿠키를 세 부분으로 보관하는 것입니다.

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

그런 다음 검증하기 위해 :

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

참고 : 사용자와 토큰의 조합을 사용하여 데이터베이스에서 레코드를 조회하지 마십시오. 항상 사용자를 기반으로 레코드를 가져와 타이밍 안전 비교 기능을 사용하여 나중에 가져온 토큰을 비교하십시오. 타이밍 공격에 대한 자세한 내용.

이제, 그것은입니다 매우 중요합니다 SECRET_KEY 암호화 비밀 (와 같은 것에 의해 생성됩니다 /dev/urandom 및/또는 하이 엔트로피 입력에서 파생 된). 또한, GenerateRandomToken() 강력한 무작위 소스 여야합니다 (mt_rand() 거의 강하지 않습니다. 다음과 같은 라이브러리를 사용하십시오 randomlib 또는 random_compat, 또는 mcrypt_create_iv() ~와 함께 DEV_URANDOM)...

그만큼 hash_equals() 방지하는 것입니다 타이밍 공격. PHP 5.6 미만의 PHP 버전을 사용하는 경우 기능 hash_equals() 지원되지 않습니다. 이 경우 교체 할 수 있습니다 hash_equals() TimingSafeCompare 기능으로 :

/**
 * 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;
}

다른 팁

보안 통지: 쿠키를 결정 론적 데이터의 MD5 해시에서 바탕으로하는 것은 나쁜 생각입니다. CSPRNG에서 파생 된 임의의 토큰을 사용하는 것이 좋습니다. 보다 Ircmaxell의 대답 보다 안전한 접근 방식을 위해이 질문에.

보통 나는 다음과 같이합니다.

  1. 사용자 로그인 '로그인 유지'로 로그인
  2. 세션을 만듭니다
  3. MD5 (Salt+Username+IP+Salt) 및 Something Something inclating ID라는 쿠키라는 쿠키 만들기
  4. 쿠키를 데이터베이스에 저장하십시오
  5. 사용자는 물건과 잎을합니다 ----
  6. 사용자 반환, SomethingElse 쿠키를 확인하십시오. 쿠키가 존재하는 경우 해당 사용자의 데이터베이스에서 이전 해시를 가져 와서 쿠키의 내용을 데이터베이스에서 해시와 일치하는 내용을 확인하고 새로 계산 된 해시와 일치해야합니다 ( 따라서 ip) : cookiehash == databasehash == md5 (salt+username+ip+salt), 그들이 할 때, 그들이 얻지 못하면 1

코스는 다른 쿠키 이름 등을 사용할 수 있습니다. 또한 쿠키의 내용을 약간 변경할 수 있습니다. 쉽게 만들지 않도록하십시오. 예를 들어 사용자가 생성 될 때 user_salt를 만들고 쿠키에 넣을 수도 있습니다.

또한 MD5 대신 SHA1을 사용할 수 있습니다 (또는 거의 모든 알고리즘).

소개

귀하의 제목 "로그인 유지" - 최선의 접근 방식 최선의 접근 방식을 찾고 있다면 다음을 고려해야 하기 때문에 어디서부터 시작해야 할지 알기 어렵습니다.

  • 신분증
  • 보안

쿠키

쿠키는 취약합니다. 일반적인 브라우저 쿠키 도난 취약성과 크로스 사이트 스크립팅 공격 사이에서 우리는 쿠키가 안전하지 않다는 점을 인정해야 합니다.보안을 강화하려면 다음 사항에 유의해야 합니다. php setcookies 다음과 같은 추가 기능이 있습니다.

부울 쿠키 세트 ( 문자열 $name [, 문자열 $value [, int $expire = 0 [, 문자열 $path [, 문자열 $domain [, bool $보안 = 거짓 [, 부울 $httponly = 거짓 ]]]]]] )

  • 보안(HTTPS 연결 사용)
  • httponly(XSS 공격을 통한 신원 도용 감소)

정의

  • 토큰(n 길이의 예측할 수 없는 임의 문자열입니다./dev/urandom)
  • 참조(예: n 길이의 예측할 수 없는 임의 문자열)/dev/urandom)
  • 서명(HMAC 방법을 사용하여 키 해시 값 생성)

간단한 접근 방식

간단한 해결책은 다음과 같습니다.

  • 사용자가 Remember Me로 로그온했습니다.
  • 토큰 및 서명으로 발행된 로그인 쿠키
  • 돌아올 때 서명을 확인합니다.
  • 시그니처가 괜찮다면..그런 다음 사용자 이름과 토큰이 데이터베이스에서 조회됩니다.
  • 유효하지 않은 경우 ..로그인 페이지로 돌아가기
  • 유효한 경우 자동으로 로그인

위의 사례 연구는 이 페이지에 제공된 모든 예를 요약하지만 단점은 다음과 같습니다.

  • 쿠키가 도난당했는지 알 수 있는 방법이 없습니다
  • 공격자는 비밀번호 변경이나 개인정보, 베이킹 정보 등의 데이터와 같은 민감한 작업에 접근할 수 있습니다.
  • 손상된 쿠키는 쿠키 수명 동안 계속 유효합니다.

더 나은 솔루션

더 나은 해결책은

  • 사용자가 로그인되어 있고 나를 기억이 선택되어 있습니다.
  • 토큰 및 서명을 생성하고 쿠키에 저장
  • 토큰은 무작위이며 단일 인증에만 유효합니다.
  • 토큰은 사이트를 방문할 때마다 교체됩니다.
  • 로그인하지 않은 사용자가 사이트를 방문하면 서명, 토큰 및 사용자 이름이 확인됩니다.
  • 로그인은 접근이 제한되어야 하며 비밀번호, 개인 정보 등의 수정을 허용하지 않아야 한다는 점을 기억하십시오.

예제 코드

// 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 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 및 Chrome에서 테스트

enter image description here

이점

  • 더 나은 보안
  • 공격자에 대한 접근 제한
  • 쿠키가 도난당한 경우 단일 액세스에만 유효합니다.
  • 다음번에 원래 사용자가 사이트에 액세스하면 도난 사실을 자동으로 감지하고 사용자에게 알릴 수 있습니다.

불리

  • 다중 브라우저(모바일 및 웹)를 통한 영구 연결을 지원하지 않습니다.
  • 사용자는 다음 로그인 후에만 알림을 받기 때문에 쿠키는 여전히 도난당할 수 있습니다.

빠른 수정

  • 지속적인 연결이 필요한 시스템별 승인 시스템 도입
  • 인증을 위해 여러 쿠키 사용

다중 쿠키 접근 방식

공격자가 쿠키를 훔치려 할 때 특정 웹사이트나 도메인에만 집중하십시오. example.com

하지만 실제로는 서로 다른 2개의 도메인에서 사용자를 인증할 수 있습니다(example.com & fakeaddsite.com) 그리고 "광고 쿠키"처럼 보이게 만듭니다.

  • 사용자가 로그온한 위치 example.com 나를 기억해줘
  • 사용자 이름, 토큰, 참조를 쿠키에 저장
  • 사용자 이름, 토큰, 참조를 데이터베이스에 저장합니다.멤캐시
  • get 및 iframe을 통해 참조 ID를 다음으로 보냅니다. fakeaddsite.com
  • fakeaddsite.com은 참조를 사용하여 데이터베이스에서 사용자 및 토큰을 가져옵니다.
  • fakeaddsite.com은 서명을 저장합니다
  • 사용자가 fakeaddsite.com에서 iframe을 사용하여 서명 정보 가져오기를 반환하는 경우
  • 데이터를 결합하고 유효성 검사를 수행합니다.
  • .....남은 건 너 알지?

어떤 사람들은 2개의 다른 쿠키를 어떻게 사용할 수 있는지 궁금해할 수도 있습니다.뭐 가능하지, 상상해봐 example.com = localhost 그리고 fakeaddsite.com = 192.168.1.120.쿠키를 검사하면 다음과 같습니다.

enter image description here

위 이미지에서

  • 현재 방문한 사이트는 localhost입니다.
  • 또한 192.168.1.120에서 설정된 쿠키도 포함되어 있습니다.

192.168.1.120

  • 정의된 것만 수락 HTTP_REFERER
  • 지정된 연결만 허용합니다. REMOTE_ADDR
  • JavaScript 없음, 콘텐츠는 없지만 정보에 서명하고 쿠키에서 추가하거나 검색하는 것 외에는 아무것도 구성하지 않습니다.

이점

  • 당신이 공격자를 속인 시간의 99%
  • 공격자의 첫 번째 시도에서 쉽게 계정을 잠글 수 있습니다
  • 다른 방법과 마찬가지로 다음 로그인 이전에도 공격을 방지할 수 있습니다.

불리

  • 단일 로그인에 대해서만 서버에 다중 요청

개선

  • iframe 사용 완료 ajax

"remember-me" 문제에 대한 완벽한 솔루션을 검색하던 중 발견한 매우 흥미로운 두 가지 기사가 있습니다.

나는이 질문의 한 각도를 물었다 여기, 그리고 답변은 필요한 모든 토큰 기반 타이밍 아웃 쿠키 링크로 연결됩니다.

기본적으로 useid를 쿠키에 저장하지 않습니다. 사용자가 이전 로그인 세션을 수령하는 데 사용하는 일회성 토큰 (거대한 문자열)을 저장합니다. 그런 다음 실제로 안전하게 만들기 위해 비밀번호 자체 변경과 같은 대형 작업에 대한 비밀번호를 요청합니다.

Stefan이 언급 한 접근법을 추천합니다 (즉, 지침을 따르십시오. 개선 된 영구 로그인 쿠키 모범 사례) 또한 쿠키가 httponly 쿠키 따라서 잠재적으로 악의적 인 JavaScript에 접근 할 수 없습니다.

해시를 생성하고, 아마도 당신이 아는 비밀만으로 DB에 저장하여 사용자와 연관 될 수 있도록 DB에 저장하십시오. 잘 작동해야합니다.

오래된 실이지만 여전히 유효한 관심사입니다. 보안에 대한 좋은 반응과 '모호함을 통한 보안'사용을 피했지만 실제 기술적 방법은 내 눈에는 충분하지 않았습니다. 내 방법을 기여하기 전에 내가 말해야 할 것 :

  • 절대 명확한 텍스트로 비밀번호를 저장하십시오 ...
  • 절대 사용자의 해시 암호를 데이터베이스의 둘 이상의 위치에 저장하십시오. 서버 백엔드는 항상 사용자 테이블에서 해시 비밀번호를 가져올 수 있습니다. 추가 DB 트랜잭션 대신 중복 데이터를 저장하는 것이 더 효율적이지 않으며 역수는 사실입니다.
  • 세션 ID는 고유해야하므로 두 사용자는 할 수 없습니다. 항상 신분증을 공유하므로 ID의 목적 (운전 면허 ID 번호가 다른 사람과 일치 할 수 있습니까?) 2 개의 고유 한 문자열을 기반으로 2 피스 고유 한 조합을 생성합니다. 세션 테이블은 ID를 PK로 사용해야합니다. 자동 서명에 대해 여러 장치를 신뢰할 수 있도록 모든 검증 된 장치 목록이 포함 된 신뢰할 수있는 장치에 다른 테이블을 사용하고 (아래 예제 참조) 사용자 이름을 사용하여 매핑됩니다.
  • 알려진 데이터를 쿠키로 해시하는 목적은 없으며 쿠키를 복사 할 수 있습니다. 우리가 찾고있는 것은 공격자가 사용자의 컴퓨터를 손상시키지 않으면 얻을 수없는 진정한 정보를 제공하는 준수 사용자 장치입니다 (다시, 내 예 참조). 그러나 이는 기계의 정적 정보 (예 : Mac 주소, 장치 호스트 이름, 브라우저로 제한되는 경우 UserAgent 등)를 금지하는 합법적 인 사용자가 나머지 일관성 (또는 처음에는 스푸핑)을 할 수 없다는 것을 의미합니다. 이 기능을 사용하십시오. 그러나 이것이 관심이라면, 당신이 사용자에게 자동 서명을 제공하고 있다는 사실을 고려하십시오. 자신을 독특하게 식별하십시오, 따라서 Mac을 스프핑하고, 사용자의 스푸핑하고, 호스트 이름을 스푸핑/스푸핑하고, 프록시 뒤에 숨기는 등으로 알려지지 않으면 식별 할 수 없으므로 자동 서비스를 위해 인증되지 않아야합니다. 이를 원한다면 사용중인 장치에 대한 ID를 설정하는 클라이언트 측 소프트웨어와 함께 번들 된 스마트 카드 액세스를 살펴 봐야합니다.

모든 것이 말하면, 시스템에 자동 서명을하는 두 가지 좋은 방법이 있습니다.

첫째, 다른 사람에게 모든 것을 넣는 저렴하고 쉬운 방법. Google+ 계정 (예 : Google+ 계정으로 로그인하는 경우 사이트가 이미 로그인 한 경우 사용자가 이미 Google에 서명 된 경우 로그인하는 간소화 된 Google+ 버튼이있을 수 있습니다. Google에 서명). 신뢰할 수 있고 지원되는 인증 자로 이미 로그인 한 경우 사용자가 자동으로 로그인하고 상자를 확인한 경우로드하기 전에 클라이언트 측 스크립트가 해당 '로그인 버튼'뒤에 코드를 수행하도록하려면. , 서버 저장소에 사용자 이름, 세션 ID 및 사용자에게 사용되는 인증기가있는 자동 서명 테이블에 고유 한 ID를 갖도록하십시오. 이러한 로그인 방법은 Ajax를 사용하므로 어쨌든 응답을 기다리고 있으며 해당 응답은 검증 된 응답 또는 거부입니다. 검증 된 응답을 얻으려면 정상적으로 응답을 사용하고 로그인 한 사용자를 평소처럼 계속로드하십시오. 그렇지 않으면 로그인이 실패했지만 사용자에게 알리지 않고 로그인되지 않은 상태에서 계속 표시됩니다. 이는 쿠키를 훔치거나 (특권을 확대하기 위해 위조 된 시도) 사용자가 사용자가 사이트에 자동 서명한다는 것을 알지 못하게하는 공격자를 방지하기위한 것입니다.

이것은 저렴하며, Google 및 Facebook과 같은 장소와 이미 자체적으로 서명 한 자체를 검증하려고 시도하기 때문에 일부는 더러워 질 수 있습니다. 그러나 사이트를 자동 서명하라는 요청을받지 않은 사용자에게는 사용해서는 안되며이 특정 방법은 Google+ 또는 FB와 같은 외부 인증을위한 것입니다.

외부 인증자는 사용자가 검증되었는지 여부를 무대 뒤에서 서버에 알리는 데 사용되었으므로 공격자는 고유 한 ID 이외의 다른 것을 얻을 수 없습니다. 자세히 설명하겠습니다 :

  • 사용자 'Joe'는 처음으로 사이트를 방문하고 쿠키 '세션'에 배치 된 세션 ID.
  • 사용자 'Joe'로그인, 권한을 에스컬레이션하고 새로운 세션 ID를 가져오고 쿠키 '세션'을 갱신합니다.
  • 사용자 'Joe'는 Google+를 사용하여 자동 서명하기로 선택하여 Cookie 'KeepMesignedIn'에 배치 된 고유 한 ID를 얻습니다.
  • 사용자 'Joe'에는 Google이 Google을 가입하여 로그인하여 사이트가 백엔드에서 Google을 사용하여 사용자를 자동으로 표시 할 수 있습니다.
  • 공격자는 체계적으로 'KeepMesignedIn'에 대한 고유 한 ID를 시도하며 (이것은 모든 사용자에게 전달 된 공개 지식입니다) 다른 곳에 서명하지 않습니다. 'Joe'에게 주어진 독특한 ID를 시도합니다.
  • 서버는 'Joe'에 대한 고유 ID를 수신하고 Google+ 계정의 DB에서 일치를 가져옵니다.
  • 서버는 공격자에게 로그인으로 로그인으로 로그인으로 로그인합니다.
  • Google 서버는 요청을 수신하고 API를 사용하여 공격자가 현재 로그인되지 않은 것을 확인합니다.
  • Google 은이 연결을 통해 현재 사용자에게 서명 된 것이 없다는 응답을 보냅니다.
  • 공격자의 페이지는 응답을 수신하고 스크립트는 URL에 포스트 값을 인코딩 한 후 로그인 페이지로 자동 리디렉션을받습니다.
  • 로그인 페이지는 우편 값을 가져오고 'keepMesignedIned'의 쿠키를 빈 값으로 보내고 1-1970의 날짜까지 자동 시도를 방해하여 공격자의 브라우저가 쿠키를 간단히 삭제합니다.
  • 공격자에게는 일반적인 최초 로그인 페이지가 제공됩니다.

어쨌든 공격자가 존재하지 않는 ID를 사용하더라도 검증 된 응답을받는 경우를 제외하고는 모든 시도에서 시도가 실패해야합니다.

이 방법은 외부 인증자를 사용하여 사이트에 로그인하는 사람들을 위해 내부 인증 자와 함께 사용될 수 있으며 사용해야합니다.

=========

이제 사용자를 자동 서명 할 수있는 자체 인증기 시스템의 경우 이것이 제가 수행하는 방법입니다.

DB에는 몇 개의 테이블이 있습니다.

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

사용자 이름의 길이는 255 자일 수 있습니다. 내 서버 프로그램 제한 내 시스템의 사용자 이름이 32 자로 제한되어 있지만 외부 인증기는 @domain.tld가있는 사용자 이름이 그보다 클 수 있으므로 최대 호환성을 위해 이메일 주소의 최대 길이를 지원합니다.

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

로그인 된 경우 사용자 이름이 세션 데이터에 있고 프로그램은 NULL 데이터를 허용하지 않기 때문에이 테이블에는 사용자 필드가 없습니다. Session_ID 및 Session_Token은 임의의 MD5 해시, SHA1/128/256 해시, 임의의 문자열이 추가 된 DateTime 스탬프를 사용하여 생성 할 수 있습니다. 무차별 적 공격을 완화하여 땅에서 내리기도하면서 세션 클래스에서 생성 된 모든 해시를 추가하려고 시도하기 전에 세션 테이블에서 일치하는지 확인해야합니다.

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 주소는 본질적으로 독특해야하므로 각 항목에는 고유 한 가치가 있음이 합리적입니다. 반면에 호스트 이름은 별도의 네트워크에서 합법적으로 복제 될 수 있습니다. 얼마나 많은 사람들이 컴퓨터 이름 중 하나로 "Home-PC"를 사용합니까? 사용자 이름은 서버 백엔드의 세션 데이터에서 가져 오므로 조작 할 수 없습니다. 토큰의 경우, 페이지에 대한 세션 토큰을 생성하는 동일한 방법을 사용하여 사용자 자동 서명을위한 쿠키의 토큰을 생성해야합니다. 마지막으로, DateTime 코드는 사용자가 자격 증명을 재평가 해야하는 경우에 추가됩니다. 며칠 내에 사용자 로그인 에서이 Datetime을 업데이트하거나 한 달 정도만 보관하는 마지막 로그인에 관계없이 만료되도록 강제로 디자인이 지시됩니다.

이로 인해 누군가가 자동 서명을 알고있는 사용자의 Mac과 호스트 이름을 체계적으로 스푸핑하지 못하게합니다. 절대 사용자에게 암호, 명확한 텍스트 또는 기타로 쿠키를 보관하도록하십시오. 세션 토큰과 마찬가지로 각 페이지 탐색에서 토큰을 재생하도록하십시오. 이로 인해 공격자가 유효한 토큰 쿠키를 얻고 로그인하는 데 사용할 가능성이 크게 줄어 듭니다. 어떤 사람들은 공격자가 피해자의 쿠키를 훔치고 로그인을 위해 세션 재생 공격을 할 수 있다고 말하려고합니다. 공격자가 쿠키를 훔칠 수 있다면 (가능) 전체 장치를 타협했을 것입니다. 즉, 장치를 사용하여 로그인하여 쿠키를 완전히 훔치기위한 목적을 물리 칠 수 있습니다. 귀하의 사이트가 HTTPS (암호, CC 번호 또는 기타 로그인 시스템을 처리 할 때)를 통해 실행되는 한 브라우저 내에서 할 수있는 사용자에게 모든 보호를 제공했습니다.

명심해야 할 한 가지 : 자동 서명을 사용하는 경우 세션 데이터가 만료되어서는 안됩니다. 세션을 거짓으로 계속할 수있는 능력을 만료 할 수 있지만, 세션간에 계속 될 것으로 예상되는 영구 데이터 인 경우 시스템에 검증되면 세션 데이터를 재개해야합니다. 지속성 및 비 연개 세션 데이터를 모두 원한다면 사용자 이름을 PK로 사용하여 지속적인 세션 데이터에 다른 테이블을 사용하고 일반 세션 데이터와 같이 서버가 검색하도록하고 다른 변수를 사용하십시오.

이런 식으로 로그인이 달성되면 서버는 여전히 세션을 검증해야합니다. 이곳에서 도난 당하거나 손상된 시스템에 대한 기대치를 코딩 할 수 있습니다. 세션 데이터에 대한 로그인의 패턴 및 기타 예상 결과는 종종 액세스를 얻기 위해 시스템이 납치되거나 쿠키가 위조되었다는 결론을 이끌어 낼 수 있습니다. 이곳에서 ISS 기술이 자동 서명 시스템에서 사용자의 계정 잠금 또는 자동 제거를 트리거하는 규칙을 제시 할 수 있으며, 공격자가 공격자가 성공한 방법과 차단 방법을 결정할 수있을 정도로 공격자를 오랫동안 유지할 수 있습니다.

마감 참고로, 임계 값을 지나서 복구 시도, 비밀번호 변경 또는 로그인 실패로 인해 사용자가 올바르게 검증하고이를 확인할 때까지 자동 서명이 비활성화되는지 확인하십시오.

내 대답으로 코드가 나올 것으로 기대하고 있다면 사과드립니다. 여기서는 일어나지 않을 것입니다. 나는 PHP, JQuery 및 Ajax를 사용하여 사이트를 실행한다고 말하며 Windows를 서버로 사용하지 않습니다.

내 해결책은 다음과 같습니다. 100% 방탄은 아니지만 대부분의 경우에 당신을 구할 것이라고 생각합니다.

사용자 로그인 이이 정보로 문자열을 성공적으로 생성 할 때 다음과 같습니다.

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

암호화 $data, 유형을 설정하십시오 httponly 쿠키를 설정하십시오.

사용자가 사이트로 돌아 오면이 단계를 수행하십시오.

  1. 쿠키 데이터를 얻으십시오. 쿠키 내부에서 위험한 캐릭터를 제거하십시오. 그것을 폭발시킵니다 : 캐릭터.
  2. 유효성을 확인하십시오. 쿠키가 X 일보다 오래된 경우 사용자를 로그인 페이지로 리디렉션하십시오.
  3. 쿠키가 오래되지 않은 경우; 데이터베이스에서 최신 비밀번호 변경 시간을 얻습니다. 사용자의 마지막 로그인 후 암호가 변경된 경우 사용자를 로그인 페이지로 리디렉션합니다.
  4. 최근에 패스가 변경되지 않은 경우; 사용자의 현재 브라우저 에이전트를 얻으십시오. (currentuseragenthash == cookieuseragenthash)를 확인하십시오. 에이전트가 동일하면 다음 단계로 이동하면 로그인 페이지로 리디렉션됩니다.
  5. 모든 단계가 통과 된 경우 사용자 이름을 성공적으로 승인합니다.

사용자 가입이있는 경우이 쿠키를 제거하십시오. 사용자가 다시 로그 인 경우 새 쿠키를 만듭니다.

암호화 된 버전이 해킹을 해야하는 암호화 버전 일 때 암호화 된 물건을 쿠키에 저장하는 개념을 이해하지 못합니다. 내가 뭔가를 놓치면 댓글을 달아주세요.

나는이 접근법을 '기억'에 생각하고 있다고 생각하고 있습니다. 문제를 볼 수 있다면 댓글을 달아주십시오.

  1. "기억"데이터를 저장할 테이블을 작성하여 여러 장치에서 로그인 할 수 있도록 사용자 테이블에 별도로 표시됩니다.

  2. 성공적인 로그인시 (기억이 나와 함께) :

    a)이 컴퓨터의 userID로 사용할 고유 임의 문자열 생성 : biguserid

    b) 고유 한 무작위 문자열을 생성합니다 : Bigkey

    c) 쿠키를 저장하십시오 : Biguserid : Bigkey

    d) "기억"테이블에서 userID, IP 주소, biguserid, bigkey와 함께 레코드를 추가하십시오.

  3. 로그인이 필요한 것에 액세스하려고하는 경우 :

    a) 쿠키를 확인하고 일치하는 IP 주소로 biguserid & bigkey를 검색하십시오.

    b) 당신이 그것을 찾으면, 사람을 로그인하지만 사용자 테이블 "소프트 로그인"에 플래그를 설정하여 위험한 작업의 경우 전체 로그인을 자랑 할 수 있습니다.

  4. 로그 아웃시 해당 사용자의 모든 "기억"레코드를 만료 된 것으로 표시하십시오.

내가 볼 수있는 유일한 취약점은;

  • 누군가의 노트북을 잡고 쿠키로 IP 주소를 스푸핑 할 수 있습니다.
  • 당신은 매번 다른 IP 주소를 스푸핑하고 모든 것을 추측 할 수 있습니다. 그러나 두 개의 큰 문자열이 일치 할 것입니다. 그것은 위의 비슷한 계산을 수행 할 것입니다 ... 나는 전혀 모른다 ... 큰 확률은?

나는 모든 답을 읽었지만 여전히 내가해야 할 일을 추출하기가 어렵다는 것을 알았습니다. 사진이 1K 단어의 가치가 있다면 이것이 Barry Jaspan의 보안 유지 보관을 구현하는 데 도움이되기를 바랍니다. 개선 된 영구 로그인 쿠키 모범 사례

enter image description here

질문, 피드백 또는 제안이 있으면 보안 영구 로그인을 구현하려는 초보자를 반영하기 위해 다이어그램을 업데이트하려고합니다.

"로그인 유지"기능을 구현하면 사용자에게 의미하는 바를 정확하게 정의해야합니다. 가장 간단한 경우,이를 사용하여 세션의 시간 초과가 훨씬 더 길다는 것을 의미합니다. 2 시간 대신 2 일 (2 일). 이를 위해서는 데이터베이스에 자체 세션 스토리지가 필요하므로 세션 데이터에 대한 사용자 정의 만료 시간을 설정할 수 있습니다. 그런 다음 브라우저를 닫을 때 만료되지 않고 며칠 (또는 더 오래) 쿠키를 설정해야합니다.

"왜 2 일? 왜 2 주가 아닌가?" PHP에서 세션을 사용하면 만료가 자동으로 뒷받침되기 때문입니다. PHP에서 세션이 만료되는 것은 실제로 유휴 시간 초과이기 때문입니다.

이제, 나는 아마도 세션 자체에 저장하는 더 어려운 시간 초과 값을 구현하고 2 주 정도에 코드를 추가하여이를보고 강제로 세션을 무효화하기 위해 코드를 추가 할 것입니다. 또는 적어도 로그 아웃하십시오. 이는 사용자가 정기적으로 로그인하도록 요청받을 것임을 의미합니다. 야후! 이렇게합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top