การทำให้โทเค็น JSON Web Tokens ไม่ถูกต้อง


421

สำหรับโครงการใหม่ node.js ที่ฉันกำลังทำงานฉันกำลังคิดที่จะเปลี่ยนจากวิธีเซสชันแบบใช้คุกกี้ (โดยสิ่งนี้ฉันหมายถึงการจัดเก็บรหัสไปยังที่เก็บคีย์ - ค่าที่มีเซสชันผู้ใช้ในเบราว์เซอร์ของผู้ใช้) สู่แนวทางเซสชันที่ใช้โทเค็น (ไม่มีที่เก็บคีย์ - ค่า) โดยใช้ JSON Web Tokens (jwt)

โครงการนี้เป็นเกมที่ใช้ socket.io - การมีเซสชันที่ใช้โทเค็นจะเป็นประโยชน์ในสถานการณ์ดังกล่าวซึ่งจะมีช่องทางการสื่อสารหลายช่องในเซสชันเดียว (เว็บและ socket.io)

หนึ่งจะให้โทเค็น / เซสชั่นไม่ถูกต้องจากเซิร์ฟเวอร์โดยใช้วิธีการ jwt?

ฉันยังต้องการที่จะเข้าใจถึงข้อผิดพลาด / การโจมตีที่พบบ่อย (หรือผิดปกติ) ที่ฉันควรระวังด้วยกระบวนทัศน์ประเภทนี้ ตัวอย่างเช่นหากกระบวนทัศน์นี้มีความเสี่ยงต่อการโจมตีประเภทเดียวกัน / แตกต่างกันกับวิธีการจัดเก็บเซสชัน / คุกกี้ตามวิธี

ดังนั้นบอกว่าฉันมีดังต่อไปนี้ (ดัดแปลงมาจากสิ่งนี้และสิ่งนี้ ):

เข้าสู่ระบบร้านค้าเซสชัน:

app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        // Create session token
        var token= createSessionToken();

        // Add to a key-value database
        KeyValueStore.add({token: {userid: profile.id, expiresInMinutes: 60}});

        // The client should save this session token in a cookie
        response.json({sessionToken: token});
    });
}

การลงชื่อเข้าใช้แบบ Token:

var jwt = require('jsonwebtoken');
app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        var token = jwt.sign(profile, 'My Super Secret', {expiresInMinutes: 60});
        response.json({token: token});
    });
}

-

การล็อกเอาต์ (หรือไม่ถูกต้อง) สำหรับวิธีการในที่จัดเก็บเซสชันจะต้องมีการอัพเดทฐานข้อมูล KeyValueStore ด้วยโทเค็นที่ระบุ

ดูเหมือนว่ากลไกดังกล่าวจะไม่มีอยู่ในวิธีที่ใช้โทเค็นเนื่องจากโทเค็นเองจะมีข้อมูลที่ปกติจะมีอยู่ในที่เก็บคีย์ - ค่า


1
หากคุณใช้แพ็คเกจ 'express-jwt' คุณสามารถดูisRevokedตัวเลือกหรือพยายามทำซ้ำฟังก์ชันการทำงานเดียวกัน github.com/auth0/express-jwt#revoked-tokens
— Signus

1
พิจารณาใช้เวลาหมดอายุสั้น ๆ ในโทเค็นการเข้าถึงและใช้โทเค็นการรีเฟรชที่มีการหมดอายุที่ยาวนานเพื่อให้สามารถตรวจสอบสถานะการเข้าถึงของผู้ใช้ในฐานข้อมูล (บัญชีดำ) auth0.com/blog/…
— Rohmer

ตัวเลือกอื่นจะแนบที่อยู่ IP ใน payload ขณะที่สร้างโทเค็น jwt และตรวจสอบ IP ที่เก็บไว้เทียบกับคำขอขาเข้าสำหรับที่อยู่ IP เดียวกัน เช่น req.connection.remoteAddress ใน nodeJs มีผู้ให้บริการ ISP ที่ไม่ออก IP แบบคงที่ต่อลูกค้าฉันคิดว่านี่จะไม่เกิดปัญหาหากลูกค้าเชื่อมต่ออินเทอร์เน็ตใหม่
— Gihan Sandaru

คำตอบ:


392

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

1) เพียงลบโทเค็นออกจากไคลเอนต์

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

2) สร้างบัญชีดำโทเค็น

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

3) เพียงแค่ทำให้โทเค็นมีอายุสั้นและหมุนบ่อยๆ

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

แผนฉุกเฉิน

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

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

ในแง่ของความคล้ายคลึง / ความแตกต่างที่เกี่ยวข้องกับการโจมตีโดยใช้โทเค็นโพสต์นี้ตอบคำถาม: https://github.com/dentarg/blog/blob/master/_posts/2014-01-07-angularjs-authentication-with-cookies -vs-token.markdown


3
วิธีการที่ยอดเยี่ยม ลำไส้ของฉันจะทำการรวมกันของทั้ง 3 และ / หรือขอโทเค็นใหม่หลังจากคำขอ "n" ทุกครั้ง (ตรงข้ามกับตัวจับเวลา) เรากำลังใช้ redis สำหรับการจัดเก็บวัตถุในหน่วยความจำและเราสามารถใช้มันได้อย่างง่ายดายสำหรับกรณี # 2 แล้วเวลาแฝงจะลดลง
— Aaron Wagner

2
โพสต์สยองขวัญการเข้ารหัสนี้มีคำแนะนำบางอย่าง: ทำให้คุกกี้แบกเซสชัน (หรือโทเค็น) สั้น แต่ทำให้ผู้ใช้มองไม่เห็น - ซึ่งดูเหมือนจะสอดคล้องกับ # 3 ความกล้าของตัวเอง (อาจเป็นเพราะดั้งเดิมมากกว่า) คือการมีโทเค็น (หรือแฮชของมัน) ทำหน้าที่เป็นกุญแจสู่ฐานข้อมูลเซสชั่นสีขาว (คล้ายกับ # 2)
— funseiki

