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.
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.
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
Tādā veidā mēs varam ļoti vienkāršā veidā aizsargāt savu svarīgo galapunktu, taču, protams, ir daži kompromisi
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: