在客戶端,您有一個(gè)供用戶填寫的登錄或注冊(cè)表單。然后,客戶端將用戶的憑據(jù)發(fā)送到服務(wù)器。服務(wù)器驗(yàn)證憑據(jù)并為用戶創(chuàng)建新會(huì)話。它發(fā)回身份驗(yàn)證令牌或會(huì)話 ID。

客戶端將此 ID 或令牌存儲(chǔ)在前端。然后,它會(huì)隨客戶端向服務(wù)器發(fā)出的每個(gè)請(qǐng)求將其發(fā)送回去。這就是服務(wù)器識(shí)別哪個(gè)用戶正在發(fā)出請(qǐng)求以及資源是否允許傳入請(qǐng)求的方式。

身份驗(yàn)證工作流程中可能存在漏洞

服務(wù)器負(fù)責(zé)生成會(huì)話 ID 或身份驗(yàn)證令牌。另一方面,客戶端負(fù)責(zé)安全地存儲(chǔ)它。如果泄露,此身份驗(yàn)證令牌或會(huì)話 ID 可能會(huì)在您的身份驗(yàn)證工作流程中造成漏洞。然后,攻擊者可以利用它使用合法用戶的帳戶侵入您的應(yīng)用程序。

此外,攻擊者可以竊取用戶的所有憑據(jù),訪問私有資源并篡改用戶的數(shù)據(jù)。由于您的服務(wù)器識(shí)別出該活動(dòng)來自合法用戶,因此您和用戶都不會(huì)對(duì)此情況感到驚慌。這可能會(huì)對(duì)您的產(chǎn)品、用戶和業(yè)務(wù)造成很大損害。

但是在什么情況下此令牌或 ID 可能會(huì)泄露?讓我們討論一下 Angular 在服務(wù)器端和客戶端上身份驗(yàn)證失敗的幾種情況。

服務(wù)器端漏洞

當(dāng)用戶通過注銷結(jié)束會(huì)話時(shí),服務(wù)器應(yīng)刪除該會(huì)話 ID 或身份驗(yàn)證令牌。然后,下次用戶創(chuàng)建新會(huì)話時(shí),服務(wù)器還應(yīng)生成一個(gè)全新的會(huì)話 ID。

然而,很多時(shí)候服務(wù)器只是更新數(shù)據(jù)庫中的會(huì)話標(biāo)志以注銷用戶。因此,它會(huì)對(duì)較新的會(huì)話使用相同的會(huì)話 ID。這種做法可能會(huì)有問題。如果攻擊者或第三方之前見過您的會(huì)話 ID,即使您結(jié)束會(huì)話,他們也可以使用相同的會(huì)話 ID 劫持您的帳戶。

因此,服務(wù)器絕不應(yīng)該對(duì)新會(huì)話使用舊的會(huì)話 ID。相反,它應(yīng)該為每個(gè)新會(huì)話創(chuàng)建一個(gè)新的會(huì)話 ID 或身份驗(yàn)證令牌。此外,服務(wù)器應(yīng)該為每個(gè)會(huì)話輪換會(huì)話 ID。換句話說,會(huì)話 ID 應(yīng)該有一個(gè)TTL。在 TTL 過期后,應(yīng)該終止或重新創(chuàng)建會(huì)話。這很有用,因?yàn)榉?wù)器不需要完全依賴客戶端來終止會(huì)話。

現(xiàn)在您已經(jīng)了解了一些服務(wù)器端漏洞以及如何應(yīng)對(duì)它們,接下來讓我們來討論客戶端漏洞。

客戶端漏洞

您的大多數(shù)客戶端代碼都可在瀏覽器中使用。因此,至關(guān)重要的是,您要以某種方式構(gòu)建您的系統(tǒng),以便在源代碼的全局變量暴露時(shí),系統(tǒng)仍能保持完整。

您應(yīng)該遵循的最佳做法之一是確保會(huì)話 ID 不會(huì)被任何人輕易看到或訪問。首先,您不應(yīng)該在前端 URL 中存儲(chǔ)或附加身份驗(yàn)證令牌或會(huì)話 ID。

Angular 身份驗(yàn)證失敗指南:示例和預(yù)防措施

