[security] JWT를 해독 할 수 있다면 어떻게 안전합니까?

JWT를 얻었고 페이로드를 디코딩 할 수 있다면 어떻게 안전합니까? 헤더에서 토큰을 가져 와서 페이로드의 사용자 정보를 해독하고 변경 한 다음 동일한 올바른 암호화 비밀로 다시 보낼 수는 없습니까?

나는 그들이 안전해야한다는 것을 알고 있지만 기술을 정말로 이해하고 싶습니다. 내가 무엇을 놓치고 있습니까?



답변

JWT는 서명, 암호화 또는 둘 다 가능합니다. 토큰이 서명되었지만 암호화되지 않은 경우 모든 사람이 해당 내용을 읽을 수 있지만 개인 키를 모르면 변경할 수 없습니다. 그렇지 않으면 수신자는 서명이 더 이상 일치하지 않음을 알 수 있습니다.

귀하의 의견에 대한 답변 : 귀하의 의견을 올바르게 이해했는지 확실하지 않습니다. 디지털 서명을 알고 이해하고 있습니까? 한 변형 (HMAC, 대칭이지만 다른 많은 변형)에 대해 간단히 설명하겠습니다.

Alice가 JWT를 Bob에게 보내려고한다고 가정 해 봅시다. 둘 다 공유 비밀을 알고 있습니다. Mallory는 그 비밀을 알지 못하지만 JWT를 방해하고 변경하기를 원합니다. 이를 방지하기 위해 Alice Hash(payload + secret)는이를 서명으로 계산 하고 추가합니다.

메시지를 수신 할 때 Bob은 Hash(payload + secret)서명이 일치하는지 확인하기 위해 계산할 수도 있습니다 . 그러나 Mallory가 콘텐츠에서 무언가를 변경하면 일치하는 서명 ()을 계산할 수 없습니다 Hash(newContent + secret). 그녀는 그 비밀을 알지 못하고 그것을 찾을 방법이 없습니다. 즉, 그녀가 무언가를 변경하면 서명이 더 이상 일치하지 않으며 Bob은 더 이상 JWT를 수락하지 않습니다.

다른 사람에게 메시지를 보내고에 {"id":1}서명 한다고 가정 해 봅시다 Hash(content + secret). (+는 단지 연결입니다). SHA256 Hash 함수를 사용하고 얻은 서명은 다음과 같습니다 330e7b0775561c6e95797d4dd306a150046e239986f0a1373230fda0235bda8c. 이제 당신 차례입니다 : Mallory의 역할을하고 메시지에 서명하십시오 {"id":2}. 내가 사용한 비밀을 모르기 때문에 할 수 없습니다. 수신자가 비밀을 알고 있다고 가정하면 메시지의 서명을 계산하고 올바른지 확인할 수 있습니다.


답변

로 이동 jwt.io하여 토큰을 붙여넣고 내용을 읽을 수 있습니다. 이것은 처음에는 많은 사람들에게 문제가됩니다.

짧은 대답은 JWT가 암호화와 관련이 없다는 것입니다. 유효성 검사가 중요합니다. 즉, “이 토큰의 내용을 조작 했습니까?”라는 답변을 항상 얻을 수 있습니까? 이것은 서버가 토큰을 알고 무시하기 때문에 JWT 토큰의 사용자 조작은 쓸모가 없다는 것을 의미합니다. 서버는 클라이언트에 토큰을 발행 할 때 페이로드를 기반으로 서명을 추가합니다. 나중에 페이로드와 일치하는 서명을 확인합니다.

