[javascript] JSON 웹 토큰 무효화

내가 작업하고있는 새로운 node.js 프로젝트의 경우 쿠키 기반 세션 접근 방식에서 전환하는 것에 대해 생각하고 있습니다. JSON 웹 토큰 (jwt)을 사용하여 토큰 기반 세션 접근 방식 (키-값 저장소 없음)

이 프로젝트는 socket.io를 사용하는 게임입니다. 토큰 기반 세션을 갖는 것은 단일 세션 (web 및 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});
    });
}

토큰 기반 로그인 :

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) 단순히 클라이언트에서 토큰을 제거하십시오

분명히 이것은 서버 측 보안에는 아무런 영향을 미치지 않지만 토큰이 존재하지 않도록하여 공격자를 막을 수 있습니다 (즉, 로그 아웃하기 전에 토큰을 도난 했어야 함).

2) 토큰 블랙리스트 생성

초기 만료 날짜까지 유효하지 않은 토큰을 저장하고 들어오는 요청과 비교할 수 있습니다. 모든 요청에 ​​대해 데이터베이스를 터치해야하기 때문에 처음부터 완전히 토큰을 사용해야하는 이유를 무효화하는 것처럼 보입니다. 로그 아웃과 만료 시간 사이에있는 토큰 만 저장하면되기 때문에 스토리지 크기는 더 작을 수 있습니다 (이것은 직감이며 컨텍스트에 따라 달라집니다).

3) 토큰 만기 시간을 짧게 유지하고 자주 회전하십시오.

토큰 만료 시간을 충분히 짧은 간격으로 유지하고 실행중인 클라이언트가 추적을 유지하고 필요한 경우 업데이트를 요청하면 번호 1은 완전한 로그 아웃 시스템으로 효과적으로 작동합니다. 이 방법의 문제점은 클라이언트 코드가 닫히는 사이 (만료 간격을 설정하는 시간에 따라) 사용자가 로그인 상태를 유지할 수 없다는 것입니다.

비상시 계획

응급 상황이 발생했거나 사용자 토큰이 손상된 경우 사용자가 로그인 자격 증명을 사용하여 기본 사용자 조회 ID를 변경할 수 있습니다. 연관된 사용자를 더 이상 찾을 수 없으므로 모든 연관된 토큰이 유효하지 않게됩니다.

또한 마지막 로그인 날짜를 토큰과 함께 포함시키는 것이 좋습니다. 따라서 먼 시간이 지난 후에 다시 로그인 할 수 있습니다.

토큰을 사용한 공격과의 유사점 / 차이점에서이 게시물은 다음과 같은 질문을 해결합니다. https://github.com/dentarg/blog/blob/master/_posts/2014-01-07-angularjs-authentication-with-cookies -vs-token.markdown


답변

위에 게시 된 아이디어는 훌륭하지만 기존 JWT를 모두 무효화하는 매우 간단하고 쉬운 방법은 단순히 비밀을 변경하는 것입니다.

서버가 JWT를 작성하고 비밀 (JWS)로 서명 한 후이를 클라이언트로 전송하면, 단순히 비밀을 변경하면 기존 토큰이 모두 무효화되고 모든 사용자가 이전 토큰이 갑자기 유효하지 않아 인증을 위해 새 토큰을 얻도록 요구합니다. 서버에.

실제 토큰 내용 (또는 조회 ID)을 수정하지 않아도됩니다.

분명히 이것은 토큰 만료마다 위의 솔루션 중 하나가 필요한 경우 (예 : 토큰 만료 시간이 짧거나 토큰 내부에 저장된 키 무효화) 기존의 모든 토큰이 만료되기를 원하는 긴급 상황에서만 작동합니다.


답변

이것은 @mattway의 답변을 뒷받침하고 지원하는 긴 의견입니다.

주어진:

이 페이지에서 제안 된 다른 솔루션 중 일부는 모든 요청에 ​​대해 데이터 스토어에 도달하는 것을지지합니다. 모든 인증 요청의 유효성을 검사하기 위해 기본 데이터 저장소에 도달하면 다른 기존 토큰 인증 메커니즘 대신 JWT를 사용해야 할 이유가 줄어 듭니다. 매번 데이터 저장소에 갈 때 상태 비 저장 대신 JWT를 기본적으로 상태 저장으로 설정했습니다.

