paint-brush
Om du inte kan kontrollera lagringen, kontrollera åtkomstenförbi@axotion
199 avläsningar

Om du inte kan kontrollera lagringen, kontrollera åtkomsten

förbi Kamil Fronczak4m2025/01/18
Read on Terminal Reader

För länge; Att läsa

Nyligen har jag funderat på hur jag kan göra några av mina avgörande slutpunkter säkrare när jag använder lokalt lagrad JWT.
featured image - Om du inte kan kontrollera lagringen, kontrollera åtkomsten
Kamil Fronczak HackerNoon profile picture

Nyligen har jag funderat på hur jag kan göra några av mina avgörande slutpunkter säkrare när jag använder lokalt lagrad JWT.


Det kanske inte är den bästa praxisen för säkerhet (på grund av möjligheten för XSS-attacker), men detta var ett krav som inte ställts av mig. Jag var tvungen att anpassa mig.


Så för att göra detta säkrare har jag hittat en lösning som förhoppningsvis också kommer att hjälpa dig.

Problemet

Jag antar att vi alla vet vad JWT är - en token utfärdad av backend-tjänsten, som inte kan modifieras av frontend, eftersom det skulle ändra signaturen och automatiskt ogiltigförklara token.


Det låter bra, tills vi möter situationen där token kan bli stulen - som i mitt fall med XSS-attack. Många webbplatser lagrar tokens i lokal lagring istället för endast HTTP-cookie, så de är också sårbara.

Lösningen

Så när det inte är möjligt att ändra hur frontend lagrar den token, måste vi ändra sättet vi utfärdar och validerar token, och detta är ögonblicket då jag vill introducera dig för en enkel lösning - JWT med hashat fingeravtryck, men låt oss se koden


Låt oss anta att vi har en slutpunkt för att utfärda tokens, låt oss kalla det inloggningsslutpunkt

 @Controller('v1/sign-in') export class SignInAction { constructor(private jwtService: JwtService) {} @Post() async handle( @Req() request: Request, @Body() body: SignInHttpRequest, ): Promise<SignInHttpResponse> { const ip = request.headers['x-forwarded-for'] || request.socket.remoteAddress; const userAgent = request.headers['user-agent']; const token = this.jwtService.sign({ email: body.email, fingerprint: getSHA512Hash(`${ip}${userAgent}`), // It's worth to mention, that to make it even more secure, you should add salt }); return { token, }; } }


Så vad vi gör här är i princip att samla in användaragent och fjärr-IP från användare som ansluter, hasha det och sedan lägga in det i JWT-token, så även om användare eller angripare skulle vilja se vad som finns inuti JWT-token, kommer de bara att se något slumpmässigt hashat värde


Men hur kommer det att öka min säkerhet undrar du? Låt oss ta en titt på vår andra slutpunkt, visa fingeravtrycket


 @Controller('v1/fingerprint') @UseGuards(AuthGuard(JWT_STRATEGY)) export class ShowFingerPrintAction { @Get() async handle(@Req() request) { return { fingerprint: request.user.fingerprint, }; } }


Till en början är det inget speciellt. Slutpunkt som returnerar fingeravtryck från begäran, så vad gör det säkrare? JWT_STRATEGI!


 export const JWT_STRATEGY = 'JWT'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy, JWT_STRATEGY) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: 'hard!to-guess_secret', passReqToCallback: true, }); } async validate(request: Request, payload: any): Promise<any> { const fingerprint = payload.fingerprint; const ip = request.headers['x-forwarded-for'] || request.socket.remoteAddress; const userAgent = request.headers['user-agent']; const calculatedFingerprint = getSHA512Hash(`${ip}${userAgent}`); // It's worth to mention, that to make it even more secure, you should add salt if (fingerprint !== calculatedFingerprint) { throw new BadRequestException('Invalid fingerprint'); } return payload; } }

Som du kan se, i strategi, extraherar vi samma värden som när vi extraherade dem från inloggningsslutpunkten och jämför dem - om IP-adressen eller användaragenten har ändrats, kommer vi att neka åtkomst till vår fingeravtrycksslutpunkt.


Detta är fallet där även om angriparen stal vår token, kan han inte göra något med den, eftersom

  • Han vet inte vad haschen inne i fingeravtrycket är
  • Vi kommer inte att tillåta honom att ansluta till våra slutpunkter på grund av annan IP och användaragent (och kanske fler faktorer)


Det är så vi kan skydda vår avgörande slutpunkt på ett mycket enkelt sätt, men naturligtvis finns det några avvägningar

  • För varje samtal måste vi beräkna sha512-hash
  • Om användaren har en dynamisk IP måste han logga in ofta


För det första problemet kan du naturligtvis bara kontrollera IP och inte ens hasha den när du utfärdar en token, men då känner den potentiella angriparen till vektorn för attacken (IP-spoofing eller andra metoder), så det är säkrare att offra en del prestanda för säkerheten.


Jag hoppas att den här metoden kommer att hjälpa dig att introducera ett extra lager av säkerhet för dina avgörande slutpunkter.


Länk till fungerande källkod: Källkod


Tips:

  • X-FORWARDER-FOR header kan lätt förfalskas om du inte använder tjänster som Cloudflare, tänk på det


L O A D I N G
. . . comments & more!

About Author

Kamil Fronczak HackerNoon profile picture
Kamil Fronczak@axotion
I’m a 2X-year-old tech dude from Poland, and this is my blog about tech stuff: NestJS, Node

HÄNG TAGGAR

DENNA ARTIKEL PRESENTERAS I...