7
บทความนี้เขียนได้ดีและเป็นเวอร์ชั่นที่กล่าวถึง2)ข้างต้นอย่างละเอียด ในขณะที่ใช้งานได้ดีโดยส่วนตัวฉันไม่เห็นความแตกต่างมากกับร้านค้าเซสชันดั้งเดิม ฉันเดาว่าความต้องการพื้นที่เก็บข้อมูลจะลดลง แต่คุณยังต้องการฐานข้อมูล สิ่งที่ดึงดูดใจมากที่สุดของ JWT สำหรับฉันคือการไม่ใช้ฐานข้อมูลเลยสำหรับการประชุม
— วิธีที่แมตต์

210
วิธีการทั่วไปในการทำให้โทเค็นเป็นโมฆะเมื่อผู้ใช้เปลี่ยนรหัสผ่านของตนคือลงชื่อโทเค็นด้วยแฮชของรหัสผ่าน ดังนั้นหากรหัสผ่านมีการเปลี่ยนแปลงโทเค็นก่อนหน้านี้จะล้มเหลวในการตรวจสอบโดยอัตโนมัติ คุณสามารถขยายสิ่งนี้สู่การออกจากระบบโดยรวมการออกจากระบบครั้งสุดท้ายในบันทึกของผู้ใช้และการใช้การรวมกันของแฮกเกอร์ครั้งสุดท้ายที่ออกจากระบบและรหัสผ่านเพื่อเซ็นโทเค็น สิ่งนี้ต้องใช้การค้นหาฐานข้อมูลทุกครั้งที่คุณต้องการตรวจสอบลายเซ็นโทเค็น แต่สันนิษฐานว่าคุณกำลังค้นหาผู้ใช้อยู่ดี
— เทรวิสเทอร์รี่

4
บัญชีดำสามารถทำให้มีประสิทธิภาพได้โดยเก็บไว้ในหน่วยความจำเพื่อให้ฐานข้อมูลจำเป็นต้องถูกกดเพื่อบันทึกการตรวจสอบความถูกต้องและลบการตรวจสอบความถูกต้องที่หมดอายุแล้วและอ่านเมื่อเปิดตัวเซิร์ฟเวอร์เท่านั้น ภายใต้สถาปัตยกรรมการปรับสมดุลโหลดบัญชีดำในหน่วยความจำสามารถโพลฐานข้อมูลในช่วงเวลาสั้น ๆ เช่น 10 วินาทีซึ่ง จำกัด การเปิดรับโทเค็นที่ไม่ถูกต้อง วิธีการเหล่านี้ช่วยให้เซิร์ฟเวอร์สามารถตรวจสอบคำขอต่อไปได้โดยไม่ต้องเข้าถึงฐานข้อมูลตามคำขอ
— Joe Lapp

86

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

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

ไม่จำเป็นต้องแก้ไขเนื้อหาโทเค็นจริง ๆ (หรือ ID การค้นหา)

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


9
ฉันคิดว่าวิธีนี้ไม่เหมาะ ในขณะที่มันใช้งานได้และง่ายมากลองนึกภาพกรณีที่คุณใช้กุญแจสาธารณะ - คุณไม่ต้องการที่จะไปและสร้างกุญแจใหม่เมื่อใดก็ตามที่คุณต้องการทำให้โทเค็นเดี่ยวใช้ไม่ได้
— Signus

1
@KijanaWoodard คู่กุญแจสาธารณะ / ส่วนตัวสามารถใช้ในการตรวจสอบลายเซ็นเป็นความลับที่มีประสิทธิภาพในอัลกอริทึม RS256 ในตัวอย่างที่แสดงที่นี่เขากล่าวถึงการเปลี่ยนความลับเพื่อทำให้ JWT เป็นโมฆะ ซึ่งสามารถทำได้โดย a) แนะนำ pubkey ปลอมที่ไม่ตรงกับลายเซ็นหรือ b) สร้าง pubkey ใหม่ ในสถานการณ์นั้นมันน้อยกว่าอุดมคติ
— Signus

1
@Signus - gotcha ไม่ใช้รหัสสาธารณะเป็นความลับ แต่คนอื่น ๆ อาจใช้รหัสสาธารณะเพื่อยืนยันลายเซ็น
— Kijana Woodard

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

5
@TuomasToivonen แต่คุณต้องลงชื่อ JWT ด้วยความลับและสามารถยืนยัน JWT ด้วยความลับเดียวกันนั้นได้ ดังนั้นคุณต้องเก็บความลับในทรัพยากรที่ได้รับการคุ้มครอง หากความลับถูกบุกรุกคุณต้องเปลี่ยนและแจกจ่ายการเปลี่ยนแปลงนั้นไปยังแต่ละโหนดของคุณ ผู้ให้บริการโฮสติ้งที่มีการจัดกลุ่ม / การปรับขนาดมักจะอนุญาตให้คุณเก็บความลับในบริการของพวกเขาเพื่อทำให้การกระจายความลับเหล่านี้ง่ายและเชื่อถือ
— Rohmer

67

นี่คือการสนับสนุนหลักในระยะยาวและการสร้างคำตอบโดย @mattway

ได้รับ:

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

(หากไซต์ของคุณได้รับคำขอจำนวนมากที่ไม่ได้รับอนุญาต JWT จะปฏิเสธคำขอโดยไม่ต้องกดปุ่ม Data Datore ซึ่งมีประโยชน์อาจมีกรณีการใช้งานอื่น ๆ เช่นนั้น)

ได้รับ:

การพิสูจน์ตัวตน JWT ไร้สัญชาติอย่างแท้จริงนั้นไม่สามารถทำได้สำหรับเว็บแอปทั่วไปในโลกแห่งความเป็นจริงเพราะ JWT ไร้สัญชาติไม่มีวิธีให้การสนับสนุนทันทีและปลอดภัยสำหรับกรณีการใช้งานที่สำคัญต่อไปนี้:

บัญชีของผู้ใช้ถูกลบ / บล็อก / ระงับ

