“ Keep Me ล็อกอิน” - วิธีที่ดีที่สุด


257

เว็บแอปพลิเคชันของฉันใช้เซสชันเพื่อเก็บข้อมูลเกี่ยวกับผู้ใช้เมื่อพวกเขาเข้าสู่ระบบและเพื่อรักษาข้อมูลนั้นเมื่อพวกเขาเดินทางจากหน้าหนึ่งไปอีกหน้าหนึ่งภายในแอป ในการประยุกต์ใช้ที่เฉพาะเจาะจงนี้ฉันจัดเก็บuser_id, first_nameและlast_nameของบุคคล

ฉันต้องการเสนอตัวเลือก "ให้ฉันเข้าสู่ระบบ" ในการเข้าสู่ระบบที่จะวางคุกกี้บนเครื่องของผู้ใช้เป็นเวลาสองสัปดาห์ที่จะรีสตาร์ทเซสชันของพวกเขาด้วยรายละเอียดเดียวกันเมื่อพวกเขากลับไปที่แอป

อะไรคือวิธีที่ดีที่สุดในการทำสิ่งนี้? ฉันไม่ต้องการเก็บไว้user_idในคุกกี้เนื่องจากดูเหมือนว่าจะทำให้ผู้ใช้รายหนึ่งลองและปลอมแปลงข้อมูลประจำตัวของผู้ใช้รายอื่นได้ง่าย

คำตอบ:


735

ตกลงให้ฉันทำแบบนี้โผงผาง: ถ้าคุณใส่ข้อมูลผู้ใช้หรือสิ่งที่ได้รับจากข้อมูลผู้ใช้ลงในคุกกี้เพื่อจุดประสงค์นี้คุณกำลังทำอะไรผิด

ที่นั่น ฉันเป็นคนพูดมันเอง. ตอนนี้เราสามารถไปยังคำตอบจริงได้

คุณถามอะไรผิดปกติกับการแฮชข้อมูล? มันลงมาที่ผิวสัมผัสและความปลอดภัยผ่านความสับสน

ลองนึกภาพหนึ่งวินาทีว่าคุณเป็นผู้โจมตี คุณเห็นคุกกี้การเข้ารหัสที่ตั้งค่าไว้สำหรับจดจำฉันในเซสชันของคุณ กว้าง 32 ตัวอักษร Gee นั่นอาจเป็น MD5 ...

ลองจินตนาการถึงวินาทีที่พวกเขารู้อัลกอริทึมที่คุณใช้ ตัวอย่างเช่น:

md5(salt+username+ip+salt)

ตอนนี้ผู้โจมตีทุกคนต้องทำคือกำลังดุร้าย "เกลือ" (ซึ่งไม่ใช่เกลือจริง ๆ แต่มากกว่านั้นในภายหลัง) และตอนนี้เขาสามารถสร้างโทเค็นปลอมทั้งหมดที่เขาต้องการด้วยชื่อผู้ใช้สำหรับที่อยู่ IP ของเขา! แต่การบังคับให้ใส่เกลือเป็นเรื่องยากใช่มั้ย อย่างแน่นอน แต่ GPU ในยุคปัจจุบันนั้นยอดเยี่ยมมาก และถ้าคุณใช้การสุ่มในที่มากพอ (ทำให้มันใหญ่พอ) มันจะล้มลงอย่างรวดเร็วและด้วยกุญแจของปราสาทของคุณ

ในระยะสั้นสิ่งเดียวที่ปกป้องคุณคือเกลือซึ่งไม่ได้ปกป้องคุณเท่าที่คุณคิด

แต่เดี๋ยวก่อน!

ทั้งหมดนี้เป็นการแจ้งล่วงหน้าว่าผู้โจมตีรู้อัลกอริทึม! ถ้ามันเป็นความลับและทำให้สับสนคุณก็ปลอดภัยใช่ไหม? ผิด แนวความคิดนั้นมีชื่อ: ความปลอดภัยผ่านความสับสนซึ่งไม่ควรเชื่อถือ

วิธีที่ดีกว่า

วิธีที่ดีกว่าคือการไม่ปล่อยให้ข้อมูลของผู้ใช้ออกจากเซิร์ฟเวอร์ยกเว้นรหัส

เมื่อผู้ใช้ล็อกอินสร้างโทเค็นแบบสุ่มขนาดใหญ่ (128 ถึง 256 บิต) เพิ่มลงในตารางฐานข้อมูลซึ่งจับคู่โทเค็นกับหมายเลขผู้ใช้แล้วส่งไปยังไคลเอนต์ในคุกกี้

เกิดอะไรขึ้นถ้าผู้โจมตีเดาโทเค็นแบบสุ่มของผู้ใช้รายอื่น

ลองทำคณิตศาสตร์กันที่นี่ เรากำลังสร้างโทเค็นแบบสุ่ม 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 พันล้านวินาที ...

มาลองดูตัวเลขที่เป็นมิตรกันมากขึ้น

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 ด้านล่าง PHP 5.6 ฟังก์ชั่น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;
}

7
แต่วิธีการนี้ไม่ได้หมายความว่าทุกคนสามารถใช้ชื่อผู้ใช้และคุกกี้นี้และเข้าสู่ระบบในฐานะผู้ใช้รายนี้จากอุปกรณ์อื่น ๆ ?
เรียบง่าย

8
lol :-) โปรดทราบว่า 47917 ปีเป็นเวลาสูงสุดที่จะเดาโทเค็นแบบสุ่มสามารถเดาได้ใน 1 ชั่วโมงเช่นกัน
storm_buster

33
มันแปลกเพราะรหัสของคุณขัดแย้งกับคำตอบของคุณ คุณพูดว่า "ถ้าคุณใส่ข้อมูลผู้ใช้ในคุกกี้ [... ] คุณกำลังทำอะไรผิด" แต่นั่นเป็นสิ่งที่รหัสของคุณกำลังทำอยู่! การลบชื่อผู้ใช้ออกจากคุกกี้คำนวณแฮชเหนือโทเค็นเท่านั้น (และอาจเพิ่มที่อยู่ IP เพื่อป้องกันการโจรกรรมคุกกี้) จากนั้นทำการ fetchUsernameByToken แทนที่จะเป็น fetchTokenByUserName ใน RememberMe ()
Leven

9
ตั้งแต่ PHP 5.6 hash_equalsสามารถใช้เพื่อป้องกันการโจมตีตามจังหวะเวลาเมื่อทำการเปรียบเทียบสตริง
F21