(사이트에 많은 양의 무단 요청이 수신되는 경우 JWT는 데이터 스토어에 도달하지 않고 요청을 거부합니다. 이는 도움이됩니다. 다른 유스 케이스도있을 수 있습니다.)

주어진:

상태 비 저장 JWT 에는 다음과 같은 중요한 사용 사례에 대한 즉각 적이고 안전한 지원 을 제공 할 수있는 방법이 없기 때문에 일반적인 실제 웹 앱에 대한 상태 비 저장 JWT 인증을 얻을 수 없습니다 .

사용자 계정이 삭제 / 차단 / 일시 중지되었습니다.

사용자 비밀번호가 변경되었습니다.

사용자의 역할 또는 권한이 변경되었습니다.

관리자가 사용자를 로그 아웃했습니다.

JWT 토큰의 다른 응용 프로그램 중요 데이터는 사이트 관리자가 변경합니다.

이 경우 토큰 만료를 기다릴 수 없습니다. 토큰 무효화는 즉시 발생해야합니다. 또한 악의적 인 의도가 있든 없든 클라이언트가 이전 토큰의 사본을 보관 및 사용하지 않도록 신뢰할 수 없습니다.

따라서 @ matt-way # 2 TokenBlackList의 답변이 JWT 기반 인증에 필요한 상태를 추가하는 가장 효율적인 방법이라고 생각합니다.

만료 날짜에 도달 할 때까지이 토큰을 보유하는 블랙리스트가 있습니다. 토큰 목록은 총 사용자 수에 비해 상당히 적습니다. 만료 될 때까지 블랙리스트에 올린 토큰 만 유지하면되기 때문입니다. 무효화 된 토큰을 redis, memcached 또는 키의 만료 시간 설정을 지원하는 다른 메모리 내 데이터 저장소에 넣어 구현합니다.

초기 JWT 인증을 통과하는 모든 인증 요청에 대해 인 메모리 DB를 계속 호출해야하지만 전체 사용자 세트에 대한 키를 저장할 필요는 없습니다. 특정 사이트에 큰 영향을 줄 수도 있고 아닐 수도 있습니다.


답변

사용자 모델에 jwt 버전 번호를 기록합니다. 새로운 jwt 토큰은 버전을 이것으로 설정합니다.

jwt의 유효성을 검사 할 때 사용자의 현재 jwt 버전과 버전 번호가 동일한 지 확인하십시오.

이전 jwt를 무효화하려면 언제든지 사용자 jwt 버전 번호를 충돌 시키십시오.


답변

아직 시도하지 않았으며 다른 답변 중 일부를 기반으로 많은 정보를 사용합니다. 여기서 복잡한 점은 사용자 정보 요청 당 서버 측 데이터 저장소 호출을 피하는 것입니다. 대부분의 다른 솔루션에는 사용자 세션 저장소에 대한 요청 당 db 조회가 필요합니다. 특정 시나리오에서는 문제가되지 않지만 이러한 호출을 피하고 필요한 서버 측 상태를 매우 작게 만들기 위해 만들어졌습니다. 모든 강제 무효화 기능을 제공하기 위해 서버 쪽 세션을 다시 만들지 만 소규모입니다. 그러나 당신이 그것을하고 싶다면 여기 요점이 있습니다 :

목표 :

  • 데이터 저장소 사용을 완화하십시오 (상태 비 저장).
  • 모든 사용자를 강제로 로그 아웃하는 기능
  • 언제든지 개인을 강제로 로그 아웃하는 기능.
  • 일정 시간이 지나면 비밀번호를 다시 입력해야합니다.
  • 여러 클라이언트와 작업 할 수있는 기능
  • 사용자가 특정 클라이언트에서 로그 아웃을 클릭하면 강제로 다시 로그인 할 수 있습니다. (사용자가 자리를 비운 후 누군가가 클라이언트 토큰을 “삭제 취소”하지 못하게하려면 추가 정보에 대한 설명을 참조하십시오)