รหัสผ่านของผู้ใช้มีการเปลี่ยนแปลง

บทบาทหรือการอนุญาตของผู้ใช้มีการเปลี่ยนแปลง

ผู้ใช้ออกจากระบบโดยผู้ดูแลระบบ

ข้อมูลสำคัญของแอปพลิเคชันอื่น ๆ ในโทเค็น JWT จะถูกเปลี่ยนโดยผู้ดูแลไซต์

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

ดังนั้น: ฉันคิดว่าคำตอบจาก @ matt-way, # 2 TokenBlackList จะเป็นวิธีที่มีประสิทธิภาพที่สุดในการเพิ่มสถานะที่ต้องการในการตรวจสอบความถูกต้องโดยใช้ JWT

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

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


15
ฉันไม่เห็นด้วยกับคำตอบของคุณ การกดปุ่มฐานข้อมูลไม่ทำให้สถานะเป็นอะไร สถานะการจัดเก็บในแบ็กเอนด์ของคุณทำ JWT ไม่ได้ถูกสร้างขึ้นเพื่อให้คุณไม่ต้องกดฐานข้อมูลในแต่ละคำขอแอปพลิเคชันหลัก ๆ ที่ใช้ JWT ได้รับการสำรองข้อมูลจากฐานข้อมูล JWT แก้ปัญหาที่แตกต่างอย่างสิ้นเชิง en.wikipedia.org/wiki/Stateless_protocol
— Julian

6
@ จูเลียนคุณสามารถอธิบายรายละเอียดเกี่ยวกับเรื่องนี้ได้ไหม? ปัญหาใดที่ JWT แก้ไขได้จริง
— zero01alpha

8
@ zero01alpha การรับรองความถูกต้อง: นี่เป็นสถานการณ์จำลองที่พบบ่อยที่สุดสำหรับการใช้ JWT เมื่อผู้ใช้ลงชื่อเข้าใช้แล้วคำขอแต่ละรายการในภายหลังจะรวม JWT เพื่อให้ผู้ใช้สามารถเข้าถึงเส้นทางบริการและทรัพยากรที่ได้รับอนุญาตด้วยโทเค็นนั้น การแลกเปลี่ยนข้อมูล: JSON Web Tokens เป็นวิธีที่ดีในการส่งข้อมูลอย่างปลอดภัยระหว่างฝ่ายต่างๆ เนื่องจากสามารถลงชื่อ JWT ได้คุณจึงมั่นใจได้ว่าผู้ส่งเป็นใคร ดูjwt.io/introduction
— Julian

7
@Julian ฉันไม่เห็นด้วยกับความขัดแย้งของคุณ :) JWT แก้ปัญหา (สำหรับการบริการ) เพื่อให้มีความจำเป็นในการเข้าถึงองค์กรส่วนกลางที่ให้ข้อมูลการอนุญาตสำหรับลูกค้าที่ได้รับ ดังนั้นแทนที่จะให้บริการ A และบริการ B ต้องเข้าถึงทรัพยากรบางอย่างเพื่อตรวจสอบว่าไคลเอนต์ X มีหรือไม่มีสิทธิ์ในการทำอะไรบริการ A และ B จะได้รับโทเค็นจาก X ที่พิสูจน์สิทธิ์ของเขา / เธอ ปาร์ตี้) อย่างไรก็ตาม JWT เป็นเครื่องมือที่ช่วยหลีกเลี่ยงสถานะแชร์ระหว่างบริการในระบบโดยเฉพาะอย่างยิ่งเมื่อพวกเขาถูกควบคุมโดยผู้ให้บริการมากกว่าหนึ่งราย
— LIvanov

1
นอกจากนี้จากjwt.io/introduction If the JWT contains the necessary data, the need to query the database for certain operations may be reduced, though this may not always be the case.
— Giantas

43

ฉันจะเก็บบันทึกหมายเลขรุ่น jwt ในรุ่นผู้ใช้ โทเค็น jwt ใหม่จะตั้งค่าเวอร์ชันเป็นสิ่งนี้

เมื่อคุณตรวจสอบ jwt เพียงตรวจสอบว่ามีหมายเลขรุ่นเท่ากับรุ่น jwt ปัจจุบันของผู้ใช้

เมื่อใดก็ตามที่คุณต้องการทำให้ jwts เก่าใช้ไม่ได้ให้กดหมายเลขผู้ใช้ jwt


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

13
สันนิษฐานว่าคุณกำลังจัดเก็บรหัสผู้ใช้ในโทเค็นของคุณแล้วทำการสอบถามฐานข้อมูลเพื่อตรวจสอบว่ามีผู้ใช้อยู่ / ได้รับอนุญาตให้เข้าถึงจุดปลาย api ดังนั้นคุณไม่ได้ทำการสืบค้นฐานข้อมูลเพิ่มเติมใด ๆ โดยเปรียบเทียบหมายเลขรุ่น jwt token กับหนึ่งในผู้ใช้
— DaftMonk

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

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

10
ฉันเห็นด้วยกับ @SergioCorrea สิ่งนี้จะทำให้ JWT เกือบเป็นรัฐเหมือนกับกลไกการพิสูจน์ตัวตนโทเค็นอื่น ๆ
— Ed J

40

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

เป้าหมาย:

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