5
@Levit มันป้องกันบางคนจากการใช้โทเค็นที่ถูกต้องและเปลี่ยนหมายเลขผู้ใช้ที่แนบมา
ircmaxell

93

ประกาศด้านความปลอดภัย : การลบคุกกี้ออกจากแฮช MD5 ของข้อมูลที่กำหนดขึ้นเป็นความคิดที่ไม่ดี เป็นการดีกว่าที่จะใช้โทเค็นแบบสุ่มที่มาจาก CSPRNG ดูคำตอบของ ircmaxellสำหรับคำถามนี้เพื่อแนวทางที่ปลอดภัยยิ่งขึ้น

ฉันมักจะทำสิ่งนี้:

  1. ผู้ใช้ลงชื่อเข้าใช้ด้วย 'ให้ฉันอยู่ในระบบ'
  2. สร้างเซสชัน
  3. สร้างคุกกี้ที่ชื่อว่า SOMETHING ประกอบด้วย: md5 (เกลือ + ชื่อผู้ใช้ + ip + เกลือ) และคุกกี้ที่เรียกว่า somethingElse มี id
  4. จัดเก็บคุกกี้ในฐานข้อมูล
  5. ผู้ใช้ทำสิ่งต่าง ๆ และออกจาก ----
  6. ผู้ใช้ส่งคืนตรวจสอบคุกกี้ someElse หากมีอยู่ได้รับแฮชเก่าจากฐานข้อมูลสำหรับผู้ใช้นั้นตรวจสอบเนื้อหาของคุกกี้บางสิ่งที่ตรงกับแฮชจากฐานข้อมูลซึ่งควรตรงกับแฮชที่คำนวณใหม่ (สำหรับ ip) ดังนั้น: cookieHash == databaseHash == md5 (เกลือ + ชื่อผู้ใช้ + ip + เกลือ) ถ้าพวกเขาทำได้ข้าม 2 ถ้าพวกเขาไม่ข้าม 1

นอกหลักสูตรคุณสามารถใช้ชื่อคุกกี้ที่แตกต่างกันเป็นต้นนอกจากนี้คุณยังสามารถเปลี่ยนเนื้อหาของคุกกี้ได้เพียงแค่ทำให้แน่ใจว่ามันจะไม่ถูกสร้างขึ้นอย่างง่ายดาย ตัวอย่างเช่นคุณสามารถสร้าง user_salt ได้เมื่อผู้ใช้ถูกสร้างและใส่ไว้ในคุกกี้

นอกจากนี้คุณสามารถใช้ sha1 แทน md5 (หรืออัลกอริทึมใดก็ได้)


30
เหตุใดจึงรวม IP ไว้ในแฮช นอกจากนี้ตรวจสอบให้แน่ใจว่าได้รวมข้อมูลการประทับเวลาในคุกกี้และใช้ข้อมูลนี้เพื่อกำหนดอายุสูงสุดสำหรับคุกกี้เพื่อที่คุณจะไม่ได้สร้างโทเค็นข้อมูลเฉพาะตัวที่ดีสำหรับนิรันดร์
Scott Mitchell

4
@Abhishek Dilliwal: นี่เป็นหัวข้อเก่า ๆ แต่ฉันเจอมันเพื่อหาคำตอบเดียวกับ Mathew ฉันไม่คิดว่าการใช้ session_ID จะใช้ได้กับคำตอบของ Pim เพราะคุณไม่สามารถตรวจสอบ db hash, cookie hash และ session_ID ปัจจุบันได้เนื่องจาก session_ID เปลี่ยนทุก session_start (); แค่คิดว่าฉันจะชี้เรื่องนี้
Partack

3
ฉันขอโทษที่จะน่าเบื่อ แต่วัตถุประสงค์ของคุกกี้ที่สองคืออะไร ELSE? id ในกรณีนี้คืออะไร เป็นเพียงการเรียงลำดับอย่างง่าย ๆ ของค่า "จริง / เท็จ" เพื่อระบุว่าผู้ใช้ต้องการใช้คุณลักษณะให้ฉันเข้าสู่ระบบเลยหรือไม่? ถ้าเป็นเช่นนั้นทำไมไม่ลองตรวจสอบดูว่ามีคุกกี้บางอย่างอยู่ในตำแหน่งที่หนึ่งหรือไม่? หากผู้ใช้ไม่ต้องการให้การเข้าสู่ระบบคงอยู่คุกกี้ SOMETHING จะไม่อยู่ในตำแหน่งแรกใช่ไหม ในที่สุดคุณสร้างแฮชอีกครั้งแบบไดนามิกและตรวจสอบกับคุกกี้และฐานข้อมูลเป็นมาตรการรักษาความปลอดภัยเพิ่มเติมหรือไม่
itsmequinn

4
โทเค็นควรเป็น RANDOM ไม่ได้เชื่อมต่อกับผู้ใช้ / IP / ผู้ใช้ของเขา / สิ่งใดก็ตาม มันเป็นข้อบกพร่องด้านความปลอดภัยที่สำคัญ
pamil

4
ทำไมคุณถึงใช้เกลือสองเม็ด md5 (เกลือ + ชื่อผู้ใช้ + ip + เกลือ)
Aaron Kreider

77

บทนำ

ชื่อของคุณ“ ให้ฉันเข้าสู่ระบบ” - วิธีที่ดีที่สุดทำให้ยากสำหรับฉันที่จะรู้ว่าจะเริ่มต้นเพราะถ้าคุณกำลังมองหาวิธีที่ดีที่สุดแล้วคุณจะต้องพิจารณาดังต่อไปนี้:

  • บัตรประจำตัว
  • ความปลอดภัย

คุ้กกี้

คุกกี้มีความเสี่ยงระหว่างช่องโหว่การโจรกรรมคุกกี้ของเบราว์เซอร์ทั่วไปและการโจมตีสคริปต์ข้ามไซต์เราต้องยอมรับว่าคุกกี้ไม่ปลอดภัย เพื่อช่วยปรับปรุงความปลอดภัยคุณต้องทราบว่าphp setcookiesมีฟังก์ชั่นเพิ่มเติมเช่น

บูลsetcookie (สตริง $ name [, สตริง $ value [, int $ expire = 0 [, สตริง $ path [, สตริง $ domain [, bool $ secure = false [, bool $ httponly = false]]]]]]]

  • ปลอดภัย (ใช้การเชื่อมต่อ HTTPS)
  • httponly (ลดการขโมยข้อมูลประจำตัวผ่านการโจมตี XSS)