您應(yīng)該在前端實(shí)現(xiàn)會(huì)話管理,使會(huì)話 ID 遠(yuǎn)離 UI。為此,您可以將會(huì)話 ID 存儲(chǔ)在瀏覽器存儲(chǔ)中,而不是路由參數(shù)中。

讓我們看看如何在 Angular 應(yīng)用程序中實(shí)現(xiàn)上述實(shí)現(xiàn)。結(jié)果是我們應(yīng)該對(duì) Angular 破壞身份驗(yàn)證有一些防御措施。

如何在 Angular 中處理會(huì)話管理

首先,我們將創(chuàng)建一個(gè)帶有路由的新 Angular 應(yīng)用:

ng new my-app

接下來,我們將創(chuàng)建兩個(gè)組件:主頁組件和登錄組件。

ng  g c home login

然后我們將為這些組件配置路由。轉(zhuǎn)到app-routing.module.ts文件并添加以下內(nèi)容:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { HomeComponent } from './home/home.component';
const routes: Routes = [
{ path:"", component:LoginComponent},
{ path:"home", component:HomeComponent},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

添加認(rèn)證服務(wù)

接下來,我們將創(chuàng)建一個(gè)模擬登錄 API 操作的服務(wù)。

ng g s auth

在此服務(wù)中,我們將有一個(gè)函數(shù),它簡(jiǎn)單地向我們返回一個(gè)模擬會(huì)話 ID。auth.service.ts 文件應(yīng)如下所示

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
})
export class AuthService {
baseURL: string = "https://random-data-api.com/api/users/random_user";

constructor(private http: HttpClient) { }

getAuthUser(): Observable<any> {
return this.http.get(this.baseURL)
}
}

我們的服務(wù)有一個(gè)getAuthUser函數(shù),它只是向模擬 API 發(fā)出 HTTP 請(qǐng)求,該 API 將返回一些數(shù)據(jù)。在這些數(shù)據(jù)中,我們將獲得有關(guān)用戶和id屬性的一些信息。出于本教程的目的,我們假設(shè)此id是用戶的會(huì)話 ID。明白了嗎?

創(chuàng)建登錄頁面

讓我們回到我們的登錄組件。在login.component.html文件中,我有一個(gè)簡(jiǎn)單的標(biāo)記來呈現(xiàn)登錄表單。

<section>
<h3>Login Page</h3>
<form>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Remember Me</label>
</div>
<button (click)="login($event)" type="submit" class="btn btn-primary">Submit</button>
</form>
</section>

我在login.component.css文件中也添加了一些樣式,如下所示:

section{
margin: 20px auto;
max-width: 400px;
}

section h3{
margin: 20px 0;
}

這應(yīng)該使登錄頁面看起來如下所示:

Angular 身份驗(yàn)證失敗指南:示例和預(yù)防措施

我使用 Bootstrap 快速將一些樣式引入到我的模板中。

login.component.ts文件中,我只需從我們的Auth服務(wù)中調(diào)用getAuthUser函數(shù)并以編程方式路由到 home 組件。我還將從getAuthUser函數(shù)收到的id作為查詢參數(shù)傳遞給 home 組件。

login.component.ts文件如下所示:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

constructor(private AuthService:AuthService, private router: Router) { }

ngOnInit(): void {
}

login(e:any){
e.preventDefault()
this.AuthService.getAuthUser().subscribe(data=>
this.router.navigate(['/home'],{queryParams:{session_id:data.id}})
)
}

}

如果您注意到模板,當(dāng)從模板單擊登錄按鈕時(shí),將調(diào)用上面創(chuàng)建的登錄函數(shù)。

Home 組件

Home 組件的 HTML 文件有一個(gè)簡(jiǎn)單的模板,如下所示:

<section>
<h3>This is the home page</h3>
<p>Session ID of user: {{sessionId}}</p>
</section>

現(xiàn)在,我們?cè)?strong>/home URL 中獲取sessionId作為查詢參數(shù)。我們將從路由中獲取它并將其附加到我們組件的狀態(tài)變量中。以下是home.component.ts文件應(yīng)有的樣子:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

sessionId:any;
constructor(private route:ActivatedRoute) { }

ngOnInit(): void {
this.route.queryParams.subscribe(params=>this.sessionId=params['session_id'])
}

}

如果您現(xiàn)在單擊登錄頁面上的登錄按鈕,您將獲得以下內(nèi)容:

