paint-brush
Kung Hindi Mo Makontrol ang Storage, Kontrolin ang Accesssa pamamagitan ng@axotion
199 mga pagbabasa

Kung Hindi Mo Makontrol ang Storage, Kontrolin ang Access

sa pamamagitan ng Kamil Fronczak4m2025/01/18
Read on Terminal Reader

Masyadong mahaba; Upang basahin

Kamakailan, pinag-iisipan ko kung paano gagawing mas secure ang ilan sa aking mahahalagang endpoint kapag gumagamit ng JWT na lokal na nakaimbak.
featured image - Kung Hindi Mo Makontrol ang Storage, Kontrolin ang Access
Kamil Fronczak HackerNoon profile picture

Kamakailan, pinag-iisipan ko kung paano gagawing mas secure ang ilan sa aking mahahalagang endpoint kapag gumagamit ng JWT na lokal na nakaimbak.


Maaaring hindi ito ang pinakamahusay na kasanayan para sa seguridad (dahil sa posibilidad ng pag-atake ng XSS), ngunit ito ay isang kinakailangan na hindi ko itinakda. Kinailangan kong umangkop.


Kaya para maging mas secure ito, nakahanap ako ng solusyon, na, sana, ay makatulong din sa iyo.

Ang Problema

Ipinapalagay ko na alam nating lahat kung ano ang JWT - isang token na inisyu ng backend service, na hindi mababago ng frontend, dahil mababago nito ang lagda at awtomatikong magpapawalang-bisa sa token.


Napakaganda nito, hanggang sa matugunan natin ang sitwasyon kung saan maaaring manakaw ang token - tulad ng sa aking kaso sa pag-atake ng XSS. Maraming website ang nag-iimbak ng mga token sa lokal na storage sa halip na HTTP-only na cookie, kaya mahina rin ang mga ito.

Ang Solusyon

Kaya, kapag hindi posible na baguhin ang paraan ng pag-iimbak ng frontend ng token na iyon, kailangan nating baguhin ang paraan ng pag-isyu at pagpapatunay ng token, at ito ang sandali kung kailan gusto kong ipakilala sa iyo ang isang simpleng solusyon - JWT na may naka-hash na fingerprint, ngunit tingnan natin ang code


Ipagpalagay natin, na mayroon tayong endpoint para mag-isyu ng mga token, tawagan natin itong sign-in endpoint

 @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, }; } }


Kaya ang ginagawa namin dito, ay karaniwang nangongolekta ng user agent at remote IP mula sa user na kumokonekta, nagha-hash nito, at pagkatapos ay inilalagay ito sa JWT token, kaya kahit na gusto ng user o attacker na makita kung ano ang nasa loob ng JWT token, gagawin lang nila. makakita ng ilang random na hash na halaga


Ngunit paano nito mapapalakas ang aking seguridad na itatanong mo? Tingnan natin ang aming pangalawang endpoint, ipakita ang fingerprint


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


Sa una, ito ay walang espesyal. Endpoint na nagbabalik ng fingerprint mula sa kahilingan, kaya ano ang ginagawang mas secure? Ang JWT_STRATEGY!


 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; } }

Tulad ng nakikita mo, sa diskarte, kinukuha namin ang parehong mga halaga tulad noong kinuha namin ang mga ito mula sa endpoint sa pag-sign-in at inihambing ang mga ito - kung nagbago ang IP o user agent, tatanggihan namin ang access sa aming fingerprint endpoint.


Ito ang kaso kung saan kahit ninakaw ng attacker ang token namin, wala siyang magagawa, kasi

  • Hindi niya alam kung ano ang hash sa loob ng fingerprint
  • Hindi namin siya papayagan na kumonekta sa aming mga endpoint, dahil sa iba't ibang IP at user agent (at marahil higit pang mga kadahilanan)


Ganyan natin mapoprotektahan ang ating napakahalagang endpoint sa napakasimpleng paraan, ngunit siyempre, may ilang mga tradeoffs

  • Para sa bawat tawag, kailangan nating kalkulahin ang sha512 hash
  • Kung ang user ay may dynamic na IP kailangan niyang mag-log in nang madalas


Para sa unang problema, siyempre, maaari mong suriin lamang ang IP at huwag mo itong i-hash kapag nag-isyu ng isang token, ngunit pagkatapos ay alam ng potensyal na umaatake ang vector ng pag-atake (IP spoofing o iba pang mga pamamaraan), kaya mas ligtas na isakripisyo ang ilan. pagganap para sa seguridad.


Umaasa ako na ang paraang ito ay makakatulong sa iyo na magpakilala ng karagdagang layer ng seguridad sa iyong mahahalagang endpoint.


Link sa gumaganang source code: Source Code


Mga tip:

  • Ang X-FORWARDER-FOR na header ay madaling ma-spoof maliban kung gumagamit ka ng mga serbisyo tulad ng Cloudflare, tandaan iyon