คำนิยาม

  • โทเค็น (ความยาวสตริงที่ไม่สามารถคาดเดาได้ของความยาว n เช่น. / dev / urandom)
  • การอ้างอิง (สตริงสุ่มที่คาดเดาไม่ได้ซึ่งมีความยาว n เช่น. / dev / urandom)
  • ลายเซ็น (สร้างค่าแฮชคีย์โดยใช้วิธี HMAC)

วิธีการง่าย ๆ

ทางออกที่ง่ายจะเป็น:

  • ผู้ใช้เข้าสู่ระบบด้วยจดจำฉัน
  • คุกกี้เข้าสู่ระบบออกด้วยโทเค็น & ลายเซ็น
  • เมื่อกลับมาลายเซ็นจะถูกตรวจสอบ
  • หาก Signature ไม่เป็นไร .. จะมีการค้นหาชื่อผู้ใช้และโทเค็นในฐานข้อมูล
  • หากไม่ถูกต้อง .. กลับสู่หน้าเข้าสู่ระบบ
  • หากถูกต้องเข้าสู่ระบบโดยอัตโนมัติ

กรณีศึกษาด้านบนสรุปตัวอย่างทั้งหมดที่ให้ไว้ในหน้านี้ แต่ข้อเสียคือ

  • ไม่มีทางรู้ว่าคุกกี้ถูกขโมยหรือไม่
  • ผู้โจมตีอาจเข้าถึงการทำงานที่ละเอียดอ่อนเช่นการเปลี่ยนรหัสผ่านหรือข้อมูลเช่นข้อมูลส่วนบุคคลและการอบเป็นต้น
  • คุกกี้ที่ถูกบุกรุกจะยังคงใช้ได้สำหรับช่วงอายุคุกกี้

ทางออกที่ดีกว่า

ทางออกที่ดีกว่าก็คือ

  • ผู้ใช้เข้าสู่ระบบและเลือกฉันจำไว้ว่า
  • สร้างโทเค็น & ลายเซ็นและเก็บในคุกกี้
  • โทเค็นเป็นแบบสุ่มและใช้ได้สำหรับการตรวจสอบครั้งเดียวเท่านั้น
  • โทเค็นจะถูกแทนที่ในการเยี่ยมชมเว็บไซต์แต่ละครั้ง
  • เมื่อผู้ใช้ที่ไม่ได้เข้าสู่ระบบเยี่ยมชมเว็บไซต์ลายเซ็นโทเค็นและชื่อผู้ใช้จะถูกตรวจสอบ
  • จดจำฉันเข้าสู่ระบบควรมีการเข้าถึงที่ จำกัด และไม่อนุญาตให้แก้ไขรหัสผ่านข้อมูลส่วนบุคคลอื่น ๆ

รหัสตัวอย่าง

// 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

ป้อนคำอธิบายรูปภาพที่นี่

ความได้เปรียบ

  • ความปลอดภัยที่ดีขึ้น
  • การ จำกัด การเข้าถึงสำหรับผู้โจมตี
  • เมื่อคุกกี้ถูกขโมยจะมีผลเฉพาะการเข้าใช้ครั้งเดียวเท่านั้น
  • เมื่อผู้ใช้เดิมเข้าถึงไซต์คุณสามารถตรวจจับและแจ้งผู้ใช้ว่าถูกขโมยโดยอัตโนมัติ

ข้อเสียเปรียบ

  • ไม่รองรับการเชื่อมต่อแบบถาวรผ่านเบราว์เซอร์หลายตัว (มือถือ & เว็บ)
  • คุกกี้ยังสามารถถูกขโมยได้เนื่องจากผู้ใช้จะได้รับการแจ้งเตือนหลังจากเข้าสู่ระบบครั้งต่อไปเท่านั้น

แก้ไขด่วน

  • แนะนำระบบการอนุมัติสำหรับแต่ละระบบที่ต้องมีการเชื่อมต่อแบบต่อเนื่อง
  • ใช้คุกกี้หลายรายการสำหรับการรับรองความถูกต้อง

วิธีการหลายคุกกี้

เมื่อผู้โจมตีกำลังจะขโมยคุกกี้จะให้ความสำคัญกับเว็บไซต์หรือโดเมนเฉพาะเช่น example.com

แต่จริงๆคุณสามารถรับรองความถูกต้องผู้ใช้จาก 2 โดเมนที่แตกต่างกัน ( example.com & fakeaddsite.com ) และทำให้ดูเหมือนว่า "Advert Cookie"

  • ผู้ใช้ล็อกออนเข้าสู่example.comด้วยจดจำฉัน
  • ชื่อผู้ใช้โทเค็นการอ้างอิงในคุกกี้
  • ชื่อผู้ใช้ร้านค้าโทเค็นการอ้างอิงในฐานข้อมูลเช่น memcache
  • ส่งรหัส refrence ผ่านทาง get และ iframe ไปที่fakeaddsite.com
  • fakeaddsite.com ใช้การอ้างอิงเพื่อดึงข้อมูลผู้ใช้ & โทเค็นจากฐานข้อมูล
  • fakeaddsite.com เก็บลายเซ็น
  • เมื่อผู้ใช้กลับมาดึงข้อมูลลายเซ็นด้วย iframe จาก fakeaddsite.com
  • รวมข้อมูลและทำการตรวจสอบ
  • ..... คุณรู้ส่วนที่เหลือ

บางคนอาจสงสัยว่าคุณจะใช้คุกกี้ 2 แบบได้อย่างไร ดีเป็นไปได้คิดและexample.com = localhost fakeaddsite.com = 192.168.1.120หากคุณตรวจสอบคุกกี้มันจะมีลักษณะเช่นนี้

ป้อนคำอธิบายรูปภาพที่นี่

จากภาพด้านบน

  • เว็บไซต์ปัจจุบันที่เข้าชมคือ localhost
  • นอกจากนี้ยังมีชุดคุกกี้จาก 192.168.1.120

192.168.1.120

  • ยอมรับเฉพาะที่กำหนดไว้ HTTP_REFERER
  • ยอมรับการเชื่อมต่อจากที่ระบุเท่านั้น REMOTE_ADDR
  • ไม่มี JavaScript, ไม่มีเนื้อหา แต่ไม่มีอะไรมากกว่าการลงชื่อเข้าใช้ข้อมูลและเพิ่มหรือดึงข้อมูลจากคุกกี้

