paint-brush
Jei negalite valdyti saugyklos, valdykite prieigąpateikė@axotion
199 skaitymai

Jei negalite valdyti saugyklos, valdykite prieigą

pateikė Kamil Fronczak4m2025/01/18
Read on Terminal Reader

Per ilgai; Skaityti

Pastaruoju metu galvojau, kaip padaryti kai kuriuos svarbiausius galutinius taškus saugesnius naudojant vietoje saugomą JWT.
featured image - Jei negalite valdyti saugyklos, valdykite prieigą
Kamil Fronczak HackerNoon profile picture

Pastaruoju metu galvojau, kaip padaryti kai kuriuos svarbiausius galutinius taškus saugesnius naudojant vietoje saugomą JWT.


Galbūt tai nėra geriausia saugumo praktika (dėl XSS atakų galimybės), bet tai buvo reikalavimas, kurio aš nenustačiau. Turėjau prisitaikyti.


Taigi, kad tai būtų saugesnė, radau sprendimą, kuris, tikiuosi, padės ir jums.

Problema

Darau prielaidą, kad visi žinome, kas yra JWT – backend paslaugos išduotas prieigos raktas, kurio frontend negali keisti, nes tai pakeistų parašą ir automatiškai panaikintų prieigos raktą.


Skamba puikiai, kol nesusidursime su situacija, kai tokenas gali būti pavogtas – kaip mano atveju su XSS ataka. Daugelis svetainių saugo žetonus vietinėje saugykloje, o ne tik HTTP slapuką, todėl jie taip pat yra pažeidžiami.

Sprendimas

Taigi, kai neįmanoma pakeisti būdo, kuriuo priekinėje sistemoje saugomas šis prieigos raktas, turime pakeisti prieigos rakto išdavimo ir patvirtinimo būdą, ir tai yra momentas, kai noriu supažindinti jus su paprastu sprendimu – JWT su maišos piršto atspaudu, bet pažiūrėkime kodą


Tarkime, kad turime galinį tašką žetonams išduoti, pavadinkime jį prisijungimo tašku

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


Taigi, ką mes darome čia, iš esmės renkame vartotojo agentą ir nuotolinį IP iš vartotojo, kuris prisijungia, sumaišo jį ir įdedame jį į JWT prieigos raktą, todėl net jei vartotojas ar užpuolikas norėtų pamatyti, kas yra JWT prieigos rakte, jie tik pamatyti kokią nors atsitiktinę maišos reikšmę


Bet kaip tai padidins mano saugumą, klausiate? Pažvelkime į antrąjį galinį tašką, parodykime piršto atspaudą


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


Iš pradžių tai nieko ypatingo. Galutinis taškas, grąžinantis piršto atspaudą iš užklausos, taigi, kas daro jį saugesnį? 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; } }

Kaip matote, strategijoje išgauname tas pačias reikšmes, kaip ir tada, kai jas ištraukėme iš prisijungimo galinio taško, ir lyginame – jei pasikeitė IP arba vartotojo agentas, uždrausime prieigą prie savo pirštų atspaudų galinio taško.


Tai yra atvejis, kai net jei užpuolikas pavogė mūsų žetoną, jis negali nieko su juo padaryti, nes

  • Jis nežino, kokia maiša yra piršto atspaude
  • Neleisime jam prisijungti prie mūsų galinių taškų dėl skirtingo IP ir vartotojo agento (o gal ir daugiau veiksnių)


Taip galime labai paprastai apsaugoti savo esminį galinį tašką, tačiau, žinoma, yra tam tikrų kompromisų

  • Kiekvienam skambučiui turime apskaičiuoti sha512 maišą
  • Jei vartotojas turi dinaminį IP, jis turės dažnai prisijungti


Dėl pirmosios problemos, žinoma, galite patikrinti tik IP ir net nenaudoti maišos išduodami prieigos raktą, bet tada potencialus užpuolikas žino atakos vektorių (IP klaidinimas ar kiti metodai), todėl saugiau paaukoti šiek tiek pasirodymas dėl saugumo.


Tikiuosi, kad šis metodas padės jums įdiegti papildomą saugumo lygį svarbiems galutiniams taškams.


Nuoroda į veikiantį šaltinio kodą: Šaltinio kodas


Patarimai:

  • X-FORWARDER-FOR antraštę galima lengvai suklastoti, nebent naudojate tokias paslaugas kaip „Cloudflare“, turėkite tai omenyje


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

PABAIGTI ŽYMES

ŠIS STRAIPSNIS BUVO PRISTATYMAS...