논리적 인 질문은 암호화 된 내용과 관련이없는 동기는 무엇입니까?

  1. 가장 간단한 이유는 이것이 대부분 해결 된 문제라고 가정하기 때문입니다. 예를 들어 웹 브라우저와 같은 클라이언트를 처리하는 경우 secure(HTTP를 통해 전송되지 않고 HTTPS를 통해서만 전송 됨) 및 httpOnly(Javascript로 읽을 수 없음 ) 쿠키를 통해 JWT 토큰을 저장 하고 서버와 통신 할 수 있습니다 암호화 된 채널 (HTTPS) 서버와 클라이언트 사이에 보안 채널이 있다는 것을 알게되면 JWT 또는 원하는 다른 것을 안전하게 교환 할 수 있습니다.

  2. 이것은 일을 단순하게 유지합니다. 간단한 구현으로 채택이 쉬워 지지만 각 계층에서 가장 잘 수행 할 수 있습니다 (HTTPS에서 암호화 처리).

  3. JWT는 민감한 데이터를 저장하기위한 것이 아닙니다. 서버가 JWT 토큰을 수신하여 유효성을 검증 한 후에는 해당 사용자에 대한 추가 정보 (권한, 우편 주소 등)를 위해 자체 데이터베이스에서 사용자 ID를 자유롭게 검색 할 수 있습니다. 이는 JWT의 크기를 작게 유지하고 모든 사람이 JWT에 민감한 데이터를 보관하지 않기 때문에 우발적 인 정보 유출을 방지합니다.

쿠키 자체의 작동 방식과 크게 다르지 않습니다. 쿠키에는 종종 암호화되지 않은 페이로드가 포함됩니다. HTTPS를 사용하는 경우 모든 것이 좋습니다. 그렇지 않은 경우 민감한 쿠키 자체를 암호화하는 것이 좋습니다. 그렇게하지 않으면 중간자 (man-in-the-middle) 공격이 가능합니다. 프록시 서버 나 ISP가 쿠키를 읽은 다음 나중에 사용자 척에 따라 쿠키를 재생합니다. 비슷한 이유로 JWT는 항상 HTTPS와 같은 보안 계층을 통해 교환되어야합니다.


답변

JWT (Json Web Token)의 내용은 본질적으로 안전하지 않지만 토큰 신뢰성을 확인하기위한 기본 제공 기능이 있습니다. JWT는 마침표로 구분 된 세 개의 해시입니다. 세 번째는 서명입니다. 공개 / 개인 키 시스템에서 발급자는 해당 공개 키로 만 확인할 수있는 개인 키로 토큰 서명에 서명합니다.

발급자와 검증 자의 차이점을 이해하는 것이 중요합니다. 토큰 수령인은이를 확인해야합니다.

웹 애플리케이션에서 JWT를 안전하게 사용하는 데는 두 가지 중요한 단계가 있습니다. 1) 암호화 된 채널을 통해 전송하고 2) 서명을 받으면 즉시 서명을 확인합니다. 공개 키 암호화의 비대칭 특성으로 인해 JWT 서명 확인이 가능합니다. 공개 키는 일치하는 개인 키로 JWT에 서명했는지 확인합니다. 다른 키 조합은이 확인을 수행 할 수 없으므로 가장 시도를 방지합니다. 이 두 단계를 수행하면 JWT의 신뢰성을 수학적으로 확실하게 보장 할 수 있습니다.

추가 정보 : 공개 키는 서명을 어떻게 확인합니까?


답변

처음부터 토론합시다 :

JWT는 Json 웹 토큰으로 확장되는 매우 현대적이고 간단하며 안전한 접근 방식입니다. Json 웹 토큰은 인증을위한 상태 비 저장 솔루션입니다. 따라서 서버에 세션 상태를 저장할 필요가 없습니다. 물론 편안한 API에 적합합니다. 편안한 API는 항상 상태가 없어야하며 JWT 인증에 가장 널리 사용되는 대안은 세션을 사용하여 서버에 사용자의 로그인 상태를 저장하는 것입니다. 그러나 물론 편안한 API는 상태가 없어야한다는 원칙을 따르지 않으므로 JWT와 같은 솔루션이 널리 보급되었습니다.