การแก้ไขปัญหา:

  • ใช้อายุสั้น (<5m) การเข้าถึงโทเค็นจับคู่กับอาศัยอยู่อีกต่อไป (ไม่กี่ชั่วโมง) ลูกค้าเก็บไว้รีเฟรชโทเค็น
  • ทุกคำขอจะตรวจสอบวันหมดอายุของโทเค็นการรับรองความถูกต้องหรือการรีเฟรชเพื่อความถูกต้อง
  • เมื่อโทเค็นการเข้าถึงหมดอายุไคลเอนต์ใช้โทเค็นการรีเฟรชเพื่อฟื้นฟูโทเค็นการเข้าถึง
  • ระหว่างการตรวจสอบโทเค็นการรีเฟรชเซิร์ฟเวอร์จะตรวจสอบบัญชีดำขนาดเล็กของรหัสผู้ใช้ - หากพบว่าปฏิเสธคำขอให้รีเฟรช
  • เมื่อลูกค้าไม่มีการรีเฟรชที่ถูกต้อง (ไม่หมดอายุ) หรือโทเค็นการตรวจสอบความถูกต้องผู้ใช้จะต้องกลับเข้าสู่ระบบเนื่องจากคำขออื่น ๆ ทั้งหมดจะถูกปฏิเสธ
  • ตามคำขอเข้าสู่ระบบให้ตรวจสอบที่เก็บข้อมูลผู้ใช้เพื่อห้าม
  • เมื่อล็อกเอาต์ - เพิ่มผู้ใช้นั้นในบัญชีดำเซสชันเพื่อให้พวกเขาต้องลงชื่อเข้าใช้อีกครั้งคุณจะต้องเก็บข้อมูลเพิ่มเติมเพื่อไม่ให้ออกจากระบบของอุปกรณ์ทั้งหมดในสภาพแวดล้อมที่มีหลายอุปกรณ์ แต่สามารถทำได้โดยการเพิ่มฟิลด์อุปกรณ์ลงใน บัญชีดำของผู้ใช้
  • หากต้องการบังคับให้เข้าสู่ระบบอีกครั้งหลังจากระยะเวลา x - รักษาวันที่เข้าสู่ระบบครั้งล่าสุดในโทเค็นรับรองความถูกต้องและตรวจสอบตามคำขอ
  • หากต้องการบังคับให้ออกจากระบบผู้ใช้ทั้งหมด - รีเซ็ตคีย์แฮ็กโทเค็น

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

จุดด้อย:

  • ยังคงต้องทำการค้นหาแหล่งข้อมูลในคำขอโทเค็นการรีเฟรช
  • โทเค็นไม่ถูกต้องอาจทำงานต่อไปเพื่อเข้าถึงโทเค็น TTL

ข้อดี:

  • มีฟังก์ชั่นที่ต้องการ
  • การดำเนินการโทเค็นรีเฟรชถูกซ่อนไว้จากผู้ใช้ภายใต้การทำงานปกติ
  • จำเป็นต้องทำการค้นหาแหล่งข้อมูลในคำขอรีเฟรชแทนทุกคำขอ คือ 1 ทุก ๆ 15 นาทีแทน 1 ต่อวินาที
  • ลดสถานะฝั่งเซิร์ฟเวอร์ให้เหลือน้อยที่สุด

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


สิ่งที่เกี่ยวกับสถานการณ์ที่คนหนึ่งลุกขึ้นจากคอมพิวเตอร์เพื่อให้บุคคลอื่นใช้คอมพิวเตอร์เครื่องเดียวกัน บุคคลที่ 1 จะออกจากระบบและคาดว่าการออกจากระบบจะบล็อกบุคคลที่ 2 ทันที หากบุคคลที่ 2 เป็นผู้ใช้โดยเฉลี่ยไคลเอ็นต์สามารถบล็อกผู้ใช้ได้อย่างง่ายดายโดยการลบโทเค็น แต่ถ้าผู้ใช้คนที่สองมีทักษะการแฮ็คผู้ใช้มีเวลาในการกู้คืนโทเค็นที่ยังใช้การได้อยู่ ดูเหมือนว่าไม่มีวิธีหลีกเลี่ยงความจำเป็นในการทำให้โทเค็นเป็นโมฆะทันทีโดยไม่ชักช้า
— Joe Lapp

5
หรือคุณสามารถลบ JWT ของคุณออกจาก sesion / local storage หรือ cookie
— Kamil Kiełczewski

1
ขอบคุณ @Ashtonian หลังจากทำวิจัยอย่างกว้างขวางฉันละทิ้ง JWT หากคุณไม่ได้ใช้ความยาวพิเศษเพื่อรักษาความปลอดภัยของรหัสลับหรือหากคุณไม่ได้มอบหมายให้ใช้งาน OAuth อย่างปลอดภัย JWT จะมีความเสี่ยงมากกว่าเซสชันปกติ ดูรายงานฉบับเต็มของฉัน: by.jtl.xyz/2016/06/the-unspoken-vulnerability-of-jwts.html
— Joe Lapp

2
การใช้โทเค็นการรีเฟรชเป็นกุญแจสำคัญในการอนุญาตให้ขึ้นบัญชีดำ คำอธิบายที่ดี: auth0.com/blog/…
— Rohmer

1
นี่เป็นคำตอบที่ดีที่สุดสำหรับฉันเพราะมันรวมโทเค็นการเข้าถึงระยะสั้นเข้ากับโทเค็นการรีเฟรชที่มีอายุยาวซึ่งสามารถขึ้นบัญชีดำได้ เมื่อออกจากระบบไคลเอ็นต์ควรลบโทเค็นการเข้าถึงเพื่อให้ผู้ใช้รายที่สองไม่สามารถเข้าถึงได้ (แม้ว่าโทเค็นการเข้าถึงจะยังคงใช้งานได้อีกสองสามนาทีหลังจากออกจากระบบ) @Joe Lapp กล่าวว่าแฮ็กเกอร์ (ผู้ใช้คนที่ 2) เข้าถึงโทเค็นการเข้าถึงแม้ว่าจะถูกลบแล้วก็ตาม อย่างไร?
— M3RS

14

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

ข้อเสียที่สำคัญที่ฉันเห็นนี้คือมันจะออกจากเซสชันทั้งหมดหากมีหลายเบราว์เซอร์หรือมีไคลเอนต์มือถือด้วย

นี่อาจเป็นกลไกที่ดีสำหรับการยกเลิก JWT ทั้งหมดในระบบ เป็นส่วนหนึ่งของการตรวจสอบอาจจะเป็นกับการประทับเวลาโลกของผู้ที่ถูกต้องที่ผ่านมาiatเวลา


