node.js - JWT verifisere på klientsiden?


Jeg har en Node.js API med en Angular frontend. API-en bruker JWT med Passport for å sikre endepunktene sine på en vellykket måte.

Jeg er nå klar over at etter at tokenene har utløpt, vil frontenden min fortsatt tillate brukeren å be om mine API-endepunkter uten å be dem om å skrive inn innloggingsdetaljene på nytt for å få et nytt token.

Slik genererer backenden min tokenet:

function generateToken(user) {
  return jwt.sign(user, secret, {
    expiresIn: 10080 // in seconds
  });
}

Så for å implementere denne logikken tror jeg at jeg må verifisere JWT-tokenet på klientsiden. Spørsmål 1, er dette en fornuftig tilnærming.

Q2, JWT-biblioteket jeg bruker ser ut til å kreve en offentlig nøkkel for å bruke verify()-funksjonen. Jeg ser ikke ut til å ha en offentlig nøkkel, bare en hemmelighet, som jeg nettopp fant på, så den ble ikke generert med et par. Hvor kommer min offentlige nøkkel fra, eller finnes det en annen måte å verifisere tokenet mitt uten dette?

Alt dette virker som om det burde være åpenbart, og at jeg har gått glipp av noe, så beklager hvis dette er et dumt spørsmål, men jeg ser ikke ut til å finne svaret?

Svar #1

TL;DR

  1. Du må alltid verifisere signaturen til JWS på serveren.
  2. Signaturverifisering på klientsiden gir ikke mye, med mindre du har en spesifikk situasjon hvor det gir mening ikke gjør det.
  3. Du trenger ikke å verifisere signaturen til en JWS-token for å sjekke utløp i klienten. (med mindre du krypterte påstandene, også kjent som JWE, i så fall må du gjøre noe lignende fordi du trenger en nøkkel for å dekryptere påstandene).
  4. Du trenger heller ikke å verifisere signaturen til en JWS for å sjekke utløp i serveren, men du bør gjøre det fordi dette gir deg sikkerhet for at ingen har endret utløpsdatoen (ellers vil verifiseringen feile fordi hvis påstandene endres, vil den nyberegnede signaturen bli annerledes)
  5. For å lese ikke-krypterte krav trenger du bare å dekode dem. Du kan bruke jwt-decode på klienten.

Jeg er nå bevisst på at etter at tokenene har utløpt, vil frontend fortsatt tillate brukeren å forespørre mine API-endepunkter [...]

Så for å implementere denne logikken tror jeg at jeg må verifisere JWT-tokenet på klientsiden

Hvis jeg forstod deg riktig, snakker du om å sjekke om en JWS har utløpt på klientsiden. For å gjøre dette trenger du ikke å verifisere tokensignaturen (selv om biblioteket du bruker ser ut til å gjøre begge deler samtidig for deg, men lar deg også deaktivere utløpskontroll med ignoreExpiration-flagget). (Med mindre du krypterer claims, altså bruker JWE) RFC 7515 (JWS) sier ingenting om utløp. Meldingssignatur eller MAC-validering kontrollerer ikke utløp (og det bør den ikke gjøre fordi signaturer gir deg ekthet og integritet). Selv RFC 7519 (JWT) kontrollerer ikke utløpsclaimen for å avgjøre om en JWT er gyldig eller ikke.

Også er alle kravene valgfrie.

Så, du kan sjekke om en JWT har utløpt eller ikke uten å verifisere signaturen, derfor trenger du verken en offentlig nøkkel (for asymmetrisk kryptering som RSA) eller en hemmelig nøkkel (for symmetrisk kryptering som AES). I JWT- og JWS-tokener er påstandene bare ren tekst, base64-kodet, så du kan bare dekode innholdet uten å verifisere om signaturen er gyldig og lese utløpspåstanden. Hvis du krypterer innholdet (altså bruker JWE) vil du ikke kunne gjøre dette.

Et notat fra jjwt-biblioteket

JWT-er kan kryptografisk signeres (noe som gjør det til en JWS) eller krypteres (noe som gjør det til en JWE).

Her er et lettvektsbibliotek fra Auth0 for å dekode base64-kodede påstander i en JWT/JWS-token. En fyr spør til og med om hvordan man sjekker utløp.

Jeg vet ikke hvorfor du tror at du bør gjøre denne kontrollen på klientsiden, den eneste fordelen er å unngå å sende API-forespørsler som klienten vet vil mislykkes. Og de burde mislykkes fordi serveren bør validere at tokenet ikke har utløpt, tidligere signaturverifisering (med hemmelig/private nøkkel) åpenbart.