이제 인증이 실제로 Json 웹 토큰과 어떻게 작동하는지 알려 드리겠습니다. 데이터베이스에 이미 등록 된 사용자가 있다고 가정합니다. 따라서 사용자의 클라이언트는 사용자 이름과 비밀번호로 게시 요청을 시작하여 사용자가 존재하는지 확인하고 비밀번호가 올바른지 확인하면 해당 사용자에 대해서만 고유 한 Json 웹 토큰을 생성합니다.

토큰은 서버에 저장된 비밀 문자열 을 사용하여 생성 됩니다 . 그런 다음 서버는 해당 JWT를 클라이언트로 다시 전송하여 쿠키 또는 로컬 스토리지에 저장합니다.
여기에 이미지 설명을 입력하십시오

이와 같이 사용자는 서버의 상태를 떠나지 않고 인증되고 기본적으로 응용 프로그램에 로그인됩니다.

따라서 서버는 실제로 어떤 사용자가 실제로 로그인했는지 알지 못하지만 물론 사용자는 애플리케이션의 보호 된 부분에 액세스하는 여권과 같은 유효한 Json Web Token을 가지고 있기 때문에 자신이 로그인 한 것을 알고 있습니다.

다시 한 번, 아이디어를 얻었는지 확인하십시오. 사용자는 서버의 어느 곳에도 저장되지 않은 고유 한 유효한 Json Web Token을 되찾자 마자 로그인됩니다. 따라서이 프로세스는 완전히 상태 비 저장입니다.

그런 다음 사용자가 예를 들어 자신의 사용자 프로필 데이터와 같은 보호 된 경로에 액세스하려고 할 때마다. 그는 Json Web Token을 요청과 함께 보내므로 여권을 표시하여 해당 경로에 액세스하는 것과 약간 같습니다.

요청이 서버에 도달하면 앱은 Json 웹 토큰이 실제로 유효한지 확인하고 사용자가 실제로 자신이 말하는 사람인지 확인하면 요청 된 데이터가 클라이언트에 전송되고 그렇지 않은 경우 클라이언트에 전송됩니다. 사용자에게 해당 리소스에 액세스 할 수 없음을 알리는 오류가 발생했습니다.
여기에 이미지 설명을 입력하십시오

이 모든 통신은 https를 통해 이루어져야하므로 누구나 암호화 된 Http를 사용하여 누구나 비밀번호 또는 Json 웹 토큰에 액세스 할 수 없도록하십시오. 그래야만 우리는 정말 안전한 시스템을 갖게됩니다.

여기에 이미지 설명을 입력하십시오

따라서 Json Web Token은 jwt.io의 JWT 디버거에서 가져온이 스크린 샷의 왼쪽 부분처럼 보입니다. 본질적으로 세 부분으로 구성된 인코딩 문자열입니다. 헤더, 페이로드 및 서명 이제 헤더는 토큰 자체에 대한 일부 메타 데이터 일 뿐이며 페이로드는 토큰으로 인코딩 할 수있는 데이터, 실제로 원하는 데이터입니다. 여기에서 더 많은 데이터를 인코딩할수록 JWT가 더 커집니다. 어쨌든,이 두 부분은 암호화되어 있지는 않지만 일반 텍스트 일뿐입니다.

따라서 누구나 해독하고 읽을 수 있으므로 민감한 데이터는 여기에 저장할 수 없습니다. 그러나 세 번째 부분, 즉 서명에서 일이 실제로 흥미로워지기 때문에 전혀 문제가되지 않습니다. 서명은 서버에 저장된 헤더, 페이로드 및 비밀을 사용하여 작성됩니다.

그리고이 전체 프로세스를 Json 웹 토큰 서명 이라고 합니다 . 서명 알고리즘은 헤더, 페이로드 및 비밀을 사용하여 고유 한 서명을 만듭니다. 이 데이터와 비밀 만이이 서명을 만들 수 있습니다. 그런 다음 헤더 및 페이로드와 함께이 서명은 JWT를 형성 한 다음 클라이언트로 전송됩니다.
여기에 이미지 설명을 입력하십시오