1
เป็นความคิดที่ดี! ในการแก้ปัญหา "อุปกรณ์หนึ่งเครื่อง" คือการทำให้คุณลักษณะนี้เป็นเหตุการณ์ฉุกเฉินแทนที่จะออกจากระบบ เก็บวันที่ลงในระเบียนผู้ใช้ที่ทำให้โทเค็นทั้งหมดที่ใช้ก่อนหน้านั้นไม่ถูกต้อง สิ่งที่ชอบtoken_valid_afterหรือบางสิ่งบางอย่าง ! น่ากลัว
— OneHoopyFrood

1
เฮ้ @OneHoopyFrood คุณมีรหัสตัวอย่างเพื่อช่วยให้ฉันเข้าใจความคิดในทางที่ดีขึ้นหรือไม่ ผมขอขอบคุณความช่วยเหลือของคุณ!
— alexventuraio

2
เช่นเดียวกับโซลูชันอื่น ๆ ที่เสนอทั้งหมดสิ่งนี้ต้องการการค้นหาฐานข้อมูลซึ่งเป็นสาเหตุของคำถามนี้เพราะการหลีกเลี่ยงการค้นหานั้นเป็นสิ่งที่สำคัญที่สุดที่นี่! ประสิทธิภาพ, ความสามารถในการปรับขยาย ภายใต้สถานการณ์ปกติคุณไม่จำเป็นต้องมีการค้นหาฐานข้อมูลเพื่อให้มีข้อมูลผู้ใช้คุณได้รับจากลูกค้าแล้ว
— Rob Evans

9

ฉันมาสายนิดหน่อย แต่ฉันคิดว่าฉันมีทางออกที่ดี

ฉันมีคอลัมน์ "last_password_change" ในฐานข้อมูลของฉันที่เก็บวันที่และเวลาที่เปลี่ยนรหัสผ่านครั้งล่าสุด ฉันยังเก็บวันที่ / เวลาที่มีปัญหาใน JWT เมื่อตรวจสอบโทเค็นฉันจะตรวจสอบว่ารหัสผ่านมีการเปลี่ยนแปลงหลังจากที่ออกโทเค็นและหากเป็นโทเค็นจะถูกปฏิเสธแม้ว่ามันจะยังไม่หมดอายุก็ตาม


1
คุณจะปฏิเสธโทเค็นได้อย่างไร คุณสามารถแสดงตัวอย่างรหัสย่อ ๆ ได้หรือไม่
— alexventuraio

1
if (jwt.issue_date < user.last_pw_change) { /* not valid, redirect to login */}
— Vanuan

15
ต้องการการค้นหา db!
— Rob Evans

5

คุณสามารถมีฟิลด์ "last_key_used" ใน DB ของคุณในเอกสาร / บันทึกของผู้ใช้

เมื่อผู้ใช้ล็อกอินด้วยผู้ใช้และส่งให้สร้างสตริงสุ่มใหม่เก็บไว้ในฟิลด์ last_key_used และเพิ่มลงในเพย์โหลดเมื่อลงนามโทเค็น

เมื่อผู้ใช้ล็อกอินโดยใช้โทเค็นให้ตรวจสอบ last_key_used ในฐานข้อมูลเพื่อให้ตรงกับโทเค็นในโทเค็น

จากนั้นเมื่อผู้ใช้ออกจากระบบเช่นหรือหากคุณต้องการทำให้โทเค็นเป็นโมฆะเพียงแค่เปลี่ยนฟิลด์ "last_key_used" เป็นค่าสุ่มอื่นและการตรวจสอบใด ๆ ที่ตามมาจะล้มเหลวดังนั้นบังคับให้ผู้ใช้เข้าสู่ระบบด้วยผู้ใช้และผ่านอีกครั้ง


นี่เป็นวิธีแก้ปัญหาที่ฉันได้รับการพิจารณา แต่มีข้อเสียเหล่านี้: (1) คุณกำลังทำการค้นหา DB ในแต่ละคำขอเพื่อตรวจสอบแบบสุ่ม (ลบล้างสาเหตุของการใช้โทเค็นแทนเซสชัน) หรือคุณ ตรวจสอบเป็นระยะ ๆ หลังจากที่โทเค็นการรีเฟรชหมดอายุ (ป้องกันผู้ใช้ออกจากระบบทันทีหรือเซสชันถูกยกเลิกทันที); และ (2) ออกจากระบบบันทึกผู้ใช้ออกจากเบราว์เซอร์ทั้งหมดและอุปกรณ์ทั้งหมด (ซึ่งไม่ได้เป็นพฤติกรรมตามที่คาดหวัง)
— Joe Lapp

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

3

เก็บรายการในหน่วยความจำเช่นนี้

user_id   revoke_tokens_issued_before
-------------------------------------
123       2018-07-02T15:55:33
567       2018-07-01T12:34:21

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


2
ไซต์ที่ใช้งานจริงส่วนใหญ่ทำงานบนเซิร์ฟเวอร์มากกว่าหนึ่งเครื่องดังนั้นโซลูชันนี้จะไม่ทำงาน การเพิ่ม Redis หรือแคช interpocess ที่คล้ายกันทำให้ระบบซับซ้อนและมักจะนำปัญหามากกว่าวิธีแก้ไข
— user2555515

@ user2555515 เซิร์ฟเวอร์ทั้งหมดสามารถซิงโครไนซ์กับฐานข้อมูล มันเป็นทางเลือกของคุณที่กดปุ่มฐานข้อมูลทุกครั้งหรือไม่ คุณสามารถบอกได้ว่ามีปัญหาอะไรเกิดขึ้น
— Eduardo

3

------------------------ คำตอบนี้ช้าไปหน่อย แต่อาจเป็นประโยชน์กับใครสักคน ------------- -----------

จากฝั่งไคลเอ็นต์วิธีที่ง่ายที่สุดคือการลบโทเค็นออกจากที่จัดเก็บเบราว์เซอร์

