通過Salesforce喻频,我們可以配置或開發(fā)出功能強大的網(wǎng)絡(luò)應(yīng)用甥温。與此同時姻蚓,無論作為管理員還是開發(fā)者狰挡,我們都要面對數(shù)據(jù)安全的問題加叁。
常見的數(shù)據(jù)安全隱患有:
- SQL注入
- 跨站腳本攻擊
- 跨站請求偽造
- 點擊劫持
- 重定向攻擊
本文將闡述在Salesforce中對于上述隱患的基本防護措施窿撬。
SOQL注入
SQL注入是一種常見的攻擊方式窖认。Salesforce中使用了類似于SQL的數(shù)據(jù)庫查詢語言SOQL扑浸,同樣存在注入攻擊的風(fēng)險喝噪。
實例
假設(shè)系統(tǒng)提供給用戶的功能是查詢所有金額少于某個數(shù)值的記錄,用戶可以輸入數(shù)值進行查詢驰吓。
當(dāng)用戶輸入數(shù)值之后,系統(tǒng)會用類似于下面的SOQL進行查詢缺亮。
SELECT Id, Name, Amount
FROM Certain_Object__c
WHERE Amount < 1000
其中的1000就是直接來源于用戶的輸入萌踱。
那么當(dāng)用戶輸入“1000 LIMIT 1”的時候并鸵,查詢語句將變?yōu)椋?/p>
SELECT Id, Name, Amount
FROM Certain_Object__c
WHERE Amount < 1000 LIMIT 1
此時园担,系統(tǒng)總是只能返回一個結(jié)果,功能被破壞了艰山。
防護方法
Salesforce提供了多種方法來過濾用戶的輸入曙搬,避免SOQL注入攻擊的情況出現(xiàn)纵装。它們多用于Apex代碼中据某。
靜態(tài)查詢和參數(shù)綁定
如果在代碼中預(yù)先設(shè)定好了SOQL的語句哗脖,并且將之與用戶輸入拼接形成最終的查詢語句才避,使用靜態(tài)查詢和參數(shù)綁定的方法可以避免注入攻擊。
比如:
String query = 'SELECT Id, Name FROM Account WHERE Name = \'' + userInputString + '\'';
result = Database.execute(query);
這段查詢代碼信任了用戶的輸入棘劣,從而有了被注入攻擊的風(fēng)險茬暇。
比如用戶輸入是:“test' LIMIT 1”糙俗,“test”后面的單引號就將查詢語句里的WHERE部分結(jié)束巧骚,而將LIMIT部分加入了查詢劈彪。注入攻擊的結(jié)果就是每次返回的記錄只有一條沧奴。
使用靜態(tài)查詢和參數(shù)綁定滔吠,改寫上面的代碼:
result = [SELECT Id, Name FROM Account WHERE Name = :userInputString];
這段代碼將用戶的輸入作為參數(shù)綁定到查詢語句中屠凶。當(dāng)用戶輸入包含惡意代碼時矗愧,它并不會拼接到前面的查詢中,而是作為用戶輸入的一部分進行正常查詢夜涕。
當(dāng)用戶輸入是“test' LIMIT 1”時女器,系統(tǒng)會將其看作一個整體驾胆,單引號不會將WHERE部分結(jié)束丧诺,后面的LIMIT的部分也不會被執(zhí)行了驳阎。
數(shù)據(jù)類型轉(zhuǎn)換
Salesforce提供了幾種數(shù)據(jù)類型轉(zhuǎn)換函數(shù)呵晚,可以安全的將用戶輸入轉(zhuǎn)換為相應(yīng)的數(shù)據(jù)類型沫屡。
比如:
result = string.valueOf(userInputString)
可以將用戶輸入userInputString變?yōu)橐粋€字符串沮脖,以便放入查詢語句中使用。
過濾單引號
Salesforce提供了一個函數(shù)將字符串中的單引號全部過濾。
result = string.escapeSingleQuotes(userInputString)
這個函數(shù)的返回結(jié)果是將用戶輸入userInputString中的所有單引號前面都加上轉(zhuǎn)義符“\”涮因,保證查詢語句的安全养泡。
跨站腳本攻擊(XSS)
跨站腳本攻擊全稱Cross-site scripting澜掩。攻擊者將惡意代碼注入網(wǎng)頁肩榕,當(dāng)用戶瀏覽網(wǎng)站時,惡意代碼就會自動執(zhí)行筐乳。攻擊成功后蝙云,攻擊者將可能獲得更高的權(quán)限勃刨、可以查看session身隐、cookie或其他私密內(nèi)容抡医。
XSS的惡意代碼多數(shù)是由于網(wǎng)頁上對于用戶的輸入不加檢查而導(dǎo)致忌傻。比如攻擊者將惡意代碼輸入表單搞监,在未經(jīng)檢查的情況下被保存到數(shù)據(jù)庫中琐驴,等待其他頁面讀取它,然后執(zhí)行宙刘。
XSS的基本防護措施
XSS的基本防護措施分為兩方面:
- 輸入過濾:一般的編程語言都有相應(yīng)的函數(shù)來過濾用戶的輸入悬包,將可能存在的惡意代碼轉(zhuǎn)化為普通字符串布近,從而使其在被讀取的時候無法執(zhí)行
- 輸出編碼:輸出編碼的目的是防止已經(jīng)存在于系統(tǒng)中的惡意代碼無法被執(zhí)行撑瞧。當(dāng)網(wǎng)頁需要輸出內(nèi)容時预伺,已經(jīng)存在的惡意代碼會經(jīng)過編碼轉(zhuǎn)化為普通的字符串扭屁,從而無法執(zhí)行
Salesforce中XSS的防護措施
為了避免將用戶的某些輸入錯誤的過濾掉料滥,Salesforce默認(rèn)對于用戶的輸入不會進行輸入過濾,而在做輸出時高每,總是會執(zhí)行輸出編碼用來避免XSS攻擊鲸匿。
HTML自動編碼機制
Visualforce中“apex”命名空間下的標(biāo)簽在執(zhí)行輸出時带欢,會將輸出的字符串自動進行HTML編碼乔煞。比如:
<apex:outputText value="{!$CurrentPage.parameters.name}" />
如果調(diào)用這個元素的URL中加入了帶有“<”和“>”的“script”標(biāo)簽(可能造成JavaScript代碼注入)渡贾,那么Salesforce頁面的輸出則是:
<script>
而其HTML源碼則是:
<script>
在這種情況下空骚,HTML的標(biāo)簽起止符“<”和“>”被替換成了“<”和“>”,從而避免了注入的JavaScript代碼的執(zhí)行熬甚。
apex:outputText相關(guān)知識
在Visualforce頁面中则涯,“apex:outputText”標(biāo)簽可以將其中的內(nèi)容使用一個“span”標(biāo)簽包括起來,并且對HTML編碼進行自動轉(zhuǎn)換亿昏。這樣就可以對XSS進行防護角钩。
另外递礼,<apex:outputText>中也可以接受參數(shù)脊髓。比如:
<apex:outputText value="This is {0} text with {1}">
<apex:param value="the" />
<apex:param value="parameters" />
</apex:outputText>
使用的規(guī)則是:
- 在“apex:outputText”標(biāo)簽中的value屬性中使用大括號和數(shù)字來對參數(shù)進行占位
- 使用“apex:param”標(biāo)簽來定義參數(shù)的值将硝,多個參數(shù)按照順序替換value屬性中的占位符
上面的代碼內(nèi)容當(dāng)顯示在頁面中時依疼,其后臺的HTML源碼是:
<span>This is the text with parameters.</span>
關(guān)于“apex:outputText”更詳細(xì)的講解可以參考官方文檔
關(guān)閉HTML自動編碼機制
有時頁面中需要輸出原始的HTML內(nèi)容律罢,那么在“apex:outputText”元素中將“escape”屬性設(shè)置為“false”即可。
<apex:outputText escape="false">
{!$CurrentPage.parameters.htmlStringToOutput}
</apex:outputText>
當(dāng)然沧踏,此時必須有其他對于XSS的防護措施來確保輸出的原始HTML內(nèi)容是安全的悦冀。
非HTML內(nèi)容的防護
Salesforce對于非HTML內(nèi)容并沒有默認(rèn)的編碼機制盒蟆。比如在JavaScript代碼中使用用戶輸入的數(shù)據(jù)時,該數(shù)據(jù)不會被編碼讨惩。
<script>
var x = '{!$CurrentPage.parameters.stringFromUser}';
</script>
在上面的代碼中荐捻,用戶輸入的字符串“stringFromUser”不會被自動編碼处面,從而形成了一個安全漏洞菩掏。
Visualforce提供的編碼函數(shù)
對于非HTML的內(nèi)容智绸,Visualforce提供了一些編碼函數(shù)來幫助開發(fā)者防止XSS攻擊瞧栗,主要包括:
- JSENCODE()
- HTMLENCODE()
- JSINHTMLENCODE()
JSENCODE()
JSENCODE()函數(shù)主要用于JavaScript代碼中迹恐。它的主要功能是在特殊字符的前面加上轉(zhuǎn)義符“\”。
<script>
var x = '{!JSENCODE($CurrentPage.parameters.stringFromUser)}';
</script>
在上面的代碼中通熄,用戶輸入的字符串“stringFromUser”會被自動編碼唇辨,避免了惡意代碼的自動執(zhí)行赏枚。
假設(shè)用戶輸入的字符串“stringFromUser”是:
dummy';alert("dummy text");
如果不用JSENCODE()饿幅,alert函數(shù)會被自動執(zhí)行栗恩。
HTMLENCODE()
HTMLENCODE()主要用于將HTML內(nèi)容編碼洪燥,用于當(dāng)Salesforce自動的HTML編碼機制被“escape”屬性關(guān)掉時。
<apex:outputText escape="false">
{!HTMLENCODE($CurrentPage.parameters.htmlStringToOutput)}
</apex:outputText>
在上面的代碼中市咆,字符串“htmlStringToOutput”是有可能造成攻擊的蒙兰,需要使用HTMLENCODE()來避免。
JSINHTMLENCODE()
HTMLENCODE()和JSENCODE()可以組合使用采缚,HTMLENCODE(JSENCODE())仰担。這等價于另一個函數(shù):JSINHTMLENCODE()绩社。
當(dāng)需要同時對HTML和JavaScript編碼時愉耙,可以使用任意一種方式朴沿。
Apex中的編碼
Salesforce并不鼓勵在Apex代碼中對數(shù)據(jù)內(nèi)容進行編碼赌渣,因為數(shù)據(jù)數(shù)據(jù)的輸入輸出應(yīng)該交由前端來處理昌犹,Apex代碼并不需要關(guān)心數(shù)據(jù)內(nèi)容本身斜姥。
跨站請求偽造
跨站請求偽造(Cross-site request forgery)铸敏,簡稱CSRF或XSRF,是一種挾制用戶在當(dāng)前已登錄的Web應(yīng)用程序上執(zhí)行非本意的操作的攻擊方法闪水。
摘自維基百科:
跨站請求攻擊球榆,簡單地說,是攻擊者通過一些技術(shù)手段欺騙用戶的瀏覽器去訪問一個自己曾經(jīng)認(rèn)證過的網(wǎng)站并執(zhí)行一些操作(如發(fā)郵件鞠呈,發(fā)消息蚁吝,甚至財產(chǎn)操作如轉(zhuǎn)賬和購買商品)窘茁。由于瀏覽器曾經(jīng)認(rèn)證過脆烟,所以被訪問的網(wǎng)站會認(rèn)為是真正的用戶操作而去執(zhí)行邢羔。這利用了web中用戶身份驗證的一個漏洞:簡單的身份驗證只能保證請求發(fā)自某個用戶的瀏覽器拜鹤,卻不能保證請求本身是用戶自愿發(fā)出的。攻擊者并不能通過CSRF攻擊來直接獲取用戶的賬戶控制權(quán)明也,也不能直接竊取用戶的任何信息温数。他們能做到的撑刺,是欺騙用戶瀏覽器咙边,讓其以用戶的名義執(zhí)行操作败许。
舉個例子:
假如一個銀行網(wǎng)站轉(zhuǎn)賬請求的URL地址是:(URL-1)
https://www.example.com/transfer?username=alice&amount=1000&to=bob
那么攻擊者可以在網(wǎng)上放置這樣偽造的URL地址:(URL-2)
https://www.example.com/transfer?username=alice&amount=1000&to=charlie
當(dāng)一個用戶登錄了銀行網(wǎng)站市殷,其用戶名是alice,在尚未登出時訪問了攻擊者的站點搞挣,點擊了偽造的URL鏈接(URL-2)囱桨,那么其銀行賬戶會自動轉(zhuǎn)賬1000給用戶名為charlie的用戶。
基本防護方法
CSRF的一般防護方法是在URL的參數(shù)中加入一個口令(token)搀继,口令的值是很長的字符串叽躯,并且隨著每次請求隨機生成点骑。服務(wù)器在接受請求時也會驗證口令黑滴。這樣的話攻擊者就無法猜到準(zhǔn)確的URL跷跪,也就無法利用偽造的URL地址來攻擊了齐板。
Salesforce中的設(shè)置功能自帶了關(guān)于CSRF攻擊的防護設(shè)置甘磨。在設(shè)置界面的“會話設(shè)置”連接中济舆,可以進入“會話設(shè)置”的界面滋觉。在其中可以設(shè)置多種安全保護措施椎侠。在“跨站請求偽造 (CSRF) 保護”一欄我纪,可以設(shè)置是否啟用CSRF保護浅悉。
在Apex和Visualforce中防護跨站請求偽造
在Visualforce頁面中术健,有一種情況是Salesforce中CSRF防護設(shè)置無法保護的荞估,比如下面的Visualforce頁面(以下稱作“頁面1”):
<apex:page controller="ExampleController" action="{!initFunction}">
<!-- ... -->
</apex:page>
與之對應(yīng)的Apex類:
public class ExampleController {
public void initFunction() {
String id = ApexPages.currentPage().getParameters().get('UserId');
// 使用UserId發(fā)送請求
}
}
頁面1的功能是直接讀取URL中的UserId參數(shù)泼舱,然后利用該參數(shù)發(fā)送請求。當(dāng)在另一個Visualforce頁面(以下稱作“頁面2”)中使用“outputLink”鏈接到頁面1時:
<apex:outputLink value="/apex/Example?UserId={!person.Id}">
Click
</apex:outputLink>
因為函數(shù)“initFunction()”是定義在頁面1的“apex:page”標(biāo)簽的“action”屬性中的尺迂,所以該函數(shù)會在頁面1載入瀏覽器之前就執(zhí)行噪裕,所以它會跳過在設(shè)置界面中設(shè)置的CSRF防護措施膳音。
要讓CSRF防護措施執(zhí)行铃诬,需要將頁面2中的“outputLink”鏈接變?yōu)椤癱ommandLink”祭陷,即:
<apex:commandLink value="Click" action="{!alternativeInitFunction}">
<apex:param name="uId" value="{!person.Id}" assignTo="{!curUserId}" />
</apex:commandLink>
將頁面1中的“initFunction”的功能挪到“commandLink”定義的“alternativeInitFunction()”函數(shù)中,并傳入相應(yīng)的參數(shù)趣席。然后將頁面1中的“apex:page”標(biāo)簽中的“action”屬性去掉兵志。
這樣做的結(jié)果是:本來在頁面1中剛開始就要執(zhí)行的功能和請求被放在了頁面2的“commandLink”鏈接中,而后者因為頁面2肯定已經(jīng)被載入了宣肚,所以在執(zhí)行功能和發(fā)送請求時想罕,會調(diào)用Salesforce中設(shè)置的CSRF防護措施。
這兩種流程總結(jié)對比一下:
- 使用outputLink:先重定向到新的頁面霉涨,在頁面載入之前執(zhí)行outputLink的action屬性中定義的函數(shù)按价,在函數(shù)中會發(fā)送請求,發(fā)送的請求無法使用CSRF防護措施笙瑟,函數(shù)執(zhí)行完畢后才載入頁面
- 使用commandLink:先用Ajax異步執(zhí)行commandLink的action屬性中定義的函數(shù)鸠蚪,此時因為當(dāng)前頁面時已經(jīng)載入了,所以可以使用CSRF防護措施妖谴,然后通過鏈接重定向到新的頁面并載入
點擊劫持
維基百科上對“點擊劫持”是這樣定義的:
點擊劫持(clickjacking)是一種在網(wǎng)頁中將惡意代碼等隱藏在看似無害的內(nèi)容(如按鈕)之下窑多,并誘使用戶點擊的手段技潘。
舉個例子:在網(wǎng)頁上值桩,攻擊者將一個包含惡意鏈接或其他惡意內(nèi)容的iframe元素設(shè)置透明度為0,并顯示在網(wǎng)頁正常內(nèi)容的上方。由于iframe的透明度為0,用戶無法看到其中的惡意內(nèi)容,只能看到網(wǎng)頁上的正常內(nèi)容。當(dāng)用戶在看似正常的內(nèi)容上面點擊時碍彭,實際上點擊的是包含惡意內(nèi)容的iframe元素疏橄,從而被攻擊者利用,遭受損失。
在Salesforce的設(shè)置頁面中彰导,進入“會話設(shè)置”的界面折汞。在其中可以設(shè)置是否啟用“Clickjack 保護”损同。
重定向攻擊
在瀏覽網(wǎng)站時组哩,用戶需要頁面重定向功能黍衙。重定向功能往往體現(xiàn)在URL的參數(shù)中位仁,比如
https://www.example.com?returnURL=https://www.exampleWebsite.com
當(dāng)用戶使用此URL時涛浙,頁面會自動重定向到“https://www.exampleWebsite.com”中胸墙。
當(dāng)攻擊者將重定向的鏈接改為了惡意網(wǎng)頁,則該URL便成為了網(wǎng)絡(luò)攻擊的工具。
Salesforce中的頁面重定向功能
在Salesforce中,提供了標(biāo)準(zhǔn)的頁面重定向參數(shù):
- startURL:當(dāng)頁面被載入時重定向到指定的URL
- retURL:當(dāng)用戶點擊“后退”按鈕時重定向到指定的URL
- saveURL:當(dāng)用戶點擊“保存”按鈕時重定向到指定的URL
- cancelURL:當(dāng)用戶點擊“取消”按鈕時重定向到指定的URL
當(dāng)這些參數(shù)的值可以被攻擊者利用時,比如參數(shù)的值從用戶輸入中得到,那么Salesforce的網(wǎng)頁就存在被攻擊的風(fēng)險屈留。
重定向攻擊防護
在Salesforce中帮寻,可以采取以下幾種方法進行重定向攻擊防護:
對重定向進行硬編碼(Hardcode)
通過對重定向進行硬編碼惜傲,可以完全防止用戶更改重定向鏈接。但是這會損失靈活性。
只重定向到本地
另一種方法是強迫所有重定向鏈接只能重定向到同一個域名下质欲,比如只能重定向到“xxx.salesforce.com”下盛霎,這樣的話可以保證無論用戶如何更改重定向鏈接,被重定向的鏈接總是處于開發(fā)者的控制下。
假設(shè)在一個Visualforce頁面中包含一個“urlToRedirect”參數(shù),它來源于用戶的輸入,然后該頁面會重定向到“urlToRedirect”唤蔗。那么我們可以用以下代碼強制用戶的輸入重定向到當(dāng)前的域名下:
PageReference finalPage;
// 得到用戶的輸入
String urlToRedirect = ApexPages.currentPage().getParameters().get('urlToRedirect');
// 清除用戶輸入開頭的“/”符號
if(urlToRedirect.startsWith('/') {
urlToRedirect = urlToRedirect.replaceFirst('/', '');
})
// 將頁面的重定向設(shè)定到當(dāng)前域名下,保證用戶轉(zhuǎn)到的頁面是當(dāng)前網(wǎng)站下的某一個頁面(或不存在的頁面)
finalPage = new PageReference('/' + urlToRedirect);
finalPage.setRedirect(true);
設(shè)定域名白名單
如果開發(fā)的過程中的確需要重定向到外部網(wǎng)站最疆,則可以在代碼中手動建立一個字符串列表仍源,作為可信的域名白名單。當(dāng)用戶的輸入包含白名單中的域名時知染,允許頁面重定向到外部網(wǎng)站。
Salesforce中沒有預(yù)設(shè)此類功能或函數(shù),需要開發(fā)者自己實現(xiàn)钢颂。
總結(jié)
以上介紹了在Salesforce中對于常見網(wǎng)絡(luò)攻擊的防護措施,并側(cè)重于在Apex和Visualforce中的實現(xiàn)小腊。
隨著技術(shù)的發(fā)展,這些防護措施可能會過時。所以窜锯,追蹤數(shù)據(jù)安全的最新動態(tài)芍秆、養(yǎng)成時刻考慮數(shù)據(jù)安全的習(xí)慣更加重要朽们。
如果想對數(shù)據(jù)安全(不光是Salesforce)進行更多的學(xué)習(xí)和了解,在此推薦OWASP組織的網(wǎng)站条舔。