ความได้เปรียบ

  • 99% เปอร์เซ็นต์ของเวลาที่คุณหลอกผู้โจมตี
  • คุณสามารถล็อคบัญชีได้อย่างง่ายดายในการโจมตีครั้งแรก
  • การโจมตีสามารถป้องกันได้แม้กระทั่งก่อนเข้าสู่ระบบครั้งต่อไปเช่นเดียวกับวิธีอื่น ๆ

ข้อเสียเปรียบ

  • การร้องขอหลายครั้งไปยังเซิร์ฟเวอร์สำหรับการเข้าสู่ระบบครั้งเดียวเท่านั้น

การปรับปรุง

  • ใช้ iframe เสร็จแล้วใช้งาน ajax

5
แม้ว่า @ircmaxell อธิบายทฤษฎีค่อนข้างดี แต่ฉันชอบวิธีนี้เพราะมันใช้งานได้ดีโดยไม่ต้องเก็บ ID ผู้ใช้ (ซึ่งจะเป็นการเปิดเผยที่ไม่ต้องการ) และยังมีลายนิ้วมือมากกว่า ID ผู้ใช้และแฮชเพื่อระบุ ผู้ใช้เช่นเบราว์เซอร์ ทำให้ผู้โจมตีใช้คุกกี้ที่ถูกขโมยได้ยากขึ้น มันเป็นวิธีที่ดีที่สุดและปลอดภัยที่สุดเท่าที่ฉันเคยเห็นมา +1
Marcello Mönkemeyer

6

ฉันถามมุมหนึ่งในคำถามนี้ที่นี่และคำตอบจะนำคุณไปสู่ทุกจังหวะออกเชื่อมโยงคุกกี้ token-ตามที่คุณต้องการ

โดยทั่วไปคุณจะไม่เก็บ userId ในคุกกี้ คุณเก็บโทเค็นครั้งเดียว (สตริงขนาดใหญ่) ซึ่งผู้ใช้ใช้เพื่อเลือกเซสชันการเข้าสู่ระบบเก่าของพวกเขา จากนั้นเพื่อให้ปลอดภัยจริงๆคุณขอรหัสผ่านสำหรับการทำงานหนัก (เช่นการเปลี่ยนรหัสผ่านเอง)


6

เธรดเก่า แต่ยังคงเป็นปัญหาที่ถูกต้อง ฉันสังเกตเห็นการตอบสนองที่ดีเกี่ยวกับความปลอดภัยและหลีกเลี่ยงการใช้ 'ความปลอดภัยผ่านความสับสน' แต่วิธีการทางเทคนิคที่แท้จริงนั้นไม่เพียงพอในสายตาของฉัน สิ่งที่ฉันต้องพูดก่อนที่จะมีส่วนร่วมวิธีการของฉัน:

  • ไม่เคยเก็บรหัสผ่านเป็นข้อความที่ชัดเจน ... เคยมา!
  • ไม่เคยเก็บรหัสผ่านที่แฮชของผู้ใช้ในที่ตั้งมากกว่าหนึ่งแห่งในฐานข้อมูลของคุณ แบ็กเอนด์เซิร์ฟเวอร์ของคุณสามารถดึงรหัสผ่านที่แฮชได้จากตารางผู้ใช้เสมอ มันไม่ได้มีประสิทธิภาพมากขึ้นในการจัดเก็บข้อมูลที่ซ้ำซ้อนแทนการทำธุรกรรมฐานข้อมูลเพิ่มเติมผกผันเป็นจริง
  • รหัสเซสชันของคุณควรจะไม่ซ้ำกันเพื่อไม่ให้ผู้ใช้สองคนอาจจะเคยแบ่งปัน ID ดังนั้นวัตถุประสงค์ของประชาชน (ที่อาจจำนวนใบอนุญาต ID ขับรถของคุณเคยตรงกับบุคคลอื่นได้หรือไม่ไม่) นี้จะสร้างสองชิ้นรวมกันที่ไม่ซ้ำกันขึ้นอยู่กับ 2 สตริงที่ไม่ซ้ำกัน ตารางเซสชั่นของคุณควรใช้ ID เป็น PK หากต้องการอนุญาตให้อุปกรณ์หลายเครื่องเชื่อถือได้สำหรับการลงชื่อเข้าใช้อัตโนมัติให้ใช้ตารางอื่นสำหรับอุปกรณ์ที่เชื่อถือได้ซึ่งมีรายการอุปกรณ์ที่ตรวจสอบความถูกต้องทั้งหมด (ดูตัวอย่างด้านล่าง) และแมปโดยใช้ชื่อผู้ใช้
  • มันไม่มีจุดประสงค์ในการแฮชข้อมูลที่รู้จักไปยังคุกกี้คุกกี้สามารถคัดลอกได้ สิ่งที่เรากำลังมองหาคืออุปกรณ์ของผู้ใช้ที่ปฏิบัติตามกฎระเบียบเพื่อให้ข้อมูลจริงที่ไม่สามารถรับได้หากผู้โจมตีไม่ทำอันตรายต่อเครื่องของผู้ใช้ (อีกครั้งโปรดดูตัวอย่างของฉัน) อย่างไรก็ตามนั่นหมายความว่าผู้ใช้ที่ถูกกฎหมายที่ห้ามข้อมูลคงที่ของเครื่องของเขา (เช่นที่อยู่ MAC, ชื่อโฮสต์ของอุปกรณ์, ชื่อผู้ใช้หากถูก จำกัด โดยเบราว์เซอร์ ฯลฯ ) จากความสอดคล้องที่เหลืออยู่ (หรือปลอมแปลงในตอนแรก) จะไม่สามารถ ใช้คุณสมบัตินี้ แต่ถ้านี่เป็นข้อกังวลให้พิจารณาข้อเท็จจริงที่ว่าคุณกำลังเสนอการลงชื่อเข้าใช้อัตโนมัติให้กับผู้ใช้ที่ระบุตัวตนของพวกเขาโดยเฉพาะดังนั้นหากพวกเขาปฏิเสธที่จะเป็นที่รู้จักโดยการปลอมแปลง MAC ของพวกเขาปลอมแปลงผู้ใช้ของพวกเขาปลอมแปลง / เปลี่ยนชื่อโฮสต์ซ่อนอยู่หลังพร็อกซี่ ฯลฯ พวกเขาจะไม่สามารถระบุตัวตนได้และไม่ควรรับรองความถูกต้องสำหรับบริการอัตโนมัติ หากคุณต้องการสิ่งนี้คุณจะต้องตรวจสอบการเข้าถึงสมาร์ทการ์ดที่มาพร้อมกับซอฟต์แวร์ฝั่งไคลเอ็นต์ที่สร้างรหัสประจำตัวสำหรับอุปกรณ์ที่ใช้