แต่จะเกิดอะไรขึ้นถ้าคุณต้องการทำลายโทเค็นบนเซิร์ฟเวอร์โหนด -

ปัญหาของแพ็คเกจ JWT คือมันไม่มีวิธีการหรือวิธีการทำลายโทเค็น คุณอาจใช้วิธีการต่าง ๆ ที่เกี่ยวข้องกับ JWT ซึ่งกล่าวถึงข้างต้น แต่ที่นี่ฉันไปกับ jwt-redis

ดังนั้นเพื่อทำลายโทเค็นบนเซิร์ฟเวอร์คุณอาจใช้แพ็คเกจ jwt-redis แทน JWT

ไลบรารีนี้ (jwt-redis) ทำซ้ำฟังก์ชันการทำงานทั้งหมดของไลบรารี jsonwebtoken ซ้ำอย่างสมบูรณ์ด้วยการเพิ่มที่สำคัญอย่างหนึ่ง Jwt-redis ช่วยให้คุณสามารถเก็บป้ายโทเค็นใน Redis เพื่อตรวจสอบความถูกต้อง การไม่มีโทเค็นฉลากเป็นสีแดงทำให้โทเค็นไม่ถูกต้อง เพื่อทำลายโทเค็นใน jwt-redis มีวิธีทำลาย

มันทำงานในลักษณะนี้:

1) ติดตั้ง jwt-redis จาก npm

2) การสร้าง -

var redis = require('redis');
var JWTR =  require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new JWTR(redisClient);

jwtr.sign(payload, secret)
    .then((token)=>{
            // your code
    })
    .catch((error)=>{
            // error handling
    });

3) เพื่อตรวจสอบ -

jwtr.verify(token, secret);

4) การทำลาย -

jwtr.destroy(token)

หมายเหตุ : คุณสามารถระบุ expiresIn ในระหว่างการลงชื่อเข้าใช้โทเค็นเช่นเดียวกับที่มีให้ใน JWT

อาจจะเป็นสิ่งนี้จะช่วยให้ใครบางคน


2

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


ใช่สิ่งนี้ อาจสร้างความสัมพันธ์แบบหนึ่งต่อหลายคนระหว่างตารางผู้ใช้กับตาราง (เซสชัน) ใหม่ดังนั้นคุณสามารถจัดเก็บข้อมูลเมตาพร้อมกับการเรียกร้อง jti
— Peter Lada

2
  1. ให้เวลาหมดอายุ 1 วันสำหรับโทเค็น
  2. รักษาบัญชีดำทุกวัน
  3. ใส่โทเค็นที่ไม่ถูกต้อง / ออกจากระบบให้เป็นรายการที่ไม่อนุญาต

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

สำหรับความต้องการของเซสชันที่ยาวนานควรมีกลไกสำหรับการขยายเวลาหมดอายุของโทเค็น


4
ใส่โทเค็นลงในบัญชีดำและไร้รัฐของคุณ
— Kerem Baydoğan

2

ไปงานปาร์ตี้สายสองเซ็นต์ของฉันได้รับด้านล่างหลังจากการวิจัยบางอย่าง ระหว่างออกจากระบบตรวจสอบให้แน่ใจว่ามีสิ่งต่าง ๆ ต่อไปนี้เกิดขึ้น ...

ล้างการจัดเก็บข้อมูลลูกค้า / เซสชั่น

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

นี่เป็นวิธีที่ง่ายกว่าการเก็บตารางบัญชีดำและกวาดล้างอย่างสม่ำเสมอ การสนับสนุนอุปกรณ์หลายรายการต้องใช้ตารางเพิ่มเติมเพื่อเก็บข้อมูลล็อกอินวันที่ล็อกเอาต์พร้อมรายละเอียดเพิ่มเติมบางอย่างเช่นรายละเอียด OS- หรือไคลเอนต์


2

ที่ไม่ซ้ำกันต่อสตริงผู้ใช้และสตริงทั่วโลกแฮชเข้าด้วยกัน

เพื่อใช้เป็นส่วนลับ JWT อนุญาตให้การทำให้โทเค็นของบุคคลและโกลบอลเป็นโมฆะ ความยืดหยุ่นสูงสุดที่ค่าใช้จ่ายของการค้นหา / อ่าน db ระหว่างการตรวจสอบคำขอ นอกจากนี้ยังง่ายต่อการแคชเช่นกันเนื่องจากพวกเขาไม่ค่อยมีการเปลี่ยนแปลง

นี่คือตัวอย่าง:

HEADER:ALGORITHM & TOKEN TYPE

{
  "alg": "HS256",
  "typ": "JWT"
}
PAYLOAD:DATA

{
  "sub": "1234567890",
  "some": "data",
  "iat": 1516239022
}
VERIFY SIGNATURE

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload), 
  HMACSHA256('perUserString'+'globalString')
)

where HMACSHA256 is your local crypto sha256
  nodejs 
    import sha256 from 'crypto-js/sha256';
    sha256(message);

ตัวอย่างการใช้งานดูhttps://jwt.io (ไม่แน่ใจว่าพวกเขาจัดการกับความลับแบบไดนามิก 256 บิต)


1
รายละเอียดบางอย่างน่าจะพอเพียง
— Giantas

2
@giantas ฉันคิดว่าเครื่องหมายหมายถึงอะไรคือส่วนที่เป็นลายเซ็น ดังนั้นแทนที่จะใช้คีย์เดียวเท่านั้นในการลงชื่อ JWT ให้รวมคีย์ที่ไม่ซ้ำกันสำหรับลูกค้าแต่ละราย ดังนั้นหากคุณต้องการทำให้เซสชั่นทั้งหมดของผู้ใช้เป็นโมฆะเพียงแค่เปลี่ยนคีย์สำหรับผู้ใช้นั้นและถ้าคุณจะทำให้เซสชั่นทั้งหมดในระบบของคุณเป็นโมฆะเพียงแค่เปลี่ยนคีย์เดียวแบบโกลบอล
— Tommy Aria Pradana

1