Angular 身份驗(yàn)證失敗指南:示例和預(yù)防措施

問題

如您所見,URL 中的查詢參數(shù)清楚地顯示了用戶的會(huì)話 ID。但正如我之前提到的,這不是一個(gè)好方法,可能會(huì)導(dǎo)致 Angular 在您的應(yīng)用程序中破壞身份驗(yàn)證。如果用戶在公共計(jì)算機(jī)上使用您的應(yīng)用程序,在主頁上,然后忘記注銷,該怎么辦?

或者,如果有人只是偷看了您的用戶的計(jì)算機(jī)并看到前端 URL 上清晰可見的會(huì)話 ID,會(huì)怎么樣?

使用瀏覽器存儲(chǔ)

我們不必在前端路由中傳遞會(huì)話 ID,而是可以將其推送到瀏覽器存儲(chǔ)(如本地存儲(chǔ)或會(huì)話存儲(chǔ))中。

更新后的登錄功能應(yīng)如下所示:

 login(e:any){
e.preventDefault()
this.AuthService.getAuthUser().subscribe(data=>{
// this.router.navigate(['/home'],{queryParams:{session_id:data.id}})
localStorage.setItem('session_id',data.id);
this.router.navigateByUrl('/home')
})
}

然后,在我們的主組件中,我們可以從localStorage中獲取會(huì)話 ID :

ngOnInit(): void {
//this.route.queryParams.subscribe(params=>this.sessionId=params['session_id'])
this.sessionId=localStorage.getItem('session_id')
}

現(xiàn)在,如果您返回/login并再次按登錄按鈕,您仍然應(yīng)該在/home頁面上看到會(huì)話 ID,但它不應(yīng)該出現(xiàn)在您的 URL 中:

Angular 身份驗(yàn)證失敗指南:示例和預(yù)防措施

您可以通過在 Angular 應(yīng)用中使用 NgRX 的全局狀態(tài)來進(jìn)一步改進(jìn)此實(shí)現(xiàn)。

創(chuàng)建萬無一失的身份驗(yàn)證

我們已經(jīng)了解了會(huì)話管理如何幫助您預(yù)防身份驗(yàn)證漏洞。但是,您可以實(shí)施預(yù)防措施來降低因 Angular 身份驗(yàn)證失敗而遭受損害的可能性。這個(gè)想法很簡(jiǎn)單:如果您創(chuàng)建一個(gè)萬無一失的身份驗(yàn)證系統(tǒng),那么您已經(jīng)可以預(yù)防應(yīng)用程序中未來出現(xiàn)身份驗(yàn)證漏洞了!

首先,您應(yīng)該在他們的系統(tǒng)中實(shí)現(xiàn)密碼強(qiáng)度驗(yàn)證。核心功能將在后端實(shí)現(xiàn),但如果您希望更快地交付此功能,您也可以使用這樣的客戶端庫。

Angular 身份驗(yàn)證失敗指南:示例和預(yù)防措施

其次,您還應(yīng)該為空閑用戶實(shí)現(xiàn)自動(dòng)退出功能。這對(duì)應(yīng)于用戶忘記從應(yīng)用程序退出以及在公共計(jì)算機(jī)上關(guān)閉應(yīng)用程序窗口的用例。

您可以檢測(cè)用戶是否閑置了超過指定的時(shí)間,并自動(dòng)向服務(wù)器發(fā)送會(huì)話終止或注銷請(qǐng)求。這里有一個(gè)Angular,可以方便地實(shí)現(xiàn)這一點(diǎn)。

Angular 身份驗(yàn)證失敗指南:示例和預(yù)防措施

結(jié)論

防止 Angular 身份驗(yàn)證失敗的第一步始終是實(shí)現(xiàn)萬無一失的身份驗(yàn)證。在服務(wù)器端實(shí)現(xiàn)這些身份驗(yàn)證并讓 Angular 客戶端在需要時(shí)與這些服務(wù)交互更安全、更好。這是因?yàn)榍岸藱z查、驗(yàn)證和障礙更容易繞過。

文章來源:Angular Broken Authentication Guide: Examples and Prevention

上一篇:

.NET 內(nèi)容安全策略指南:是什么及如何啟用

下一篇:

Node.js中的安全指南
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)