ทุกอย่างถูกกล่าวว่ามีสองวิธีที่ดีในการลงชื่อเข้าใช้อัตโนมัติในระบบของคุณ

ครั้งแรกวิธีที่ง่ายและราคาถูกที่ทำให้ทุกคนเป็นคนอื่น หากคุณทำให้ไซต์ของคุณรองรับการลงชื่อเข้าใช้ด้วยพูดว่าบัญชี google + ของคุณคุณอาจจะมีปุ่ม google + ที่เพรียวบางที่จะเข้าสู่ระบบผู้ใช้หากพวกเขาลงชื่อเข้าใช้แล้วใน google (ฉันทำแบบนั้นเพื่อตอบคำถามนี้ ลงชื่อเข้าใช้ google) หากคุณต้องการให้ผู้ใช้ลงชื่อเข้าใช้โดยอัตโนมัติหากพวกเขาลงชื่อเข้าใช้ด้วยตัวรับรองความถูกต้องที่เชื่อถือได้และได้รับการสนับสนุนและทำเครื่องหมายที่ช่องทำเช่นนั้นให้สคริปต์ฝั่งไคลเอ็นต์ของคุณดำเนินการโค้ดด้านหลังปุ่ม 'ลงชื่อเข้าใช้ด้วย' ตรวจสอบให้แน่ใจว่าเซิร์ฟเวอร์จัดเก็บ ID เฉพาะในตารางลงชื่อเข้าใช้อัตโนมัติที่มีชื่อผู้ใช้ ID เซสชันและตัวรับรองความถูกต้องที่ใช้สำหรับผู้ใช้ เนื่องจากวิธีการลงชื่อเข้าใช้เหล่านี้ใช้ AJAX คุณกำลังรอการตอบกลับอยู่ดี และการตอบสนองนั้นเป็นการตอบสนองที่ผ่านการตรวจสอบหรือปฏิเสธ หากคุณได้รับการตอบกลับที่ถูกต้องให้ใช้ตามปกติจากนั้นทำการโหลดผู้ใช้ที่เข้าสู่ระบบตามปกติ มิฉะนั้นการเข้าสู่ระบบล้มเหลว แต่ไม่บอกผู้ใช้เพียงแค่ดำเนินการต่อโดยไม่ได้ลงชื่อเข้าใช้พวกเขาจะสังเกตเห็น นี่คือเพื่อป้องกันไม่ให้ผู้โจมตีที่ขโมยคุกกี้ (หรือปลอมแปลงพวกเขาในความพยายามที่จะเพิ่มสิทธิ์) จากการเรียนรู้ว่าผู้ใช้ลงชื่อเข้าใช้เว็บไซต์โดยอัตโนมัติ

สิ่งนี้ราคาถูกและอาจถูกพิจารณาว่าสกปรกโดยบางคนเพราะมันพยายามตรวจสอบความถูกต้องที่ลงชื่อเข้าใช้ด้วยตัวเองกับสถานที่ต่างๆเช่น Google และ Facebook โดยไม่บอกคุณเลย อย่างไรก็ตามไม่ควรใช้กับผู้ใช้ที่ไม่ได้ขอให้ลงชื่อเข้าใช้ไซต์ของคุณโดยอัตโนมัติและวิธีการเฉพาะนี้ใช้สำหรับการรับรองความถูกต้องภายนอกเช่นเดียวกับ Google+ หรือ FB

เนื่องจากมีการใช้ตัวพิสูจน์ตัวตนภายนอกเพื่อแจ้งเซิร์ฟเวอร์เบื้องหลังว่าผู้ใช้ได้รับการตรวจสอบความถูกต้องหรือไม่ผู้โจมตีจึงไม่สามารถรับสิ่งอื่นใดนอกจากรหัสเฉพาะซึ่งไม่มีประโยชน์ในตัวเอง ฉันจะทำอย่างละเอียด:

  • ผู้ใช้ 'joe' เข้าเยี่ยมชมเว็บไซต์เป็นครั้งแรกรหัสเซสชันที่วางในคุกกี้ 'เซสชัน'
  • ผู้ใช้ 'joe' เข้าสู่ระบบเพิ่มระดับสิทธิ์รับ ID เซสชันใหม่และต่ออายุคุกกี้ 'เซสชัน'
  • ผู้ใช้ 'joe' เลือกที่จะลงชื่อเข้าใช้อัตโนมัติโดยใช้ google + รับ ID ที่ไม่ซ้ำกันในคุกกี้ 'keepmesignedin'
  • ผู้ใช้ 'joe' มี google ให้ลงชื่อเข้าใช้ทำให้ไซต์ของคุณลงชื่อเข้าใช้อัตโนมัติโดยใช้ google ในแบ็กเอนด์ของคุณ
  • Attacker จะพยายามใช้ ID ที่ไม่ซ้ำกันสำหรับ 'keepmesignedin' (นี่คือความรู้สาธารณะที่มอบให้กับผู้ใช้ทุกคน) และไม่ได้ลงชื่อเข้าใช้ที่อื่น ลองใช้ ID เฉพาะสำหรับ 'joe'
  • เซิร์ฟเวอร์ได้รับ ID เฉพาะสำหรับ 'joe' ดึงการจับคู่ในฐานข้อมูลสำหรับบัญชี google +
  • เซิร์ฟเวอร์ส่งผู้โจมตีไปยังหน้าเข้าสู่ระบบที่เรียกใช้คำขอ AJAX ไปยัง google เพื่อเข้าสู่ระบบ
  • เซิร์ฟเวอร์ Google ได้รับคำขอใช้ API เพื่อดู Attacker ไม่ได้เข้าสู่ระบบในขณะนี้
  • Google ส่งการตอบกลับว่าไม่มีผู้ใช้ลงชื่อเข้าใช้ผ่านการเชื่อมต่อนี้
  • หน้าของผู้โจมตีได้รับการตอบสนองสคริปต์จะเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบโดยอัตโนมัติด้วยค่า POST ที่เข้ารหัสใน URL
  • หน้าเข้าสู่ระบบจะได้รับค่า POST ส่งคุกกี้สำหรับ 'keepmesignedin' เป็นค่าว่างและใช้ได้จนถึงวันที่ 1-1-1970 เพื่อยับยั้งการพยายามอัตโนมัติทำให้เบราว์เซอร์ของผู้โจมตีสามารถลบคุกกี้ได้ง่าย
  • Attacker จะได้รับหน้าล็อกอินครั้งแรกตามปกติ