ฉันทำตามวิธีต่อไปนี้:

  1. สร้างunique hashแล้วเก็บไว้ในRedisของคุณและJWT สิ่งนี้สามารถเรียกว่าเซสชั่น
    • นอกจากนี้เรายังจะจัดเก็บจำนวนคำขอที่JWTทำโดยเฉพาะ- ทุกครั้งที่ส่ง jwt ไปยังเซิร์ฟเวอร์เราจะเพิ่มจำนวนเต็มของคำขอ (นี่เป็นตัวเลือก)

ดังนั้นเมื่อผู้ใช้บันทึกในกัญชาที่ไม่ซ้ำกันจะถูกสร้างขึ้นเก็บไว้ใน Redis และฉีดเข้าไปในของคุณJWT

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

เราสามารถขยายจากสิ่งนี้และทำให้JWTของเราปลอดภัยยิ่งขึ้นด้วยวิธีการดังต่อไปนี้:

Xทุกคำขอที่JWTทำขึ้นมาเราสร้างเซสชันใหม่ที่ไม่ซ้ำกันเก็บไว้ในJWTของเราแล้วขึ้นบัญชีดำที่แล้ว

ซึ่งหมายความว่าJWTมีการเปลี่ยนแปลงอยู่ตลอดเวลาและหยุดยั้งการที่JWTถูกแฮ็กถูกขโมยหรืออย่างอื่น


1
คุณสามารถแฮ็กโทเค็นเองและเก็บค่านั้นเป็นสีแดงแทนที่จะใส่แฮชใหม่ลงในโทเค็น
— Frug

ตรวจสอบaudและjtiอ้างสิทธิ์ใน JWT คุณอยู่ในเส้นทางที่ถูกต้อง
— Peter Lada

1

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

ฉันยังไม่ได้ลอง แต่ฉันขอแนะนำวิธีการต่อไปนี้เพื่ออนุญาตการเพิกถอนโทเค็นในขณะที่รักษาความนิยม DB ให้น้อยที่สุด -

หากต้องการลดอัตราการตรวจสอบฐานข้อมูลให้แบ่งโทเค็น JWT ที่ออกให้ทั้งหมดออกเป็นกลุ่ม X ตามการเชื่อมโยงที่กำหนดขึ้นบางอย่าง (เช่น 10 กลุ่มด้วยตัวเลขแรกของรหัสผู้ใช้)

โทเค็น JWT แต่ละอันจะเก็บ id กลุ่มและเวลาประทับที่สร้างขึ้นจากการสร้างโทเค็น เช่น,{ "group_id": 1, "timestamp": 1551861473716 }

เซิร์ฟเวอร์จะเก็บรหัสกลุ่มทั้งหมดในหน่วยความจำและแต่ละกลุ่มจะมีการประทับเวลาที่ระบุว่าเมื่อใดคือเหตุการณ์การออกจากระบบครั้งสุดท้ายของผู้ใช้ที่อยู่ในกลุ่มนั้น เช่น,{ "group1": 1551861473714, "group2": 1551861487293, ... }

คำร้องขอที่มีโทเค็น JWT ที่มีการประทับเวลากลุ่มแบบเก่าจะถูกตรวจสอบความถูกต้อง (การเข้าใช้ฐานข้อมูล) และหากถูกต้องโทเค็น JWT ใหม่ที่มีการประทับเวลาใหม่จะออกให้สำหรับการใช้ในอนาคตของลูกค้า หากการประทับเวลากลุ่มของโทเค็นใหม่กว่าเราเชื่อมั่นใน JWT (ไม่มีการโจมตีฐานข้อมูล)

ดังนั้น -

  1. เราตรวจสอบโทเค็น JWT โดยใช้ฐานข้อมูลเท่านั้นหากโทเค็นมีการประทับเวลากลุ่มแบบเก่าในขณะที่คำขอในอนาคตจะไม่ได้รับการตรวจสอบจนกว่าจะมีใครบางคนในกลุ่มของผู้ใช้จะออกจากระบบ
  2. เราใช้กลุ่มเพื่อ จำกัด จำนวนการเปลี่ยนแปลงเวลา (บอกว่ามีผู้ใช้เข้าและออกเหมือนไม่มีวันพรุ่งนี้ - จะมีผลเฉพาะกับจำนวนผู้ใช้ที่ จำกัด แทนที่จะเป็นทุกคน)
  3. เรา จำกัด จำนวนกลุ่มเพื่อ จำกัด จำนวนการประทับเวลาที่เก็บไว้ในหน่วยความจำ
  4. การทำให้โทเค็นเป็นโมฆะง่ายเพียงลบออกจากตารางเซสชันและสร้างการประทับเวลาใหม่สำหรับกลุ่มผู้ใช้

