重慶分公司,新征程啟航
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊(cè)、服務(wù)器等服務(wù)
為企業(yè)提供網(wǎng)站建設(shè)、域名注冊(cè)、服務(wù)器等服務(wù)
過濾和這兩個(gè)符號(hào)就可以了,用Replace替換成 lt 和 gt 去掉中間空格
創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、湖口網(wǎng)絡(luò)推廣、小程序制作、湖口網(wǎng)絡(luò)營銷、湖口企業(yè)策劃、湖口品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供湖口建站搭建服務(wù),24小時(shí)服務(wù)熱線:13518219792,官方網(wǎng)址:www.cdcxhl.com
添加審核功能,把有用的篩選下來,沒用的去掉。或者加一個(gè)提交限制,用戶提交的里面包含有惡意的內(nèi)容就不讓他提交。再或者你就不用提交到后臺(tái),在前臺(tái)來編譯,類似于HTML、json等的編譯
javaWeb安全漏洞及處理方式
關(guān)注
轉(zhuǎn)載自:
1、SQL注入攻擊
SQL注入攻擊就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請(qǐng)求的查詢字符串,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令。具體來說,它是利用現(xiàn)有應(yīng)用程序,將(惡意)的SQL命令注入到后臺(tái)數(shù)據(jù)庫引擎執(zhí)行的能力,它可以通過在Web表單中輸入(惡意)SQL語句得到一個(gè)存在安全漏洞的網(wǎng)站上的數(shù)據(jù)庫,而不是按照設(shè)計(jì)者意圖去執(zhí)行SQL語句。
隨著B/S框架結(jié)構(gòu)在系統(tǒng)開發(fā)中的廣泛應(yīng)用,惡意攻擊者利用SQL命令在Web表單中輸入合法的字符或查詢字符串來欺騙服務(wù)器執(zhí)行SQL命令。當(dāng)注入攻擊得逞后,Web程序?qū)⑿孤洞罅坑脩綦[私數(shù)據(jù)和數(shù)據(jù)庫中數(shù)據(jù)結(jié)構(gòu)。攻擊者能夠獲得系統(tǒng)較高的訪問權(quán)限,進(jìn)行破壞操作。
SQL注入可以分為平臺(tái)層注入和代碼層注入。前者由不安全的數(shù)據(jù)庫配置或數(shù)據(jù)庫平臺(tái)的漏洞所致;后者主要是由于程序員對(duì)輸入未進(jìn)行細(xì)致地過濾,從而執(zhí)行了非法的數(shù)據(jù)查詢?;诖耍琒QL注入的產(chǎn)生原因通常表現(xiàn)在以下幾方面:
1)不當(dāng)?shù)念愋吞幚?
2)不安全的數(shù)據(jù)庫配置;
3)不合理的查詢集處理;
4)不當(dāng)?shù)腻e(cuò)誤處理;
5)轉(zhuǎn)義字符處理不合適;
6) 多個(gè)提交處理不當(dāng)。
解決方法:
數(shù)據(jù)庫安全通信包括SQL注入攻擊的防范、安全設(shè)置、異常信息處理三個(gè)方面。
1.服務(wù)端Filter對(duì)訪問者輸入的字符進(jìn)行過濾檢驗(yàn),但是攻擊者經(jīng)常把危險(xiǎn)字符潛藏在用戶輸入的有效字符中完 成過濾檢驗(yàn)。
2.通過正則表達(dá)式對(duì)頁面的文本框輸入的數(shù)據(jù)進(jìn)行限制可以減少過濾檢驗(yàn)存在的漏洞。
3.使用prepareStatment預(yù)編譯sql語句
2、XSS跨站腳本攻擊
跨站腳本(Cross-site scripting,簡(jiǎn)稱XSS),是一種迫使Web站點(diǎn)回顯可執(zhí)行代碼的攻擊技術(shù),而這些可執(zhí)行代碼由攻擊者提供、最終為用戶瀏覽器加載。不同于大多數(shù)攻擊(一般只涉及攻擊者和受害者),XSS涉及到三方,即攻擊者、客戶端與網(wǎng)站。XSS的攻擊目標(biāo)是為了盜取客戶端的cookie或者其他網(wǎng)站用于識(shí)別客戶端身份的敏感信息。獲取到合法用戶的信息后,攻擊者甚至可以假冒最終用戶與網(wǎng)站進(jìn)行交互。
XSS 屬于被動(dòng)式的攻擊。攻擊者先構(gòu)造一個(gè)跨站頁面,利用SCRIPT、IMG、IFRAME等各種方式使得用戶瀏覽這個(gè)頁面時(shí),觸發(fā)對(duì)被攻擊站點(diǎn)的HTTP 請(qǐng)求。此時(shí),如果被攻擊者如果已經(jīng)在被攻擊站點(diǎn)登錄,就會(huì)持有該站點(diǎn)cookie。這樣該站點(diǎn)會(huì)認(rèn)為被攻擊者發(fā)起了一個(gè)HTTP請(qǐng)求。而實(shí)際上這個(gè)請(qǐng)求是在被攻擊者不知情情況下發(fā)起的,由此攻擊者在一定程度上達(dá)到了冒充被攻擊者的目的。精心的構(gòu)造這個(gè)攻擊請(qǐng)求,可以達(dá)到冒充發(fā)文,奪取權(quán)限等多個(gè)攻擊目的。在常見的攻擊實(shí)例中,這個(gè)請(qǐng)求是通過script 來發(fā)起的,因此被稱為Cross Site Script。
XSS漏洞成因是由于動(dòng)態(tài)網(wǎng)頁的Web應(yīng)用對(duì)用戶提交請(qǐng)求參數(shù)未做充分的檢查過濾,允許用戶在提交的數(shù)據(jù)中摻入HTML代碼(最主要的是“”、“”),然后未加編碼地輸出到第三方用戶的瀏覽器,這些攻擊者惡意提交代碼會(huì)被受害用戶的瀏覽器解釋執(zhí)行。
分為三種類型:
1)反射型(數(shù)據(jù)流向:瀏覽器 -后端 - 瀏覽器)
反射型XSS腳本攻擊即如我們上面所提到的XSS跨站腳本攻擊方式,該類型只是簡(jiǎn)單地將用戶輸入的數(shù)據(jù)直接或未經(jīng)過完善的安全過濾就在瀏覽器中進(jìn)行輸出,導(dǎo)致輸出的數(shù)據(jù)中存在可被瀏覽器執(zhí)行的代碼數(shù)據(jù)。由于此種類型的跨站代碼存在于URL中,所以黑客通常需要通過誘騙或加密變形等方式,將存在惡意代碼的鏈接發(fā)給用戶,只有用戶點(diǎn)擊以后才能使得攻擊成功實(shí)施。
2)存儲(chǔ)型(數(shù)據(jù)流向是:瀏覽器 -后端 - 數(shù)據(jù)庫 - 后端- 瀏覽器)
存儲(chǔ)型XSS腳本攻擊是指Web應(yīng)用程序會(huì)將用戶輸入的數(shù)據(jù)信息保存在服務(wù)端的數(shù)據(jù)庫或其他文件形式中,網(wǎng)頁進(jìn)行數(shù)據(jù)查詢展示時(shí),會(huì)從數(shù)據(jù)庫中獲取數(shù)據(jù)內(nèi)容,并將數(shù)據(jù)內(nèi)容在網(wǎng)頁中進(jìn)行輸出展示,因此存儲(chǔ)型XSS具有較強(qiáng)的穩(wěn)定性。
存儲(chǔ)型XSS腳本攻擊最為常見的場(chǎng)景就是在博客或新聞發(fā)布系統(tǒng)中,黑客將包含有惡意代碼的數(shù)據(jù)信息直接寫入文章或文章評(píng)論中,所有瀏覽文章或評(píng)論的用戶,都會(huì)在他們客戶端瀏覽器環(huán)境中執(zhí)行插入的惡意代碼。
3)基于DOM(數(shù)據(jù)流向是:URL--瀏覽器 )
基于DOM的XSS跨站腳本攻擊是通過修改頁面DOM節(jié)點(diǎn)數(shù)據(jù)信息而形成的XSS跨站腳本攻擊。不同于反射型XSS和存儲(chǔ)型XSS,基于DOM的XSS跨站腳本攻擊往往需要針對(duì)具體的javascript DOM代碼進(jìn)行分析,并根據(jù)實(shí)際情況進(jìn)行XSS跨站腳本攻擊的利用。
解決方法:
1).輸入過濾。對(duì)用戶的所有輸入數(shù)據(jù)進(jìn)行檢測(cè),比如過濾其中的“”、“”、“/”等可能導(dǎo)致腳本注入的特殊字符,或者過濾“script”、“javascript”等腳本關(guān)鍵字,或者對(duì)輸入數(shù)據(jù)的長度進(jìn)行限制等等。同時(shí),我們也要考慮用戶可能繞開ASCII碼,使用十六進(jìn)制編碼來輸入腳本。因此,對(duì)用戶輸入的十六進(jìn)制編碼,我們也要進(jìn)行相應(yīng)的過濾。只要能夠嚴(yán)格檢測(cè)每一處交互點(diǎn),保證對(duì)所有用戶可能的輸入都進(jìn)行檢測(cè)和XSS過濾,就能夠有效地阻止XSS攻擊。
2).輸出編碼。通過前面對(duì)XSS攻擊的分析,我們可以看到,之所以會(huì)產(chǎn)生XSS攻擊,就是因?yàn)閃eb應(yīng)用程序?qū)⒂脩舻妮斎胫苯忧度氲侥硞€(gè)頁面當(dāng)中,作為該頁面的HTML代碼的一部分。因此,當(dāng)Web應(yīng)用程序?qū)⒂脩舻妮斎霐?shù)據(jù)輸出到目標(biāo)頁面中時(shí),只要用HtmlEncoder等工具先對(duì)這些數(shù)據(jù)進(jìn)行編碼,然后再輸出到目標(biāo)頁面中。這樣,如果用戶輸入一些HTML的腳本,也會(huì)被當(dāng)成普通的文字,而不會(huì)成為目標(biāo)頁面HTML代碼的一部分得到執(zhí)行.
3、CSRF跨站請(qǐng)求偽造漏洞防護(hù)
CSRF是CrossSite Request Forgery的縮寫,乍一看和XSS差不多的樣子,但是其原理正好相反,XSS是利用合法用戶獲取其信息,而CSRF是偽造成合法用戶發(fā)起請(qǐng)求。
字面理解意思就是在別的站點(diǎn)偽造了一個(gè)請(qǐng)求。專業(yè)術(shù)語來說就是在受害者訪問一個(gè)網(wǎng)站時(shí),其 Cookie 還沒有過期的情況下,攻擊者偽造一個(gè)鏈接地址發(fā)送受害者并欺騙讓其點(diǎn)擊,從而形成 CSRF 攻擊。
根據(jù)HTTP協(xié)議,在HTTP頭中有一個(gè)字段叫Referer,它記錄了該HTTP請(qǐng)求的來源地址。在通常情況下,訪問一個(gè)安全受限頁面的請(qǐng)求必須來自于同一個(gè)網(wǎng)站。
解決方案:
配置FILTER攔截用戶所有請(qǐng)求(POST/GET),對(duì)用戶請(qǐng)求Referer頭URL進(jìn)行合法性校驗(yàn)。
4、URL鏈接注入漏洞防護(hù)
鏈接注入是修改站點(diǎn)內(nèi)容的行為,其方式為將外部站點(diǎn)的 URL 嵌入其中,或?qū)⒂幸资芄舻恼军c(diǎn)中的腳本 的 URL 嵌入其中。將URL 嵌入易受攻擊的站點(diǎn)中,攻擊者便能夠以它為平臺(tái)來啟動(dòng)對(duì)其他站點(diǎn)的攻擊,以及攻擊這個(gè)易受攻擊的站點(diǎn)本身。
解決方案:
1,二次驗(yàn)證,進(jìn)行重要敏感操作時(shí),要求用戶進(jìn)行二次驗(yàn)證。
2,驗(yàn)證碼,進(jìn)行重要敏感操作時(shí),加入驗(yàn)證碼。
3,驗(yàn)證 HTTP 的 Referer 字段。
4,請(qǐng)求地址中添加 Token 并驗(yàn)證。
5,HTTP 頭中自定義屬性并驗(yàn)證。
5、會(huì)話COOKIE中缺少HttpOnly防護(hù)
會(huì)話cookie中缺少HttpOnly屬性會(huì)導(dǎo)致攻擊者可以通過程序(JS腳本、Applet等)獲取到用戶的cookie信息,造成用戶cookie信息泄露,增加攻擊者的跨站腳本攻擊威脅。
HttpOnly是微軟對(duì)cookie做的擴(kuò)展,該值指定cookie是否可通過客戶端腳本訪問。Microsoft Internet Explorer 版本 6 Service Pack 1 和更高版本支持cookie屬性HttpOnly。
如果在Cookie中沒有設(shè)置HttpOnly屬性為true,可能導(dǎo)致Cookie被竊取。竊取的Cookie可以包含標(biāo)識(shí)站點(diǎn)用戶的敏感信息。
如果在Cookie中設(shè)置HttpOnly屬性為true,兼容瀏覽器接收到HttpOnly cookie,那么客戶端通過程序(JS腳本、Applet等)將無法讀取到Cookie信息,這將有助于緩解跨站點(diǎn)腳本威脅。
解決方案:
配置filter攔截器,將服務(wù)器端返回請(qǐng)求,向所有會(huì)話cookie中添加“HttpOnly”屬性。
示例代碼:
HttpServletResponseresponse=(HttpServletResponse)paramServletResponse;
response.setHeader("SET-COOKIE","JSESSIONID=" + sessionid + "; HttpOnly");
6、點(diǎn)擊劫持漏洞(Clickjacking)防護(hù)
點(diǎn)擊劫持是一種視覺上的欺騙手段,攻擊者使用一個(gè)透明的、不可見的iframe,覆蓋在一個(gè)網(wǎng)頁上,然后誘使用戶在該網(wǎng)頁上進(jìn)行操作,此時(shí)用戶在不知情的情況下點(diǎn)擊了透明的iframe頁面。通過調(diào)整iframe頁面的位置,可以誘使用戶恰好點(diǎn)擊在iframe頁面的一些功能性按鈕上。
解決方案:
配置FILTER攔截器,在服務(wù)器端返回請(qǐng)求中,使用一個(gè)HTTP頭“X-Frame-Options”值為SAMEORIGIN-同源策略 ,則frame頁面的地址只能為同源域名下面的頁面,防止點(diǎn)擊劫持漏洞發(fā)生。
示例代碼:
HttpServletResponseresponse=(HttpServletResponse)paramServletResponse;
response.addHeader("x-frame-options","SAMEORIGIN");
7、HTTP host 頭攻擊漏洞
使用HTTP代理工具,可以篡改HTTP報(bào)文頭部中HOST字段時(shí),該值可被注入惡意代碼。因?yàn)樾枰刂瓶蛻舳说妮斎耄试撀┒摧^難利用。
解決方案:
配置FILTER攔截器,對(duì)請(qǐng)求輸入HOST頭信息進(jìn)行信息安全性校驗(yàn),防止HOST頭信息被惡意篡改利用。
示例代碼:
HttpServletRequest request =(HttpServletRequest)servletRequest;
//主機(jī)ip和端口 或 域名和端口
String myhosts = request.getHeader("host");
if(!StringUtils.equals(myhosts, "xx.xx.xxx.xxx:xxxx")
!StringUtils.equals(myhosts, "xx.xx.xxx.xxx:xxxx")
!StringUtils.equals(myhosts,"xx.xx.xxx.xxx:xxxx")StringUtils.equals(myhosts,"xx.xx.xxx.xxx")
!StringUtils.equals(myhosts,"xx.xx.xxx.xxx") !StringUtils.equals(myhosts,"xx.xx.xxx.xxx" ){
logger.error("======訪問host非法,已攔截======");
response.sendRedirect(request.getContextPath() + "/login.jsp");
return;
}
8、越權(quán)訪問漏洞防護(hù)
越權(quán)訪問(Broken Access Control,簡(jiǎn)稱BAC)是Web應(yīng)用程序中一種常見的漏洞,分為垂直越權(quán)訪問和水平越權(quán)訪問。垂直越權(quán)是指不同用戶級(jí)別之間的越權(quán),如普通用戶執(zhí)行管理員用戶的權(quán)限。水平越權(quán)是指相同級(jí)別用戶之間的越權(quán)操作。
Web應(yīng)用程序如果存在越權(quán)訪問漏洞,可能導(dǎo)致以下危害:
1)導(dǎo)致任意用戶敏感信息泄露;
2)導(dǎo)致任意用戶信息被惡意修改或刪除。
解決方案:
配置FILTER攔截器,對(duì)請(qǐng)求所有URL進(jìn)行攔截,對(duì)于需要進(jìn)行授權(quán)的URL進(jìn)行權(quán)限校驗(yàn),防止用戶越權(quán)訪問系統(tǒng)資源。
9.弱口令漏洞
解決方案:最好使用至少6位的數(shù)字、字母及特殊字符組合作為密碼。數(shù)據(jù)庫不要存儲(chǔ)明文密碼,應(yīng)存儲(chǔ)MD5加密后的密文,由于目前普通的MD5加密已經(jīng)可以被破解,最好可以多重MD5加密,或者多種加密方式疊加組合。
10.JSP頁面拋出的異??赡鼙┞冻绦蛐畔ⅰ?/p>
有經(jīng)驗(yàn)的入侵者,可以從JSP程序的異常中獲取很多信息,比如程序的部分架構(gòu)、程序的物理路徑、SQL注入爆出來的信息等。
解決方案:自定義一個(gè)Exception,將異常信息包裝起來不要拋到頁面上。
11.本地緩存漏洞
合法用戶“注銷”后,在未關(guān)閉瀏覽器的情況下,點(diǎn)擊瀏覽器“后退”按鈕,可從本地頁面緩存中讀取數(shù)據(jù),繞過了服務(wù)端filter過濾。
解決方案:配置filter對(duì)存放敏感信息的頁面限制頁面緩存。如:
httpResponse.setHeader("Cache-Control","no-cache");
httpResponse.setHeader("Cache-Control","no-store");
httpResponse.setDateHeader("Expires",0);
httpResponse.setHeader("Pragma","no-cache");
12.文件上傳漏洞。
前臺(tái)僅使用JS對(duì)文件后綴做了過濾,這只能針對(duì)普通的用戶,而惡意攻擊者完全可以修改表單去掉JS校驗(yàn)。
13.Java WEB容器默認(rèn)配置漏洞。
如TOMCAT后臺(tái)管理漏洞,默認(rèn)用戶名及密碼登錄后可直接上傳war文件獲取webshell。
解決方案:最好刪除,如需要使用它來管理維護(hù),可更改其默認(rèn)路徑,口令及密碼。
在本文中,我們討論了對(duì)付 13 種不同靜態(tài)暴露的技巧。對(duì)于每種暴露,我們解釋了不處理這些安全性問題所造成的影響。我們還為您推薦了一些準(zhǔn)則,要開發(fā)不受這些靜態(tài)安全性暴露威脅的、健壯且安全的 Java 應(yīng)用程序,您應(yīng)該遵循這些準(zhǔn)則。一有合適的時(shí)機(jī),我們就提供代碼樣本(既有暴露的代碼也有無暴露的代碼)。
對(duì)付高嚴(yán)重性暴露的技巧
請(qǐng)遵循下列建議以避免高嚴(yán)重性靜態(tài)安全性暴露:
限制對(duì)變量的訪問
讓每個(gè)類和方法都成為 final,除非有足夠的理由不這樣做
不要依賴包作用域
使類不可克隆
使類不可序列化
使類不可逆序列化
避免硬編碼敏感數(shù)據(jù)
查找惡意代碼
限制對(duì)變量的訪問
如果將變量聲明為 public,那么外部代碼就可以操作該變量。這可能會(huì)導(dǎo)致安全性暴露。
影響
如果實(shí)例變量為 public,那么就可以在類實(shí)例上直接訪問和操作該實(shí)例變量。將實(shí)例變量聲明為 protected 并不一定能解決這一問題:雖然不可能直接在類實(shí)例基礎(chǔ)上訪問這樣的變量,但仍然可以從派生類訪問這個(gè)變量。
清單 1 演示了帶有 public 變量的代碼,因?yàn)樽兞繛?public 的,所以它暴露了。
清單 1. 帶有 public 變量的代碼
class Test {
public int id;
protected String name;
Test(){
id = 1;
name = "hello world";
}
//code
}
public class MyClass extends Test{
public void methodIllegalSet(String name){
this.name = name; // this should not be allowed
}
public static void main(String[] args){
Test obj = new Test();
obj.id = 123; // this should not be allowed
MyClass mc = new MyClass();
mc.methodIllegalSet("Illegal Set Value");
}
}
建議
一般來說,應(yīng)該使用取值方法而不是 public 變量。按照具體問題具體對(duì)待的原則,在確定哪些變量特別重要因而應(yīng)該聲明為 private 時(shí),請(qǐng)將編碼的方便程度及成本同安全性需要加以比較。清單 2 演示了以下列方式來使之安全的代碼:
清單 2. 不帶有 public 變量的代碼
class Test {
private int id;
private String name;
Test(){
id = 1;
name = "hello world";
}
public void setId(int id){
this.id = id;
}
public void setName(String name){
this.name = name;
}
public int getId(){
return id;
}
public String getName(){
return name;
}
}
讓每個(gè)類和方法都為 final
不允許擴(kuò)展的類和方法應(yīng)該聲明為 final。這樣做防止了系統(tǒng)外的代碼擴(kuò)展類并修改類的行為。
影響
僅僅將類聲明為非 public 并不能防止攻擊者擴(kuò)展類,因?yàn)槿匀豢梢詮乃约旱陌鼉?nèi)訪問該類。
建議
讓每個(gè)類和方法都成為 final,除非有足夠的理由不這樣做。按此建議,我們要求您放棄可擴(kuò)展性,雖然它是使用諸如 Java 語言之類的面向?qū)ο笳Z言的主要優(yōu)點(diǎn)之一。在試圖提供安全性時(shí),可擴(kuò)展性卻成了您的敵人;可擴(kuò)展性只會(huì)為攻擊者提供更多給您帶來麻煩的方法。
不要依賴包作用域
沒有顯式地標(biāo)注為 public、private 或 protected 的類、方法和變量在它們自己的包內(nèi)是可訪問的。
影響
如果 Java 包不是封閉的,那么攻擊者就可以向包內(nèi)引入新類并使用該新類來訪問您想保護(hù)的內(nèi)容。諸如 java.lang 之類的一些包缺省是封閉的,一些 JVM 也讓您封閉自己的包。然而,您最好假定包是不封閉的。
建議
從軟件工程觀點(diǎn)來看,包作用域具有重要意義,因?yàn)樗梢宰柚箤?duì)您想隱藏的內(nèi)容進(jìn)行偶然的、無意中的訪問。但不要依靠它來獲取安全性。應(yīng)該將類、方法和變量顯式標(biāo)注為 public、private 或 protected 中適合您特定需求的那種。
使類不可克隆
克隆允許繞過構(gòu)造器而輕易地復(fù)制類實(shí)例。
影響
即使您沒有有意使類可克隆,外部源仍然可以定義您的類的子類,并使該子類實(shí)現(xiàn) java.lang.Cloneable。這就讓攻擊者創(chuàng)建了您的類的新實(shí)例??截惉F(xiàn)有對(duì)象的內(nèi)存映象生成了新的實(shí)例;雖然這樣做有時(shí)候是生成新對(duì)象的可接受方法,但是大多數(shù)時(shí)候是不可接受的。清單 3 說明了因?yàn)榭煽寺《┞兜拇a:
清單 3. 可克隆代碼
class MyClass{
private int id;
private String name;
public MyClass(){
id=1;
name="HaryPorter";
}
public MyClass(int id,String name){
this.id=id;
this.name=name;
}
public void display(){
System.out.println("Id ="+id+"
"+"Name="+name);
}
}
// hackers code to clone the user class
public class Hacker extends MyClass implements Cloneable {
public static void main(String[] args){
Hacker hack=new Hacker();
try{
MyClass o=(MyClass)hack.clone();
o.display();
}
catch(CloneNotSupportedException e){
e.printStackTrace();
}
}
}
建議
要防止類被克隆,可以將清單 4 中所示的方法添加到您的類中:
清單 4. 使您的代碼不可克隆
public final Object clone()
throws java.lang.CloneNotSupportedException{
throw new java.lang.CloneNotSupportedException();
}
如果想讓您的類可克隆并且您已經(jīng)考慮了這一選擇的后果,那么您仍然可以保護(hù)您的類。要做到這一點(diǎn),請(qǐng)?jiān)谀念愔卸x一個(gè)為 final 的克隆方法,并讓它依賴于您的一個(gè)超類中的一個(gè)非 final 克隆方法,如清單 5 中所示:
清單 5. 以安全的方式使您的代碼可克隆
public final Object clone()
throws java.lang.CloneNotSupportedException {
super.clone();
}
類中出現(xiàn) clone() 方法防止攻擊者重新定義您的 clone 方法。
使類不可序列化
序列化允許將類實(shí)例中的數(shù)據(jù)保存在外部文件中。闖入代碼可以克隆或復(fù)制實(shí)例,然后對(duì)它進(jìn)行序列化。
影響
序列化是令人擔(dān)憂的,因?yàn)樗试S外部源獲取對(duì)您的對(duì)象的內(nèi)部狀態(tài)的控制。這一外部源可以將您的對(duì)象之一序列化成攻擊者隨后可以讀取的字節(jié)數(shù)組,這使得攻擊者可以完全審查您的對(duì)象的內(nèi)部狀態(tài),包括您標(biāo)記為 private 的任何字段。它也允許攻擊者訪問您引用的任何對(duì)象的內(nèi)部狀態(tài)。
建議
要防止類中的對(duì)象被序列化,請(qǐng)?jiān)陬愔卸x清單 6 中的 writeObject() 方法:
清單 6. 防止對(duì)象序列化
private final void writeObject(ObjectOutputStream out)
throws java.io.NotSerializableException {
throw new java.io.NotSerializableException("This object cannot
be serialized");
}
通過將 writeObject() 方法聲明為 final,防止了攻擊者覆蓋該方法。
使類不可逆序列化
通過使用逆序列化,攻擊者可以用外部數(shù)據(jù)或字節(jié)流來實(shí)例化類。
影響
不管類是否可以序列化,都可以對(duì)它進(jìn)行逆序列化。外部源可以創(chuàng)建逆序列化成類實(shí)例的字節(jié)序列。這種可能為您帶來了大量風(fēng)險(xiǎn),因?yàn)槟荒芸刂颇嫘蛄谢瘜?duì)象的狀態(tài)。請(qǐng)將逆序列化作為您的對(duì)象的另一種公共構(gòu)造器 — 一種您無法控制的構(gòu)造器。
建議
要防止對(duì)對(duì)象的逆序列化,應(yīng)該在您的類中定義清單 7 中的 readObject() 方法:
清單 7. 防止對(duì)象逆序列化
private final void readObject(ObjectInputStream in)
throws java.io.NotSerializableException {
throw new java.io.NotSerializableException("This object cannot
be deserialized");
}
通過將該方法聲明為 final,防止了攻擊者覆蓋該方法。
避免硬編碼敏感數(shù)據(jù)
您可能會(huì)嘗試將諸如加密密鑰之類的秘密存放在您的應(yīng)用程序或庫的代碼。對(duì)于你們開發(fā)人員來說,這樣做通常會(huì)把事情變得更簡(jiǎn)單。
影響
任何運(yùn)行您的代碼的人都可以完全訪問以這種方法存儲(chǔ)的秘密。沒有什么東西可以防止心懷叵測(cè)的程序員或虛擬機(jī)窺探您的代碼并了解其秘密。
建議
可以以一種只可被您解密的方式將秘密存儲(chǔ)在您代碼中。在這種情形下,秘密只在于您的代碼所使用的算法。這樣做沒有多大壞處,但不要洋洋得意,認(rèn)為這樣做提供了牢固的保護(hù)。您可以遮掩您的源代碼或字節(jié)碼 — 也就是,以一種為了解密必須知道加密格式的方法對(duì)源代碼或字節(jié)碼進(jìn)行加密 — 但攻擊者極有可能能夠推斷出加密格式,對(duì)遮掩的代碼進(jìn)行逆向工程從而揭露其秘密。
這一問題的一種可能解決方案是:將敏感數(shù)據(jù)保存在屬性文件中,無論什么時(shí)候需要這些數(shù)據(jù),都可以從該文件讀取。如果數(shù)據(jù)極其敏感,那么在訪問屬性文件時(shí),您的應(yīng)用程序應(yīng)該使用一些加密/解密技術(shù)。
查找惡意代碼
從事某個(gè)項(xiàng)目的某個(gè)心懷叵測(cè)的開發(fā)人員可能故意引入易受攻擊的代碼,打算日后利用它。這樣的代碼在初始化時(shí)可能會(huì)啟動(dòng)一個(gè)后臺(tái)進(jìn)程,該進(jìn)程可以為闖入者開后門。它也可以更改一些敏感數(shù)據(jù)。
這樣的惡意代碼有三類:
類中的 main 方法
定義過且未使用的方法
注釋中的死代碼
影響
入口點(diǎn)程序可能很危險(xiǎn)而且有惡意。通常,Java 開發(fā)人員往往在其類中編寫 main() 方法,這有助于測(cè)試單個(gè)類的功能。當(dāng)類從測(cè)試轉(zhuǎn)移到生產(chǎn)環(huán)境時(shí),帶有 main() 方法的類就成為了對(duì)應(yīng)用程序的潛在威脅,因?yàn)殛J入者將它們用作入口點(diǎn)。
請(qǐng)檢查代碼中是否有未使用的方法出現(xiàn)。這些方法在測(cè)試期間將會(huì)通過所有的安全檢查,因?yàn)樵诖a中不調(diào)用它們 — 但它們可能含有硬編碼在它們內(nèi)部的敏感數(shù)據(jù)(雖然是測(cè)試數(shù)據(jù))。引入一小段代碼的攻擊者隨后可能調(diào)用這樣的方法。
避免最終應(yīng)用程序中的死代碼(注釋內(nèi)的代碼)。如果闖入者去掉了對(duì)這樣的代碼的注釋,那么代碼可能會(huì)影響系統(tǒng)的功能性。
可以在清單 8 中看到所有三種類型的惡意代碼的示例:
清單 8. 潛在惡意的 Java 代碼
public void unusedMethod(){
// code written to harm the system
}
public void usedMethod(){
//unusedMethod(); //code in comment put with bad intentions,
//might affect the system if uncommented
// int x = 100;
// x=x+10; //Code in comment, might affect the
//functionality of the system if uncommented
}
建議
應(yīng)該將(除啟動(dòng)應(yīng)用程序的 main() 方法之外的)main() 方法、未使用的方法以及死代碼從應(yīng)用程序代碼中除去。在軟件交付使用之前,主要開發(fā)人員應(yīng)該對(duì)敏感應(yīng)用程序進(jìn)行一次全面的代碼評(píng)審。應(yīng)該使用“Stub”或“dummy”類代替 main() 方法以測(cè)試應(yīng)用程序的功能。
對(duì)付中等嚴(yán)重性暴露的技巧
請(qǐng)遵循下列建議以避免中等嚴(yán)重性靜態(tài)安全性暴露:
不要依賴初始化
不要通過名稱來比較類
不要使用內(nèi)部類
不要依賴初始化
您可以不運(yùn)行構(gòu)造器而分配對(duì)象。這些對(duì)象使用起來不安全,因?yàn)樗鼈儾皇峭ㄟ^構(gòu)造器初始化的。
影響
在初始化時(shí)驗(yàn)證對(duì)象確保了數(shù)據(jù)的完整性。
例如,請(qǐng)想象為客戶創(chuàng)建新帳戶的 Account 對(duì)象。只有在 Account 期初余額大于 0 時(shí),才可以開設(shè)新帳戶??梢栽跇?gòu)造器里執(zhí)行這樣的驗(yàn)證。有些人未執(zhí)行構(gòu)造器而創(chuàng)建 Account 對(duì)象,他可能創(chuàng)建了一個(gè)具有一些負(fù)值的新帳戶,這樣會(huì)使系統(tǒng)不一致,容易受到進(jìn)一步的干預(yù)。
建議
在使用對(duì)象之前,請(qǐng)檢查對(duì)象的初始化過程。要做到這一點(diǎn),每個(gè)類都應(yīng)該有一個(gè)在構(gòu)造器中設(shè)置的私有布爾標(biāo)志,如清單 9 中的類所示。在每個(gè)非 static 方法中,代碼在任何進(jìn)一步執(zhí)行之前都應(yīng)該檢查該標(biāo)志的值。如果該標(biāo)志的值為 true,那么控制應(yīng)該進(jìn)一步繼續(xù);否則,控制應(yīng)該拋出一個(gè)例外并停止執(zhí)行。那些從構(gòu)造器調(diào)用的方法將不會(huì)檢查初始化的變量,因?yàn)樵谡{(diào)用方法時(shí)沒有設(shè)置標(biāo)志。因?yàn)檫@些方法并不檢查標(biāo)志,所以應(yīng)該將它們聲明為 private 以防止用戶直接訪問它們。
清單 9. 使用布爾標(biāo)志以檢查初始化過程
public class MyClass{
private boolean initialized = false;
//Other variables
public MyClass (){
//variable initialization
method1();
initialized = true;
}
private void method1(){ //no need to check for initialization variable
//code
}
public void method2(){
try{
if(initialized==true){
//proceed with the business logic
}
else{
throw new Exception("Illegal State Of the object");
}
}catch(Exception e){
e.printStackTrace();
}
}
}
如果對(duì)象由逆序列化進(jìn)行初始化,那么上面討論的驗(yàn)證機(jī)制將難以奏效,因?yàn)樵谠撨^程中并不調(diào)用構(gòu)造器。在這種情況下,類應(yīng)該實(shí)現(xiàn) ObjectInputValidation 接口:
清單 10. 實(shí)現(xiàn) ObjectInputValidation
interface java.io.ObjectInputValidation {
public void validateObject() throws InvalidObjectException;
}
所有驗(yàn)證都應(yīng)該在 validateObject() 方法中執(zhí)行。對(duì)象還必須調(diào)用 ObjectInputStream.RegisterValidation() 方法以為逆序列化對(duì)象之后的驗(yàn)證進(jìn)行注冊(cè)。 RegisterValidation() 的第一個(gè)參數(shù)是實(shí)現(xiàn) validateObject() 的對(duì)象,通常是對(duì)對(duì)象自身的引用。注:任何實(shí)現(xiàn) validateObject() 的對(duì)象都可能充當(dāng)對(duì)象驗(yàn)證器,但對(duì)象通常驗(yàn)證它自己對(duì)其它對(duì)象的引用。RegisterValidation() 的第二個(gè)參數(shù)是一個(gè)確定回調(diào)順序的整數(shù)優(yōu)先級(jí),優(yōu)先級(jí)數(shù)字大的比優(yōu)先級(jí)數(shù)字小的先回調(diào)。同一優(yōu)先級(jí)內(nèi)的回調(diào)順序則不確定。
當(dāng)對(duì)象已逆序列化時(shí),ObjectInputStream 按照從高到低的優(yōu)先級(jí)順序調(diào)用每個(gè)已注冊(cè)對(duì)象上的 validateObject()。
不要通過名稱來比較類
有時(shí)候,您可能需要比較兩個(gè)對(duì)象的類,以確定它們是否相同;或者,您可能想看看某個(gè)對(duì)象是否是某個(gè)特定類的實(shí)例。因?yàn)?JVM 可能包括多個(gè)具有相同名稱的類(具有相同名稱但卻在不同包內(nèi)的類),所以您不應(yīng)該根據(jù)名稱來比較類。
影響
如果根據(jù)名稱來比較類,您可能無意中將您不希望授予別人的權(quán)利授予了闖入者的類,因?yàn)殛J入者可以定義與您的類同名的類。
例如,請(qǐng)假設(shè)您想確定某個(gè)對(duì)象是否是類 com.bar.Foo 的實(shí)例。清單 11 演示了完成這一任務(wù)的錯(cuò)誤方法:
清單 11. 比較類的錯(cuò)誤方法
if(obj.getClass().getName().equals("Foo")) // Wrong!
// objects class is named Foo
}else{
// object's class has some other name
}
建議
在那些非得根據(jù)名稱來比較類的情況下,您必須格外小心,必須確保使用了當(dāng)前類的 ClassLoader 的當(dāng)前名稱空間,如清單 12 中所示:
清單 12. 比較類的更好方法
if(obj.getClass() == this.getClassLoader().loadClass("com.bar.Foo")){
// object's class is equal to
//the class that this class calls "com.bar.Foo"
}else{
// object's class is not equal to the class that
// this class calls "com.bar.Foo"
}
然而,比較類的更好方法是直接比較類對(duì)象看它們是否相等。例如,如果您想確定兩個(gè)對(duì)象 a 和 b 是否屬同一個(gè)類,那么您就應(yīng)該使用清單 13 中的代碼:
清單 13. 直接比較對(duì)象來看它們是否相等
if(a.getClass() == b.getClass()){
// objects have the same class
}else{
// objects have different classes
}
盡可能少用直接名稱比較。
不要使用內(nèi)部類
Java 字節(jié)碼沒有內(nèi)部類的概念,因?yàn)榫幾g器將內(nèi)部類轉(zhuǎn)換成了普通類,而如果沒有將內(nèi)部類聲明為 private,則同一個(gè)包內(nèi)的任何代碼恰好能訪問該普通類。
影響
因?yàn)橛羞@一特性,所以包內(nèi)的惡意代碼可以訪問這些內(nèi)部類。如果內(nèi)部類能夠訪問括起外部類的字段,那么情況會(huì)變得更糟??赡芤呀?jīng)將這些字段聲明為 private,這樣內(nèi)部類就被轉(zhuǎn)換成了獨(dú)立類,但當(dāng)內(nèi)部類訪問外部類的字段時(shí),編譯器就將這些字段從專用(private)的變?yōu)樵诎╬ackage)的作用域內(nèi)有效的。內(nèi)部類暴露了已經(jīng)夠糟糕的了,但更糟糕的是編譯器使您將某些字段成為 private 的舉動(dòng)成為徒勞。
建議 如果能夠不使用內(nèi)部類就不要使用內(nèi)部類。
對(duì)付低嚴(yán)重性暴露的技巧
請(qǐng)遵循下列建議以避免低嚴(yán)重性靜態(tài)安全性暴露:
避免返回可變對(duì)象
檢查本機(jī)方法
避免返回可變對(duì)象
Java 方法返回對(duì)象引用的副本。如果實(shí)際對(duì)象是可改變的,那么使用這樣一個(gè)引用調(diào)用程序可能會(huì)改變它的內(nèi)容,通常這是我們所不希望見到的。
影響
請(qǐng)考慮這個(gè)示例:某個(gè)方法返回一個(gè)對(duì)敏感對(duì)象的內(nèi)部數(shù)組的引用,假定該方法的調(diào)用程序不改變這些對(duì)象。即使數(shù)組對(duì)象本身是不可改變的,也可以在數(shù)組對(duì)象以外操作數(shù)組的內(nèi)容,這種操作將反映在返回該數(shù)組的對(duì)象中。如果該方法返回可改變的對(duì)象,那么事情會(huì)變得更糟;外部實(shí)體可以改變?cè)谀莻€(gè)類中聲明的 public 變量,這種改變將反映在實(shí)際對(duì)象中。
清單 14 演示了脆弱性。getExposedObj() 方法返回了 Exposed 對(duì)象的引用副本,該對(duì)象是可變的:
清單 14. 返回可變對(duì)象的引用副本
class Exposed{
private int id;
private String name;
public Exposed(){
}
public Exposed(int id, String name){
this.id = id;
this.name = name;
}
public int getId(){
return id;
}
public String getName(){
return name;
}
public void setId(int id){
this.id=id;
}
public void setName(String name){
this.name = name;
}
public void display(){
System.out.println("Id = "+ id + " Name = "+ name);
}
}
public class Exp12{
private Exposed exposedObj = new Exposed(1,"Harry Porter");
public Exposed getExposedObj(){
return exposedObj; //returns a reference to the object.
}
public static void main(String[] args){
Exp12 exp12 = new Exp12();
exp12.getExposedObj().display();
Exposed exposed = exp12.getExposedObj();
exposed.setId(10);
exposed.setName("Hacker");
exp12.getExposedObj().display();
}
}
建議
如果方法返回可改變的對(duì)象,但又不希望調(diào)用程序改變?cè)搶?duì)象,請(qǐng)修改該方法使之不返回實(shí)際對(duì)象而是返回它的副本或克隆。要改正清單 14 中的代碼,請(qǐng)讓它返回 Exposed 對(duì)象的副本,如清單 15 中所示:
清單 15. 返回可變對(duì)象的副本
public Exposed getExposedObj(){
return new Exposed(exposedObj.getId(),exposedObj.getName());
}
或者,您的代碼也可以返回 Exposed 對(duì)象的克隆。
檢查本機(jī)方法
本機(jī)方法是一種 Java 方法,其實(shí)現(xiàn)是用另一種編程語言編寫的,如 C 或 C++。有些開發(fā)人員實(shí)現(xiàn)本機(jī)方法,這是因?yàn)?Java 語言即使使用即時(shí)(just-in-time)編譯器也比許多編譯過的語言要慢。其它人需要使用本機(jī)代碼是為了在 JVM 以外實(shí)現(xiàn)特定于平臺(tái)的功能。
影響
使用本機(jī)代碼時(shí),請(qǐng)小心,因?yàn)閷?duì)這些代碼進(jìn)行驗(yàn)證是不可能的,而且本機(jī)代碼可能潛在地允許 applet 繞過通常的安全性管理器(Security Manager)和 Java 對(duì)設(shè)備訪問的控制。
建議
如果非得使用本機(jī)方法,那么請(qǐng)檢查這些方法以確定:
它們返回什么
它們獲取什么作為參數(shù)
它們是否繞過安全性檢查
它們是否是 public、private 等等
它們是否含有繞過包邊界從而繞過包保護(hù)的方法調(diào)用
結(jié)束語
編寫安全 Java 代碼是十分困難的,但本文描述了一些可行的實(shí)踐來幫您編寫安全 Java 代碼。這些建議并不能解決您的所有安全性問題,但它們將減少暴露數(shù)目。最佳軟件安全性實(shí)踐可以幫助確保軟件正常運(yùn)行。安全至關(guān)重要和高可靠系統(tǒng)設(shè)計(jì)者總是花費(fèi)大量精力來分析和跟蹤軟件行為。只有通過將安全性作為至關(guān)緊要的系統(tǒng)特性來對(duì)待 — 并且從一開始就將它構(gòu)建到應(yīng)用程序中,我們才可以避免亡羊補(bǔ)牢似的、修修補(bǔ)補(bǔ)的安全性方法。
參考資料
請(qǐng)通過單擊文章頂部或底部的討論來參加本文的論壇。
了解關(guān)于 Java 安全性 API 的更多知識(shí)。
developerWorks 安全專題上通常含有有關(guān)計(jì)算機(jī)安全性的優(yōu)秀資源。
Larry Koved、 Anthony J. Nadalin、Don Neal 和 Tim Lawson 合作編寫的 “The evolution of Java security”(developerWorks,1998 年)對(duì) Java 語言的安全性模型早期開發(fā)進(jìn)行了深入探討。
Sing Li 在他的 Java 安全性系列文章(由兩部分組成的)(developerWorks, 2001 年 2 月)中向開發(fā)人員顯示:盡管社區(qū)可能不得不重新考慮 Java 2 中的安全性設(shè)計(jì),還是出現(xiàn)了只對(duì)開發(fā)人員有幫助,可以滿足他們的需求的一致的進(jìn)展:
第一部分
第二部分
John Viega、Tom Mutdosch、 Gary McGraw 和 Ed Felten 合著的 “Statically scanning Java code for security vulnerabilities” (IEEE Software,2000 年 9 月)介紹了一種 Java 工具,可以使用該工具來檢查您的 Java 代碼中的安全性漏洞。
G. McGraw 和 E. Felten 合作編寫的 Securing Java: Getting Down to Business with Mobile Code(John Wiley 和 Sons,1998 年)深入涵蓋了 Java 安全性。(文檔是 PDF 格式的。)
定期檢查 IBM 研究 Java 安全頁面以便 IBM 在安全性領(lǐng)域的創(chuàng)新有重要發(fā)展時(shí)能夠跟蹤這一創(chuàng)新。
如果您的 Java 代碼運(yùn)行在 S/390 系統(tǒng)上,那么您將需要查閱 S/390 Java 安全頁面以獲取額外的信息。
關(guān)于作者
Bijaya Nanda Sahu 是就職于印度 IBM Global Services 的軟件工程師。他從事過各種因特網(wǎng)技術(shù)和框架(J2EE、WSBCC、JADE)、 WebSphere 相關(guān)技術(shù)、UML 和 OOAD 方面的工作。目前,他從事因特網(wǎng)銀行安全性問題方面的工作,重點(diǎn)在 WebSphere Application Server 和 Portal Server 上??梢酝ㄟ^ bijaya.sahu@in.ibm.com 和他聯(lián)系
》類裝載器結(jié)構(gòu)
(class
loader)
》class文件檢查器
(the
class
file
verifier)
》內(nèi)置于Java虛擬機(jī)(及語言)的安全特性
》安全管理器及Java
API
(security
manager)
在Java沙箱中,類裝載器體系結(jié)構(gòu)是第一道防線。它在三個(gè)方面對(duì)Java的沙箱起作用:
1它防止惡意代碼區(qū)干涉善意的代碼
2它守護(hù)了被信任的代碼的邊界
3它將代碼歸于某類(稱為保護(hù)域),該類確定了代碼可以進(jìn)行哪種操作
Class文件檢查器:
Class文件檢查器保證裝載的class文件內(nèi)容有正確的內(nèi)部結(jié)構(gòu),并且這些class文件互相間協(xié)調(diào)一致。Class文件檢查器實(shí)現(xiàn)的安全目標(biāo)之一就是程序的健壯性。如果某個(gè)有漏洞的編譯器,或某個(gè)聰明的黑客,產(chǎn)生了一個(gè)class文件,而這個(gè)class文件中包含了一個(gè)方法,則合格方法的字節(jié)碼中含有一條跳轉(zhuǎn)到方法之外的指令,那么,一旦這個(gè)方法被調(diào)用,它將導(dǎo)致虛擬機(jī)的崩潰,所以,處于對(duì)健壯性的考慮,由虛擬機(jī)檢驗(yàn)它裝載的字節(jié)碼的完整性非常重要。
Class文件檢查器要進(jìn)行四趟獨(dú)立的掃描來完成它的操作。
第一趟:Class文件的結(jié)構(gòu)檢查
在這一趟掃描中,對(duì)每一段將被當(dāng)做類型導(dǎo)入的字節(jié)序列,Class文件檢查器都會(huì)確認(rèn)它是否符合JavaClass文件的節(jié)本結(jié)構(gòu)。在這一趟檢查中檢查器會(huì)進(jìn)行很多檢查例如:每個(gè)Class文件都必須以四個(gè)同樣的字節(jié)開始:0xCAFEBABE。因?yàn)檫@個(gè)魔數(shù)Class文件分析器會(huì)很容易判斷出某個(gè)文件具有明顯問題而加以拒絕。檢查器還必須確認(rèn)在Class文件中聲明的版本號(hào)和次版本號(hào),這個(gè)版本號(hào)必須在這個(gè)虛擬機(jī)實(shí)現(xiàn)可以支持的范圍之內(nèi)。而且第一趟掃描還必須確認(rèn)這個(gè)Class文件有沒有被刪減。總之第一趟掃描的目的就是保證這個(gè)字節(jié)序列正確的定義了一個(gè)新類型。
第二趟:類型數(shù)據(jù)的語義檢查
第二趟掃描,檢查器要查看每個(gè)組成部分,確認(rèn)它們是否是其所屬類型的實(shí)例,他們的結(jié)構(gòu)是否正確。另外還要檢查這個(gè)類本身是否符合特定的條件,它們是由Java編程語言規(guī)定的。例如,檢查器強(qiáng)制規(guī)定除Object類以外的類必須有一個(gè)超類,或者檢查final類有沒有被子化等。
第三趟:字節(jié)碼驗(yàn)證
這一趟是要確保采用任何路徑在字節(jié)碼流中都得到一個(gè)確定的操作碼,確保操作數(shù)??偸前_的數(shù)值以及正確的類型。
第四趟:符號(hào)引用的驗(yàn)證
在動(dòng)態(tài)鏈接的過程中,如果包含在一個(gè)Class文件中的符號(hào)引用被解析時(shí),Class文件檢查器要進(jìn)行第四趟檢查。第四趟掃描僅僅是動(dòng)態(tài)鏈接過程的一部分。當(dāng)一個(gè)Class文件被裝載時(shí),它包含了對(duì)其他類的符號(hào)引用以及它們的字段和方法。一個(gè)符號(hào)引用是一個(gè)字符串,它給出了名字,并且可能還包含了其他關(guān)于這個(gè)被引用項(xiàng)的信息------這些信息必須足以唯一的識(shí)別一個(gè)類、方法、字段。這樣對(duì)于其他類的符號(hào)引用必須給出這個(gè)類的全名;對(duì)于其他類的字段的符號(hào)引用必須給出類名、字段名以及字段描述符;對(duì)于其他類中的方法的引用必須給出類名、方法名以及方法的描述符。
所謂的動(dòng)態(tài)鏈接是一個(gè)將符號(hào)引用解析為直接引用的過程。
此外,由于Java程序是動(dòng)態(tài)鏈接的,所以Class文件檢查器在進(jìn)行第四次掃描中,必須檢查相互引用類之間的兼容性。
除此之外,Java虛擬機(jī)還有一些內(nèi)置的安全特性:
》類型安全的引用轉(zhuǎn)換
》結(jié)構(gòu)化的內(nèi)存訪問
》自動(dòng)垃圾收集(不必顯式地釋放被分配的內(nèi)存)
》空引用檢查
通過保證一個(gè)Java程序只能使用類型安全的、結(jié)構(gòu)化的方法去訪問內(nèi)存,Java虛擬機(jī)使得Java程序更為健壯。