ไม่ว่าจะเกิดอะไรขึ้นแม้ว่าผู้โจมตีจะใช้ 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

โปรดทราบว่าไม่มีฟิลด์ผู้ใช้ในตารางนี้เนื่องจากชื่อผู้ใช้เมื่อเข้าสู่ระบบอยู่ในข้อมูลเซสชั่นและโปรแกรมไม่อนุญาตให้มีข้อมูลที่เป็นโมฆะ session_id และ session_token สามารถสร้างขึ้นได้โดยใช้แฮ็คแบบ md5 แบบสุ่ม, sha1 / 128/256 hashes, การประทับวันที่พร้อมกับสตริงแบบสุ่มที่เพิ่มเข้ามาจากนั้นแฮชหรืออะไรก็ตามที่คุณต้องการ แต่เอนโทรปีของผลลัพธ์ของคุณควรอยู่ในระดับสูง ลดการจู่โจมแบบไม่ใช้กำลังแม้กระทั่งการหลุดจากพื้นและควรตรวจสอบแฮชทั้งหมดที่สร้างโดยคลาสเซสชันของคุณเพื่อหาแมตช์ในตารางเซสชั่นก่อนที่จะพยายามเพิ่ม

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 ตามธรรมชาติของพวกเขาควรจะเป็น UNIQUE ดังนั้นจึงเหมาะสมที่แต่ละรายการมีค่าที่ไม่ซ้ำกัน ในทางกลับกันชื่อโฮสต์สามารถทำซ้ำในเครือข่ายแยกกันอย่างถูกกฎหมาย มีกี่คนที่ใช้ "Home-PC" เป็นหนึ่งในชื่อคอมพิวเตอร์ของพวกเขา ชื่อผู้ใช้มาจากข้อมูลเซสชันโดยแบ็กเอนด์เซิร์ฟเวอร์ดังนั้นการจัดการจึงเป็นไปไม่ได้ สำหรับโทเค็นวิธีเดียวกันในการสร้างโทเค็นเซสชันสำหรับหน้าควรใช้เพื่อสร้างโทเค็นในคุกกี้สำหรับผู้ใช้ลงชื่อเข้าใช้อัตโนมัติ สุดท้ายรหัสวันที่จะถูกเพิ่มเมื่อผู้ใช้จะต้องตรวจสอบข้อมูลประจำตัวของพวกเขาอีกครั้ง อัปเดตวันที่และเวลานี้ในการเข้าสู่ระบบของผู้ใช้ที่เก็บไว้ภายในไม่กี่วันหรือบังคับให้หมดอายุโดยไม่คำนึงถึงการเข้าสู่ระบบครั้งล่าสุดทำให้มันเป็นเวลาเพียงหนึ่งเดือนหรือมากกว่านั้นแล้วแต่ว่าสิ่งใดก็ตาม

สิ่งนี้ป้องกันไม่ให้ใครบางคนจากการปลอมแปลง MAC และชื่อโฮสต์สำหรับผู้ใช้ที่พวกเขารู้ว่าการลงชื่อเข้าใช้อัตโนมัติไม่เป็นระบบให้ผู้ใช้เก็บคุกกี้ด้วยรหัสผ่านข้อความที่ชัดเจนหรืออย่างอื่น ให้สร้างโทเค็นใหม่ในการนำทางแต่ละหน้าเช่นเดียวกับที่คุณทำกับโทเค็นเซสชัน สิ่งนี้จะลดความน่าจะเป็นที่ผู้โจมตีจะได้รับโทเค็นคุกกี้ที่ถูกต้องและใช้เพื่อเข้าสู่ระบบ บางคนจะพยายามบอกว่าผู้โจมตีสามารถขโมยคุกกี้จากเหยื่อและทำการโจมตีซ้ำเพื่อเข้าสู่ระบบ หากผู้โจมตีสามารถขโมยคุกกี้ได้ (ซึ่งเป็นไปได้) พวกเขาจะทำลายอุปกรณ์ทั้งหมดอย่างแน่นอนซึ่งหมายความว่าพวกเขาสามารถใช้อุปกรณ์เพื่อเข้าสู่ระบบต่อไปได้ซึ่งจะเป็นการทำลายวัตถุประสงค์ในการขโมยคุกกี้โดยสิ้นเชิง ตราบใดที่เว็บไซต์ของคุณทำงานบน HTTPS (ซึ่งควรทำเมื่อจัดการกับรหัสผ่านหมายเลข CC หรือระบบการเข้าสู่ระบบอื่น ๆ ) คุณจะได้รับความคุ้มครองทั้งหมดต่อผู้ใช้ที่คุณสามารถทำได้ในเบราว์เซอร์

สิ่งหนึ่งที่ควรทราบ: ข้อมูลเซสชันไม่ควรหมดอายุหากคุณใช้การลงชื่อเข้าใช้อัตโนมัติ คุณสามารถหมดอายุความสามารถในการใช้งานเซสชันต่อไปอย่างไม่ถูกต้อง แต่การตรวจสอบความถูกต้องในระบบควรดำเนินการข้อมูลเซสชันต่อหากเป็นข้อมูลถาวรที่คาดว่าจะดำเนินการต่อระหว่างเซสชัน หากคุณต้องการข้อมูลเซสชันทั้งแบบถาวรและแบบไม่ถาวรให้ใช้ตารางอื่นสำหรับข้อมูลเซสชันแบบถาวรด้วยชื่อผู้ใช้เป็น PK และให้เซิร์ฟเวอร์ดึงข้อมูลเช่นเดียวกับข้อมูลเซสชันปกติให้ใช้ตัวแปรอื่น

