paint-brush
Ja nevarat kontrolēt krātuvi, kontrolējiet piekļuviautors@axotion
195 lasījumi

Ja nevarat kontrolēt krātuvi, kontrolējiet piekļuvi

autors Kamil Fronczak4m2025/01/18
Read on Terminal Reader

Pārāk ilgi; Lasīt

Pēdējā laikā esmu domājis par to, kā dažus no maniem svarīgiem galapunktiem padarīt drošākus, izmantojot lokāli saglabāto JWT.
featured image - Ja nevarat kontrolēt krātuvi, kontrolējiet piekļuvi
Kamil Fronczak HackerNoon profile picture

Nesen esmu domājis par to, kā dažus no maniem svarīgiem galapunktiem padarīt drošākus, izmantojot lokāli saglabāto JWT.


Iespējams, ka tā nav labākā drošības prakse (XSS uzbrukumu iespējamības dēļ), taču tā bija prasība, ko es nenoteicu. Man bija jāpielāgojas.


Tāpēc, lai to padarītu drošāku, esmu atradis risinājumu, kas, cerams, arī jums palīdzēs.

Problēma

Es pieņemu, ka mēs visi zinām, kas ir JWT - aizmugursistēmas servisa izdots marķieris, kuru frontend nevar modificēt, jo tas mainītu parakstu un automātiski padarītu tokenu nederīgu.


Tas izklausās lieliski, līdz mēs saskaramies ar situāciju, kad marķieris var tikt nozagts - kā manā gadījumā ar XSS uzbrukumu. Daudzas vietnes glabā marķierus vietējā krātuvē, nevis tikai HTTP sīkfailā, tāpēc arī tās ir neaizsargātas.

Risinājums

Tātad, ja nav iespējams mainīt veidu, kā priekšgalā tiek uzglabāts šis marķieris, mums ir jāmaina veids, kā mēs izsniedzam un validējam marķieri, un šis ir brīdis, kad es vēlos jūs iepazīstināt ar vienkāršu risinājumu — JWT ar jauktu pirkstu nospiedumu, bet paskatīsimies kodu


Pieņemsim, ka mums ir galapunkts marķieru izsniegšanai, sauksim to par pierakstīšanās galapunktu

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


Tātad tas, ko mēs šeit darām, pamatā ir lietotāja aģenta un attālā IP apkopošana no lietotāja, kurš veido savienojumu, to jaukšana un pēc tam ievietošana JWT pilnvarā, tāpēc pat tad, ja lietotājs vai uzbrucējs vēlas redzēt, kas atrodas JWT pilnvarā, viņi tikai skatiet kādu nejaušu jauktu vērtību


Bet kā tas uzlabos manu drošību, jūs jautājat? Apskatīsim mūsu otro galapunktu, parādīsim pirkstu nospiedumu


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


Sākumā tas nav nekas īpašs. Galapunkts, kas atgriež pirksta nospiedumu pēc pieprasījuma. Kas padara to drošāku? JWT_STRATĒĢIJA!


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

Kā redzat, stratēģijā mēs iegūstam tās pašas vērtības, kuras izvilkām no pierakstīšanās galapunkta, un salīdzinām tās — ja ir mainījies IP vai lietotāja aģents, mēs liegsim piekļuvi mūsu pirkstu nospiedumu galapunktam.


Šis ir gadījums, kad pat tad, ja uzbrucējs nozaga mūsu žetonu, viņš ar to neko nevar izdarīt, jo

  • Viņš nezina, kas ir pirksta nospieduma iekšpusē
  • Mēs neļausim viņam izveidot savienojumu ar mūsu galapunktiem dažādu IP un lietotāja aģentu (un varbūt vairāku faktoru) dēļ.


Tādā veidā mēs varam ļoti vienkāršā veidā aizsargāt savu svarīgo galapunktu, taču, protams, ir daži kompromisi

  • Katram zvanam mums ir jāaprēķina sha512 hash
  • Ja lietotājam ir dinamisks IP, viņam būs bieži jāpiesakās


Pirmās problēmas gadījumā jūs, protams, varētu pārbaudīt tikai IP un pat nejaukt to, izsniedzot marķieri, bet tad potenciālais uzbrucējs zina uzbrukuma vektoru (IP viltošana vai citas metodes), tāpēc drošāk ir upurēt dažus sniegums drošībai.


Es ceru, ka šī metode palīdzēs jums ieviest papildu drošības līmeni jūsu svarīgajos galapunktos.


Saite uz strādājošu avota kodu: avota kods


Padomi:

  • X-FORWARDER-FOR galveni var viegli viltot, ja vien neizmantojat tādus pakalpojumus kā Cloudflare, paturiet to prātā


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

PAKARINĀT TAGUS

ŠIS RAKSTS TIKS PĀRSTRĀDĀTS...