รายการเดียวกันสามารถเก็บไว้ในหน่วยความจำ (แอปพลิเคชันสำหรับ c #) และมันจะช่วยลดความจำเป็นในการกดปุ่ม db สำหรับแต่ละคำขอ รายการสามารถโหลดได้จาก db เมื่อเริ่มต้นแอปพลิเคชัน
— dvdmn

1

หากตัวเลือก "ออกจากระบบจากอุปกรณ์ทั้งหมด" เป็นที่ยอมรับ (ในกรณีส่วนใหญ่จะเป็น):

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

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


0

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

  1. LastValidTime (ค่าเริ่มต้น: เวลาสร้าง)
  2. ลงชื่อเข้าใช้ (ค่าเริ่มต้น: จริง)

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

เมื่อเราสร้าง JWT เราจะมีเวลาสร้าง JWT ในส่วนของข้อมูล เมื่อเราอนุญาตบริการเราจะตรวจสอบ 3 เงื่อนไข

  1. JWT นั้นถูกต้อง
  2. JWT คือเวลาสร้างเพย์โหลดมากกว่า User LastValidTime หรือไม่
  3. เป็นผู้ใช้เข้าสู่ระบบ

ให้ดูสถานการณ์จริง

User X มีอุปกรณ์สองตัว A, B เขาล็อกอินเข้าสู่เซิร์ฟเวอร์ของเราเวลา 19.00 น. โดยใช้อุปกรณ์ A และอุปกรณ์ B (สมมติว่า JWT หมดอายุเวลา 12 ชั่วโมง) A และ B ทั้งคู่มี JWT กับ createdTime: 19:00

เวลา 21.00 น. เขาทำอุปกรณ์ของเขาหาย B. เขาออกจากระบบทันทีในขณะที่รายการผู้ใช้ X ฐานข้อมูลของเรามี LastValidTime เป็น "ThatDate: 9: 00: xx: xxx" และลงชื่อเข้าใช้ด้วย "เท็จ"

เวลา 9.30 น. Mr.Thief พยายามเข้าสู่ระบบโดยใช้อุปกรณ์ B เราจะตรวจสอบฐานข้อมูลแม้กระทั่ง Logged-In นั้นเป็นเท็จดังนั้นเราจึงไม่อนุญาต

เวลา 22.00 น. Mr.X ลงชื่อเข้าใช้จากอุปกรณ์ของเขา A. ตอนนี้อุปกรณ์ A มี JWT พร้อมเวลาที่สร้าง: 22.00 น. ขณะนี้ฐานข้อมูลที่ล็อกอินถูกตั้งค่าเป็น "true"

เวลา 22:30 น. Mr.Thief พยายามลงชื่อเข้าใช้แม้ว่าการล็อกอินจะเป็นจริง LastValidTime คือ 21:00 น. ในฐานข้อมูล แต่ JWT ของ B ได้สร้างเวลาเป็น 19.00 น. ดังนั้นเขาจะไม่ได้รับอนุญาตให้เข้าถึงบริการ ดังนั้นการใช้อุปกรณ์ B โดยไม่มีรหัสผ่านเขาจึงไม่สามารถใช้ JWT ที่สร้างขึ้นแล้วหลังจากที่มีการออกจากระบบหนึ่งอุปกรณ์


0

โซลูชัน IAM เช่น Keycloak (ซึ่งฉันใช้งานได้) มอบจุดสิ้นสุดการเพิกถอนโทเค็นเช่น

จุดสิ้นสุดการเพิกถอนโทเค็น /realms/{realm-name}/protocol/openid-connect/revoke

หากคุณต้องการออกจากระบบผู้ใช้ (หรือผู้ใช้) คุณสามารถโทรหาจุดปลายได้เช่นกัน (สิ่งนี้จะทำให้โทเคนใช้ไม่ได้) อีกครั้งในกรณีของ Keycloak Relying Party ก็แค่เรียกจุดปลายทาง

/realms/{realm-name}/protocol/openid-connect/logout

เชื่อมโยงในกรณีที่คุณต้องการเรียนรู้เพิ่มเติม


-1

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

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

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

มันฟังดูซับซ้อนหรือไม่? มันทำให้ฉัน!

คำเตือน: ฉันไม่ได้ใช้ Redis


-1

หากคุณใช้ axios หรือการร้องขอ HTTP ตามสัญญาที่คล้ายกันคุณสามารถทำลายโทเค็นที่ส่วนหน้าภายใน.then()ส่วนได้ มันจะเปิดตัวในการตอบสนองแล้ว. () ส่วนหลังจากที่ผู้ใช้ดำเนินการฟังก์ชั่นนี้ (รหัสผลลัพธ์จากจุดสิ้นสุดของเซิร์ฟเวอร์จะต้องตกลง 200) หลังจากที่ผู้ใช้คลิกที่เส้นทางนี้ในขณะที่ค้นหาข้อมูลถ้าเขตข้อมูลฐานข้อมูลuser_enabledเป็นเท็จมันจะทริกเกอร์ทำลายโทเค็นและผู้ใช้จะถูกออกจากระบบทันทีและหยุดการเข้าถึงเส้นทาง / เพจที่ได้รับการป้องกัน เราไม่ต้องรอให้โทเค็นหมดอายุในขณะที่ผู้ใช้เข้าสู่ระบบอย่างถาวร

function searchForData() {   // front-end js function, user searches for the data
    // protected route, token that is sent along http request for verification
    var validToken = 'Bearer ' + whereYouStoredToken; // token stored in the browser 

    // route will trigger destroying token when user clicks and executes this func
    axios.post('/my-data', {headers: {'Authorization': validToken}})
     .then((response) => {
   // If Admin set user_enabled in the db as false, we destroy token in the browser localStorage
       if (response.data.user_enabled === false) {  // user_enabled is field in the db
           window.localStorage.clear();  // we destroy token and other credentials
       }  
    });
     .catch((e) => {
       console.log(e);
    });
}

-3

ฉันเพิ่งบันทึกโทเค็นลงในตารางผู้ใช้เมื่อผู้ใช้ลงชื่อเข้าใช้ฉันจะอัปเดตโทเค็นใหม่และเมื่อรับรองความถูกต้องเท่ากับ jwt ปัจจุบันของผู้ใช้

ฉันคิดว่านี่ไม่ใช่ทางออกที่ดีที่สุด แต่ใช้งานได้สำหรับฉัน


2
แน่นอนมันไม่ได้ดีที่สุด! ใครก็ตามที่มีสิทธิ์เข้าถึงฐานข้อมูลสามารถปลอมตัวเป็นผู้ใช้คนใดก็ได้
— user2555515

1
@ user2555515 วิธีนี้ใช้ได้ผลดีถ้าโทเค็นที่เก็บไว้ในฐานข้อมูลนั้นได้รับการเข้ารหัสเช่นเดียวกับรหัสผ่านใด ๆ ที่เก็บไว้ในฐานข้อมูลควรเป็น มีความแตกต่างระหว่างStateless JWTและStateful JWT(ซึ่งคล้ายกับเซสชัน) Stateful JWTสามารถได้รับประโยชน์จากการบำรุงรักษารายการที่อนุญาตโทเค็น
— TheDarkIn1978
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.