เมื่อลงชื่อเข้าใช้ด้วยวิธีนี้เซิร์ฟเวอร์ควรตรวจสอบเซสชัน นี่คือที่ที่คุณสามารถคาดหวังรหัสสำหรับระบบที่ถูกขโมยหรือถูกบุกรุก รูปแบบและผลลัพธ์อื่น ๆ ที่คาดหวังจากการเข้าสู่ข้อมูลเซสชั่นมักจะนำไปสู่ข้อสรุปว่าระบบถูกแย่งชิงหรือปลอมแปลงคุกกี้เพื่อเข้าถึง นี่คือที่ที่ ISS Tech ของคุณสามารถวางกฎที่จะเรียกใช้การล็อคบัญชีหรือการลบผู้ใช้ออกจากระบบลงชื่อเข้าใช้อัตโนมัติทำให้ผู้โจมตีออกมานานพอสำหรับผู้ใช้ในการพิจารณาว่าผู้โจมตีประสบความสำเร็จได้อย่างไรและจะตัดทิ้งอย่างไร

ในฐานะที่เป็นข้อความปิดตรวจสอบให้แน่ใจว่ามีความพยายามในการกู้คืนการเปลี่ยนแปลงรหัสผ่านหรือการเข้าสู่ระบบล้มเหลวผ่านเกณฑ์ผลลัพธ์ในการลงชื่อเข้าใช้อัตโนมัติถูกปิดใช้งานจนกว่าผู้ใช้จะตรวจสอบความถูกต้อง

ฉันขอโทษถ้าใครคาดหวังว่าจะได้รับรหัสในคำตอบของฉันนั่นจะไม่เกิดขึ้นที่นี่ ฉันจะบอกว่าฉันใช้ PHP, jQuery และ AJAX เพื่อเรียกใช้เว็บไซต์ของฉันและฉันไม่เคยใช้ Windows เป็นเซิร์ฟเวอร์ ... เคย


5

ฉันจะแนะนำวิธีที่กล่าวถึงโดยสเตฟาน (เช่นปฏิบัติตามแนวทางในการปรับปรุงวิธีปฏิบัติที่ดีที่สุดในการเข้าสู่ระบบคุกกี้ถาวร ) และขอแนะนำให้คุณตรวจสอบให้แน่ใจว่าคุกกี้ของคุณเป็นคุกกี้ HttpOnlyเพื่อไม่ให้เข้าถึง JavaScript


4

สร้างแฮชอาจมีความลับเพียงคุณเท่านั้นที่รู้แล้วเก็บไว้ในฐานข้อมูลของคุณเพื่อให้สามารถเชื่อมโยงกับผู้ใช้ ควรทำงานค่อนข้างดี


นี่จะเป็นตัวระบุที่ไม่ซ้ำกันซึ่งสร้างขึ้นเมื่อผู้ใช้ถูกสร้างขึ้นหรือจะเปลี่ยนแปลงทุกครั้งที่ผู้ใช้สร้างคุกกี้ "Keep Me Logged In" ใหม่หรือไม่
Matthew

1
คำตอบทิม Jansson อธิบายวิธีการที่ดีในการผลิตกัญชาแม้ว่าผมจะรู้สึกปลอดภัยมากขึ้นถ้ามันไม่ได้รวมถึงรหัสผ่าน
จานี่ฮาร์ติไกเนน

2

ทางออกของฉันเป็นเช่นนี้ มันไม่ได้เป็นกระสุน 100% แต่ฉันคิดว่ามันจะช่วยให้คุณประหยัดได้มากที่สุด

เมื่อผู้ใช้ล็อกอินสำเร็จสร้างสตริงด้วยข้อมูลนี้:

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

เข้ารหัส$dataตั้งประเภทเป็นHttpOnlyและตั้งค่าคุกกี้

เมื่อผู้ใช้กลับมาที่ไซต์ของคุณทำขั้นตอนนี้:

  1. รับข้อมูลคุกกี้ ลบอักขระอันตรายภายในคุกกี้ ระเบิดมันด้วย:ตัวละคร
  2. ตรวจสอบความถูกต้อง หากคุกกี้มีอายุมากกว่า X วันให้เปลี่ยนเส้นทางผู้ใช้ไปยังหน้าเข้าสู่ระบบ
  3. หากคุกกี้ยังไม่แก่ รับเวลาเปลี่ยนรหัสผ่านล่าสุดจากฐานข้อมูล หากมีการเปลี่ยนรหัสผ่านหลังจากล็อกอินครั้งสุดท้ายของผู้ใช้จะเปลี่ยนเส้นทางผู้ใช้ไปยังหน้าเข้าสู่ระบบ
  4. หากไม่ได้เปลี่ยนพาส รับตัวแทนเบราว์เซอร์ปัจจุบันของผู้ใช้ ตรวจสอบว่า (currentUserAgentHash == cookieUserAgentHash) หากตัวแทนเหมือนกันไปที่ขั้นตอนถัดไปมิฉะนั้นให้เปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบ
  5. หากขั้นตอนทั้งหมดผ่านการอนุญาตชื่อผู้ใช้สำเร็จ

หากผู้ใช้ลงชื่อเข้าใช้ให้ลบคุกกี้นี้ สร้างคุกกี้ใหม่หากผู้ใช้เข้าสู่ระบบอีกครั้ง


2

ฉันไม่เข้าใจแนวคิดของการจัดเก็บสิ่งที่เข้ารหัสไว้ในคุกกี้เมื่อเป็นรุ่นที่เข้ารหัสซึ่งคุณต้องทำการแฮ็ค หากฉันทำอะไรหายไปโปรดแสดงความคิดเห็น

ฉันกำลังคิดที่จะใช้วิธีนี้ในการ 'จดจำฉัน' หากคุณสามารถเห็นปัญหาใด ๆ โปรดแสดงความคิดเห็น

  1. สร้างตารางเพื่อเก็บข้อมูล "จดจำฉัน" ไว้ - แยกจากตารางผู้ใช้เพื่อให้ฉันสามารถเข้าสู่ระบบได้จากอุปกรณ์หลายเครื่อง

  2. ในการเข้าสู่ระบบที่ประสบความสำเร็จ (ด้วยการจดจำเครื่องหมายถูก):

    a) สร้างสตริงสุ่มที่ไม่ซ้ำกันเพื่อใช้เป็น UserID บนเครื่องนี้: bigUserID

    b) สร้างสตริงสุ่มที่ไม่ซ้ำกัน: bigKey

    c) เก็บคุกกี้: bigUserID: bigKey

    d) ในตาราง "จดจำฉัน" เพิ่มระเบียนด้วย: UserID, ที่อยู่ IP, bigUserID, bigKey

  3. หากพยายามเข้าถึงบางสิ่งที่ต้องมีการเข้าสู่ระบบ:

    a) ตรวจสอบคุกกี้และค้นหา bigUserID & bigKey ด้วยที่อยู่ IP ที่ตรงกัน

    b) หากคุณพบเข้าสู่ระบบบุคคล แต่ตั้งค่าสถานะในตารางผู้ใช้ "soft เข้าสู่ระบบ" เพื่อให้การดำเนินงานที่เป็นอันตรายใด ๆ คุณสามารถแจ้งให้เข้าสู่ระบบเต็ม

  4. เมื่อออกจากระบบให้ทำเครื่องหมายระเบียน "จดจำฉัน" ทั้งหมดสำหรับผู้ใช้นั้นว่าหมดอายุแล้ว

