
免費(fèi)API深度求索之路:獲取、調(diào)用與應(yīng)用
在客戶端,您有一個(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)求的方式。
服務(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)證失敗的幾種情況。
當(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。
您應(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)證有一些防御措施。
首先,我們將創(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 { }
接下來,我們將創(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。明白了嗎?
讓我們回到我們的登錄組件。在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)該使登錄頁面看起來如下所示:
我使用 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 組件的 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)容:
如您所見,URL 中的查詢參數(shù)清楚地顯示了用戶的會(huì)話 ID。但正如我之前提到的,這不是一個(gè)好方法,可能會(huì)導(dǎo)致 Angular 在您的應(yīng)用程序中破壞身份驗(yàn)證。如果用戶在公共計(jì)算機(jī)上使用您的應(yīng)用程序,在主頁上,然后忘記注銷,該怎么辦?
或者,如果有人只是偷看了您的用戶的計(jì)算機(jī)并看到前端 URL 上清晰可見的會(huì)話 ID,會(huì)怎么樣?
我們不必在前端路由中傳遞會(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īng)用中使用 NgRX 的全局狀態(tài)來進(jìn)一步改進(jìn)此實(shí)現(xià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),但如果您希望更快地交付此功能,您也可以使用這樣的客戶端庫。
其次,您還應(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)證失敗的第一步始終是實(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
免費(fèi)API深度求索之路:獲取、調(diào)用與應(yīng)用
面向開發(fā)人員的 8 個(gè)最佳區(qū)塊鏈 API
2024年七大最佳免費(fèi)貨幣轉(zhuǎn)換API
如何通過Smart Image Cropping API自動(dòng)裁剪圖像?
News API + React:創(chuàng)建一個(gè)卓越的實(shí)時(shí)新聞應(yīng)用程序
30款免費(fèi)開放的API,助力營(yíng)銷人員與內(nèi)容開發(fā)者
免費(fèi)獲取韻達(dá)快遞查詢API的使用指南
OpenAI ChatGPT API 與 React JS 的完美結(jié)合:全面指南
面向營(yíng)銷人員的 API:前 7 名免費(fèi) REST API
對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力
一鍵對(duì)比試用API 限時(shí)免費(fèi)