RFC 7519 sier om dette kravet:

Påstanden "exp" (utløpstid) angir utløpstidspunktet på eller etter hvilket JWT IKKE må aksepteres for behandling.

I en nettapplikasjon som den du nevner, er bruken av tokens for å tillate statsløse servere å autentisere klientforespørsler. Målet med VALGFRI utløpspåstand er å gi serveren en viss kontroll over den genererte JWS (hvis vi bruker JWT for autentisering, er det å signere dem et must, så vi bør snakke om JWS).

Uten utløp vil tokenene være gyldige for alltid eller til nøkkelen som brukes til å signere dem endres (dette vil gjøre at verifiseringsprosessen mislykkes). Forresten, å ugyldiggjøre økter er en av de mest beryktede ulempene ved å bruke statisk autentisering.

Økt ugyldiggjøring blir et reelt problem hvis vi inkluderer informasjon i JWS-payloaden (også kjent som claims) som brukes til autorisasjon, for eksempel hvilke roller brukeren har.

Slutt å bruke JWT for økter

men mer seriøst, det kan også bety at noen har en token med en rolle som administrator, selv om du nettopp har tilbakekalt deres administratorrolle. Fordi du heller ikke kan ugyldiggjøre tokens, finnes det ingen måte for deg å fjerne deres administratoradgang

Utløpskontrollen løser ikke dette problemet, og jeg tror den er mer orientert mot å unngå økt øktkapring eller CSRF-angrep.

En angriper som bruker CSRF vil kunne sende en forespørsel med en utløpt JWS til API-et ditt og hoppe over utløpskontrollen.

Et annet problem er å verifisere signaturen i klienten ved bruk av den offentlige eller hemmelige nøkkelen.

Når det gjelder spørsmålet ditt

Jeg bruker ser ut til å kreve en offentlig nøkkel for å bruke dets verify()-funksjon. Jeg ser ikke ut til å ha en offentlig nøkkel, bare en hemmelig, som jeg nettopp laget, så den ble ikke generert som et par.

Verify-metoden du pekte på sier eksplisitt at den aksepterer en offentlig eller hemmelig nøkkel.

jwt.verify(token, secretOrPublicKey, [options, callback])

secretOrPublicKey er en streng eller buffer som inneholder enten hemmeligheten for HMAC-algoritmer, eller den PEM-kodede offentlige nøkkelen for RSA og ECDSA

Jeg antar at du ikke bruker noen av delene, og at du bruker en streng som 'shhhh'.

var token = jwt.sign({ data: '¿Donde esta Santiago?'}, 'shhhh');

Da bør du gjøre

var decoded = jwt.verify(token, 'shhhhh');

Men spørsmålet her er: Er klientside-signaturverifisering virkelig nødvendig?

Jeg tror ikke det, i det minste ikke for denne typen applikasjon der klienten bare bruker JWS for å sende påfølgende forespørsler til serveren og sier: "Hei server, jeg er Gabriel og jeg har et papir (token) her som bekrefter dette, og det papiret er signert av deg." Så hvis klienten ikke validerer JWS og en MITM (mann-i-midten) har klart å gi den klienten en JWS signert av seg selv (i stedet for JWS signert av serveren), vil den påfølgende forespørselen rett og slett mislykkes. Som utløpskontroll, hindrer signaturverifisering bare klienten i å gjøre forespørsler som vil mislykkes.

Nå krever klient-side verifisering å sende den offentlige eller hemmelige nøkkelen. Å sende den offentlige nøkkelen representerer ikke en sikkerhetsrisiko, men det er ekstra arbeid og prosessering med lille fordeler.

Å sende hemmelige nøkler (som 'shhhh') kan representere et sikkerhetsproblem fordi det er den samme nøkkelen som brukes til å signere tokens.

Svar #2

Jeg legger det bare her for de som kom hit på jakt etter jwt-verifisering i nettleseren med en offentlig nøkkel.

Bibliotek: https://kjur.github.io/jsrsasign/

Eksempel: https://kjur.github.io/jsrsasign/tool/tool_jwtveri.html

Kodeeksempel: https://github.com/kjur/jsrsasign/blob/master/tool/tool_jwtveri.html

API: https://kjur.github.io/jsrsasign/api/symbols/KJUR.jws.JWS.html#.verifyJWT

P.S. Ikke bruk din hemmelige nøkkel for nettleserens JWT-verifisering! Kun offentlig nøkkel!

Relaterte artikler