การทำให้โทเค็น 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.