ช่องโหว่เดียวที่ฉันเห็นคือ;

  • คุณสามารถถือแล็ปท็อปของใครบางคนและหลอกที่อยู่ IP ของพวกเขาด้วยคุกกี้
  • คุณสามารถหลอกที่อยู่ IP ที่แตกต่างกันในแต่ละครั้งและคาดเดาได้ทั้งหมด แต่ด้วยการจับคู่สองสตริงใหญ่นั่นก็คือ ... ทำการคำนวณที่คล้ายกันกับด้านบน ... ฉันไม่รู้เลยว่า ... อัตราเดิมพันมาก

สวัสดีและขอบคุณสำหรับคำตอบนี้ฉันชอบ แต่คำถามหนึ่งข้อ: ทำไมคุณต้องสร้าง 2 สตริงสุ่ม - bigUserID & bigKey ทำไมคุณไม่สร้างเพียง 1 และใช้มัน
Jeremy Belolo

2
bigKey หมดอายุหลังจากระยะเวลาที่กำหนดไว้ล่วงหน้า แต่ bigUserID ไม่ bigUserID คืออนุญาตให้คุณมีหลายเซสชันในอุปกรณ์ต่าง ๆ ที่อยู่ IP เดียวกัน หวังว่าเหมาะสมแล้ว - ฉันต้องคิดสักครู่ :)
Enigma Plus

สิ่งหนึ่งที่มี hmac สามารถช่วยได้คือถ้าคุณพบว่า hmac ถูกดัดแปลงคุณสามารถรู้ได้อย่างแน่นอนว่ามีคนพยายามขโมยคุกกี้จากนั้นคุณสามารถรีเซ็ตทุกสถานะการเข้าสู่ระบบ ฉันถูกไหม?
Suraj Jain

2

ฉันอ่านคำตอบทั้งหมดและยังพบว่าเป็นการยากที่จะแยกสิ่งที่ฉันควรทำ หากรูปภาพมีค่า 1k คำฉันหวังว่านี่จะช่วยให้ผู้อื่นใช้ที่เก็บข้อมูลถาวรที่ปลอดภัยโดยอ้างอิงจากคุกกี้การเข้าสู่ระบบที่ปรับปรุงใหม่ของ Barry Jaspan

ป้อนคำอธิบายรูปภาพที่นี่

หากคุณมีคำถามข้อเสนอแนะหรือข้อเสนอแนะฉันจะพยายามอัปเดตไดอะแกรมเพื่อสะท้อนถึงมือใหม่ที่พยายามใช้การเข้าสู่ระบบแบบถาวรที่ปลอดภัย


0

การใช้คุณสมบัติ "Keep Me Logged In" หมายความว่าคุณต้องกำหนดสิ่งที่จะมีความหมายกับผู้ใช้ ในกรณีที่ง่ายที่สุดฉันจะใช้เพื่อหมายถึงเซสชั่นมีการหมดเวลานานกว่า: 2 วัน (พูด) แทน 2 ชั่วโมง ในการทำเช่นนั้นคุณจะต้องใช้ที่เก็บข้อมูลเซสชันของคุณเองซึ่งอาจอยู่ในฐานข้อมูลดังนั้นคุณจึงสามารถกำหนดเวลาหมดอายุที่กำหนดเองสำหรับข้อมูลเซสชันได้ จากนั้นคุณต้องตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่าคุกกี้ที่จะติดอยู่สองสามวัน (หรือนานกว่า) แทนที่จะหมดอายุเมื่อปิดเบราว์เซอร์

ฉันได้ยินคุณถามว่า "ทำไม 2 วันทำไมไม่ 2 สัปดาห์" เนื่องจากการใช้งานเซสชั่นใน PHP จะผลักดันการหมดอายุโดยอัตโนมัติ นี่เป็นเพราะการหมดอายุของเซสชันใน PHP นั้นแท้จริงแล้วเป็นการหมดเวลาที่ไม่ได้ใช้งาน

ตอนนี้ต้องบอกว่าฉันอาจใช้ค่าหมดเวลาที่ยากขึ้นที่ฉันเก็บไว้ในเซสชั่นตัวเองและออกที่ 2 สัปดาห์หรือมากกว่านั้นและเพิ่มรหัสเพื่อดูว่าและบังคับเซสชั่นโมฆะ หรืออย่างน้อยก็ออกจากระบบ นี่จะหมายความว่าผู้ใช้จะถูกขอให้เข้าสู่ระบบเป็นระยะ Yahoo! ทำสิ่งนี้.


1
การตั้งค่าเซสชันที่ยาวนานกว่านั้นอาจไม่ดีเพราะมันเปลืองทรัพยากรของเซิร์ฟเวอร์และจะส่งผลกระทบต่อประสิทธิภาพในการทำงาน
user3091530

0

ฉันคิดว่าคุณสามารถทำได้:

$cookieString = password_hash($username, PASSWORD_DEFAULT);

เก็บ $cookiestringในฐานข้อมูลและตั้งเป็นคุกกี้ ตั้งค่าชื่อผู้ใช้ของบุคคลเป็นคุกกี้ จุดรวมของแฮชคือมันไม่สามารถวิศวกรรมย้อนกลับได้

เมื่อผู้ใช้เปิดขึ้นให้รับชื่อผู้ใช้จากคุกกี้หนึ่งมากกว่า$cookieStringจากที่อื่น หาก$cookieStringตรงกับที่เก็บไว้ในฐานข้อมูลผู้ใช้จะได้รับการตรวจสอบสิทธิ์ เนื่องจาก password_hash ใช้เกลือที่แตกต่างกันในแต่ละครั้งจึงไม่เกี่ยวข้องกับข้อความที่ชัดเจน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.