서버가 보호 된 라우트에 대한 액세스 권한을 부여하기 위해 JWT를 수신 한 후에는 사용자가 실제로 자신이 누구인지를 판별하기 위해이를 검증해야합니다. 즉, 토큰의 헤더 및 페이로드 데이터를 변경 한 사람이 없는지 확인합니다. 다시이 검증 단계는 제 3자가 Json Web Token의 헤더 또는 페이로드를 실제로 변경하지 않았는지 확인합니다.

그렇다면이 검증은 실제로 어떻게 작동합니까? 글쎄, 그것은 실제로 매우 간단합니다. JWT가 수신되면 검증은 헤더와 페이로드를 취하고 여전히 서버에 저장된 비밀과 함께 기본적으로 테스트 서명을 작성합니다.

그러나 JWT가 처음 만들어 졌을 때 생성 된 원래 서명은 여전히 ​​토큰에 있습니다. 이것이 바로이 검증의 핵심입니다. 이제 테스트 서명을 원래 서명과 비교하기 만하면됩니다. 테스트 서명이 원래 서명과 동일하면 페이로드와 헤더가 수정되지 않았 음을 의미합니다.
여기에 이미지 설명을 입력하십시오

수정 된 경우 테스트 서명이 달라야합니다. 따라서 데이터를 변경하지 않은 경우 사용자를 인증 할 수 있습니다. 물론 두 시그니처가 실제로 다르면 누군가 데이터를 무단 변경했음을 의미합니다. 일반적으로 페이로드를 변경하려고합니다. 그러나 페이로드를 조작하는 제 3자는 물론 비밀에 액세스 할 수 없으므로 JWT에 서명 할 수 없습니다. 따라서 원래 서명은 조작 된 데이터와 절대 일치하지 않습니다. 따라서이 경우 검증은 항상 실패합니다. 이것이 전체 시스템을 작동시키는 열쇠입니다. JWT를 그렇게 단순하면서도 강력하게 만드는 것은 마술입니다.


답변

서버에있는 JWT의 privateKey 만 암호화 된 JWT를 해독합니다. privateKey를 알고있는 사람은 암호화 된 JWT를 해독 할 수 있습니다.

서버의 안전한 위치에 privateKey를 숨기고 다른 사람에게 privateKey를 말하지 마십시오.


답변

나처럼 비싼 데이터베이스 쿼리를 감당할 수없는 사람들을 위해 민감한 데이터 (사용자 권한 등)를 유지하는 한 가지 옵션은 JWT를 생성 할 때이 데이터를 암호화하여 JWT 토큰에 첨부 할 수 있습니다. (백엔드에 암호화 키를 보관하십시오)

민감한 정보를 읽으려면 JWT 토큰을 백엔드로 보내고 해독하여 정보를 다시 가져올 수 있습니다. 이러한 방식으로 JWT 토큰을 통해 DB 조회를 수행하거나 프런트 엔드에서 민감한 정보를 알 필요가 없습니다.


답변

jwt.io에 존재하지 않는 특수 알고리즘을 사용하여 JWE를 살펴 보는 것이 좋습니다.

참조 링크 : https://www.npmjs.com/package/node-webtokens

jwt.generate('PBES2-HS512+A256KW', 'A256GCM', payload, pwd, (error, token) => {
  jwt.parse(token).verify(pwd, (error, parsedToken) => {
    // other statements
  });
});

이 답변이 너무 늦었거나 이미 길을 찾았을 수도 있지만 여전히 귀하와 다른 사람들에게도 도움이 될 것이라고 생각했습니다.

내가 만든 간단한 예 : https://github.com/hansiemithun/jwe-example