해결책:

  • 수명이 길고 (몇 시간) 클라이언트에 저장된 새로 고침 토큰과 쌍을 이룬 단명 (5m 미만) 액세스 토큰을 사용하십시오 .
  • 모든 요청은 인증 또는 새로 고침 토큰 만료 날짜를 확인합니다.
  • 액세스 토큰이 만료되면 클라이언트는 새로 고침 토큰을 사용하여 액세스 토큰을 새로 고칩니다.
  • 새로 고침 토큰 확인 중에 서버는 작은 사용자 목록 블랙리스트를 확인합니다 (발견 된 경우 새로 고침 요청 거부).
  • 클라이언트에 유효한 (만료되지 않은) 새로 고침 또는 인증 토큰이없는 경우 다른 모든 요청이 거부되므로 사용자는 다시 로그인해야합니다.
  • 로그인 요청시 사용자 데이터 저장소에서 금지 여부를 확인하십시오.
  • 로그 아웃시-해당 사용자를 세션 블랙리스트에 추가하여 다시 로그인해야합니다. 다중 디바이스 환경에서 모든 디바이스에서 로그 아웃하지 않도록 추가 정보를 저장해야하지만 디바이스 필드를 디바이스에 추가하여 수행 할 수 있습니다. 사용자 블랙리스트.
  • x 시간이 지난 후 다시 입력을하려면 인증 토큰에서 마지막 로그인 날짜를 유지하고 요청 당 확인하십시오.
  • 모든 사용자를 강제로 로그 아웃하려면 토큰 해시 키를 재설정하십시오.

사용자 테이블에 금지 된 사용자 정보가 있다고 가정하면 서버에서 블랙리스트 (상태)를 유지해야합니다. 잘못된 세션 블랙리스트-사용자 ID 목록입니다. 이 블랙리스트는 새로 고침 토큰 요청 중에 만 확인됩니다. 새로 고침 토큰 TTL 인 한 항목을 계속 사용해야합니다. 새로 고침 토큰이 만료되면 사용자는 다시 로그인해야합니다.

단점 :

  • 새로 고침 토큰 요청에서 데이터 저장소 조회를 수행해야합니다.
  • 액세스 토큰의 TTL에 유효하지 않은 토큰이 계속 작동 할 수 있습니다.

장점 :

  • 원하는 기능을 제공합니다.
  • 정상 작동 상태에서는 토큰 새로 고침 작업이 사용자에게 표시되지 않습니다.
  • 모든 요청 대신 새로 고침 요청에 대한 데이터 저장소 조회 만 수행하면됩니다. 즉, 초당 1이 아닌 15 분마다 1이됩니다.
  • 서버 측 상태를 매우 작은 블랙리스트로 최소화합니다.

이 솔루션을 사용하면 서버가 15 분마다 DB 호출 만하 기 때문에 사용자 정보에는 적어도 reddis와 같은 메모리 데이터 저장소가 필요하지 않습니다. reddis를 사용하는 경우 유효 / 무효 세션 목록을 저장하면 매우 빠르고 간단한 솔루션이됩니다. 새로 고침 토큰이 필요 없습니다. 각 인증 토큰에는 세션 ID와 장치 ID가 있으며 생성시 reddis 테이블에 저장 될 수 있고 적절할 때 무효화 될 수 있습니다. 그런 다음 모든 요청에서 확인되고 유효하지 않은 경우 거부됩니다.


답변

내가 고려한 접근법은 항상 iatJWT에 (발행 된) 값을 갖는 것입니다. 그런 다음 사용자가 로그 아웃하면 해당 타임 스탬프를 사용자 레코드에 저장하십시오. JWT를 검증 할 때는 iat마지막으로 로그 아웃 한 타임 스탬프와 비교하십시오 . iat이전 버전 인 경우 유효하지 않습니다. 예, DB로 가야하지만 JWT가 유효하면 항상 사용자 레코드를 가져옵니다.

내가 볼 수있는 주요 단점은 여러 브라우저에 있거나 모바일 클라이언트가있는 경우 모든 세션에서 로그 아웃한다는 것입니다.

이것은 또한 시스템의 모든 JWT를 무효화하는 훌륭한 메커니즘이 될 수 있습니다. 확인의 일부는 마지막 유효 iat시간 의 글로벌 타임 스탬프에 대한 것일 수 있습니다 .


답변

나는 여기 조금 늦었지만 괜찮은 해결책이 있다고 생각합니다.

데이터베이스에 비밀번호가 마지막으로 변경된 날짜와 시간을 저장하는 “last_password_change”열이 있습니다. 또한 JWT에 발행 날짜 / 시간을 저장합니다. 토큰의 유효성을 검사 할 때 토큰이 발급 된 후 비밀번호가 변경되었는지, 아직 만료되지 않았더라도 토큰이 거부되었는지 확인합니다.