目前大多數(shù)應(yīng)用程序都會(huì)包含:服務(wù)器端邏輯、客戶端邏輯、數(shù)據(jù)存儲(chǔ)、數(shù)據(jù)傳輸、以及API等多個(gè)組件。與此同時(shí),每種語(yǔ)言、框架、以及環(huán)境的使用,都會(huì)讓應(yīng)用程序暴露于一組獨(dú)特的漏洞之中。為了保證這些組件的安全,第一時(shí)間發(fā)現(xiàn)應(yīng)用的漏洞,進(jìn)而構(gòu)建出一個(gè)安全態(tài)勢(shì)較高的應(yīng)用,往往需要我們付出不懈的努力。
值得慶幸的是,大多應(yīng)用程序的漏洞都有著相似、甚至相同的底層原因。研究這些常見(jiàn)的漏洞類型、以及背后的原因,將有助于我們對(duì)應(yīng)用程序進(jìn)行恰當(dāng)?shù)姆雷o(hù)。
下面,我將和您一起討論影響Angular和React應(yīng)用的如下六種最常見(jiàn)的漏洞,以及如何發(fā)現(xiàn)和預(yù)防它們:
- 身份驗(yàn)證繞過(guò)
- 訪問(wèn)控制不當(dāng)
- 開(kāi)放式重定向
- 跨站請(qǐng)求偽造 (CSRF)
- 模板注入
- 跨站點(diǎn)腳本包含 (XSSI)
身份驗(yàn)證繞過(guò)
身份驗(yàn)證是指在執(zhí)行敏感操作、或訪問(wèn)敏感數(shù)據(jù)之前,先驗(yàn)明身份的合法性。如果在應(yīng)用程序上未能正確地實(shí)施身份驗(yàn)證,那么攻擊者將可以利用此類錯(cuò)誤配置,來(lái)訪問(wèn)他們本不該能夠訪問(wèn)到的服務(wù)與功能。例如,Angular通常使用AppRoutingModule來(lái)進(jìn)行路由。在將用戶引導(dǎo)至應(yīng)用程序中的敏感路由之前,您應(yīng)該檢查用戶是否已通過(guò)了身份驗(yàn)證,并被授權(quán)了訪問(wèn)該資源。請(qǐng)參考如下代碼段:
({
imports: [RouterModule.forRoot([
// These paths are available to all users.
{ path: '', component: HomeComponent },
{ path: 'features', component: FeaturesComponent },
{ path: 'login', component: LoginComponent },
// These routes are only available to users after logging in.
{ path: 'feed', component: FeedComponent, canActivate: [ AuthGuard ]},
{ path: 'profile', component: ProfileComponent, canActivate: [ AuthGuard ]},
// This is the fall-through component when the route is not recognized.
{ path: '**', component: PageNotFoundComponent}
])],
exports: [RouterModule]
})
export class AppRoutingModule {}
訪問(wèn)控制不當(dāng)
攻擊者會(huì)想方設(shè)法繞過(guò)那些訪問(wèn)權(quán)限控制實(shí)施不當(dāng)?shù)膽?yīng)用程序。訪問(wèn)控制不僅僅只包括身份驗(yàn)證。也就是說(shuō),我們除了需要證明用戶的身份(即“你是誰(shuí)?”),還要通過(guò)應(yīng)用程序授予相應(yīng)的權(quán)限(即“你可以做什么?”)。只有通過(guò)兩者雙管齊下,才能共同確保用戶不會(huì)訪問(wèn)到超出其權(quán)限的服務(wù)與功能。
目前,我們有多種方式為用戶配置授權(quán),其中包括:基于角色的訪問(wèn)控制、基于所有權(quán)的訪問(wèn)控制、以及訪問(wèn)控制列表等。而開(kāi)發(fā)人員常犯的一種錯(cuò)誤是在客戶端執(zhí)行授權(quán)檢查。由于攻擊者可以操控和覆蓋客戶端的檢查,因此這是不安全的??梢?jiàn),此類授權(quán)檢查必須使用服務(wù)器端的代碼來(lái)執(zhí)行。請(qǐng)參考如下代碼段:
export class AdministratorGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<true | UrlTree> {
// Check whether this user is an administratoor.
return this.authService.isAdmin().pipe(
map(isAdmin => {
if (!isAdmin) {
return this.router.parseUrl('/')
}
return isAdmin
})
)
}
}
export class AuthService {
constructor(private http: HttpClient) {}
// Whether the user is currently logged in.
loggedIn: boolean | null = null
// The user object object encompassing the user's name and role. Will be set
user: User | null = null
// Check whether the current user is an administrator.
isAdmin(): Observable<boolean> {
return this.getCurrentUser().pipe(map(user => {
return user != null && user.role === 'admin'
}))
}
// Get the user definition from local state, or the server (if this is the first time we are checking).
getCurrentUser(): Observable<User | null> {
if (this.loggedIn !== null) {
return of(this.user)
}
return this.http.get<User>('/api/auth', {
responseType: 'json'
}).pipe(
tap({
next: user => {
// If we get a user definition from the server it indicates this user is logged in.
this.user = user
this.loggedIn = true
},
error: error => {
// A 401 response from the server indicates this user is not logged in.
this.user = null
this.loggedIn = false
}
}),
catchError(() => {
return of(null)
})
)
}
}
export interface User {
username: string
role: string
}
開(kāi)放式重定向
例如,當(dāng)未經(jīng)身份驗(yàn)證的用戶嘗試著訪問(wèn)需要登錄后才能看到的頁(yè)面時(shí),網(wǎng)站就需要將該用戶自動(dòng)重定向到登錄頁(yè)面,并在他們通過(guò)了身份驗(yàn)證之后,再讓其返回到原來(lái)的位置。
在開(kāi)放式重定向攻擊發(fā)生時(shí),攻擊者通過(guò)向用戶提供來(lái)自合法站點(diǎn)的URL,以欺騙用戶訪問(wèn)某個(gè)外部站點(diǎn)。也就是說(shuō),該URL會(huì)將其重定到其他站點(diǎn)。而該站點(diǎn)一面設(shè)法讓用戶相信他們?nèi)匀辉谠季W(wǎng)站上,一面幫助攻擊者構(gòu)建出更加看似可信的網(wǎng)絡(luò)釣魚(yú)活動(dòng)。
為了防止開(kāi)放式重定向,您需要確保應(yīng)用程序不會(huì)輕易將用戶重定向到那些惡意站點(diǎn)的位置。例如,您可以通過(guò)驗(yàn)證重定向URL,來(lái)完全禁止離站重定向行為。請(qǐng)參考如下代碼段:
export class LoginComponent {
// The username and password entered by the user in the login form.
username = '';
password = '';
// The destination URL to redirect the user to once they log in successfully.
destinationURL = '/feed'
constructor(private authService : AuthService,
private route : ActivatedRoute,
private router : Router) { }
ngOnInit() {
this.destinationURL = this.route.snapshot.queryParams['destination'] || '/feed';
}
onSubmit() {
this.authService.login(this.username, this.password)
.subscribe(
() => {
// After the user has lgged in, redirect them to their desired destination.
let url = this.destinationURL
// Confirm that the URL is a relative path - i.e. starting with a single '/' characters.
if (!url.match(/^\/[^\/\\]/)) {
url = '/feed'
}
this.router.navigate([ url ])
})
}
}
當(dāng)然,我們還有許多其他方法可以防止開(kāi)放式重定向的發(fā)生。例如:對(duì)請(qǐng)求引用方予以檢查、或使用頁(yè)面索引進(jìn)行重定向。不過(guò),正因?yàn)轵?yàn)證URL相對(duì)比較困難,因此開(kāi)放式重定向仍然是當(dāng)代Web應(yīng)用普遍存在的問(wèn)題。
跨站請(qǐng)求偽造
跨站點(diǎn)請(qǐng)求偽造(Cross-Site Request Forgery,CSRF)是一種客戶端技術(shù),可用于攻擊Web應(yīng)用的其他用戶。使用CSRF,攻擊者可以發(fā)送虛假的、來(lái)自受害者的HTTP請(qǐng)求,去執(zhí)行攻擊者的危害性操作。例如,攻擊者會(huì)在未經(jīng)許可的情況下,更改受害者的密碼、或從其銀行賬戶里轉(zhuǎn)賬。
與開(kāi)放式重定向不同,我們目前已有一種行之有效的對(duì)抗CSRF的方法,即:使用CSRF令牌和SameSite Cookie的組合,以避免使用GET請(qǐng)求進(jìn)行各項(xiàng)狀態(tài)更改的操作。例如,Angular允許您使用HttpClientXsrfModule模塊,向HTTP請(qǐng)求添加防偽的令牌。請(qǐng)參考如下代碼段:
({
declarations: [],
imports: [
BrowserModule,
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-CSRF-TOKEN'
}),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}
模板注入
類似于HTML文件的Web模板,為開(kāi)發(fā)人員提供了一種通過(guò)將應(yīng)用數(shù)據(jù)與靜態(tài)模板相結(jié)合,以指定如何呈現(xiàn)頁(yè)面的方法。此功能允許開(kāi)發(fā)人員將從數(shù)據(jù)庫(kù)或HTTP請(qǐng)求中檢索到的動(dòng)態(tài)內(nèi)容,插入到網(wǎng)頁(yè)中。
顧名思義,模板注入需要注入到網(wǎng)頁(yè)的模板中。根據(jù)受感染應(yīng)用的權(quán)限,攻擊者可以通過(guò)使用模板注入的漏洞,來(lái)讀取敏感文件、執(zhí)行惡意代碼、或提升他們?cè)谙到y(tǒng)上的各種權(quán)限。下面展示了Angular模板的不安全用法。它允許攻擊者通過(guò)URL的哈希值,來(lái)惡意注入代碼:
({
selector: 'app-header',
template: '<h1>' + (window.location.hash || 'Home') + '</h1>'
})
export class HeaderComponent {}
注意:請(qǐng)千萬(wàn)不要直接將用戶提供的輸入連接到模板中,而應(yīng)該使用模板引擎的內(nèi)置替換機(jī)制,來(lái)安全地嵌入動(dòng)態(tài)輸入。請(qǐng)參考如下代碼段:
({
selector: 'app-header',
template: '<h1>{{ title }}</h1>'
})
export class HeaderComponent {
title = ''
ngOnInit() {
this.title = window.location.hash || 'Home';
}
}
跨站點(diǎn)腳本
跨站點(diǎn)腳本包含的攻擊也稱為XSSI(Cross-Site Script Inclusion)。此類攻擊發(fā)生在當(dāng)惡意站點(diǎn)包含了來(lái)自受害者站點(diǎn)的Javascript,并通過(guò)腳本提取其敏感信息時(shí)。
同源策略(same-origin policy,SOP)通??梢云鸬娇刂茢?shù)據(jù)跨源(cross-origins)訪問(wèn)的作用。不過(guò),SOP并不能限制JavaScript代碼,而且HTML<script>標(biāo)簽會(huì)允許從任何來(lái)源加載JavaScript代碼。從技術(shù)角度來(lái)說(shuō),該功能方便了允許跨域重用的JavaScript文件,但是會(huì)帶來(lái)新的安全風(fēng)險(xiǎn):攻擊者可以通過(guò)加載受害者的JS文件,來(lái)竊取寫入JavaScript文件的數(shù)據(jù)。
例如,某網(wǎng)站通過(guò)Javascript文件,為登錄用戶存儲(chǔ)和傳輸敏感數(shù)據(jù)。如果用戶在同一瀏覽器中訪問(wèn)了惡意站點(diǎn),那么惡意站點(diǎn)可以導(dǎo)入該JavaScript文件,并訪問(wèn)與該用戶會(huì)話相關(guān)的敏感信息。而這一切都?xì)w因于存儲(chǔ)在瀏覽器中的用戶Cookie。
因此,為避免XSSI攻擊,請(qǐng)勿在JavaScript文件中傳輸敏感數(shù)據(jù)。下面是如何使用JSON文件(會(huì)受到SOP的限制)在Angular中安全地加載API令牌的示例:
// The configuration information we will retrieve from the server.
export interface Config {
username : string
accessToken : string
role : string
}
()
export class ConfigService {
constructor(private http: HttpClient) {}
// Retrieve configuration information from the server.
getConfig() {
return this.http.get<Config>('api/config')
.pipe(
catchError(this.handleError)
)
}
private handleError(error: HttpErrorResponse) {
if (error.status === 0) {
// A client-side or network error occurred. Handle it accordingly.
log.error('An error occurred:', error.error)
} else {
// The server returned an unsuccessful response code.
log.error(`Backend returned code ${error.status}, body was: `, error.error)
}
return throwError('An unexpected error occurred loading configuration.')
}
}
譯者介紹
陳峻 (Julian Chen),51CTO社區(qū)編輯,具有十多年的IT項(xiàng)目實(shí)施經(jīng)驗(yàn),善于對(duì)內(nèi)外部資源與風(fēng)險(xiǎn)實(shí)施管控,專注傳播網(wǎng)絡(luò)與信息安全知識(shí)與經(jīng)驗(yàn);持續(xù)以博文、專題和譯文等形式,分享前沿技術(shù)與新知;經(jīng)常以線上、線下等方式,開(kāi)展信息安全類培訓(xùn)與授課。
原文標(biāo)題:??Angular + React: Vulnerability Cheatsheet??,作者: Vickie Li