虛擬存儲(chǔ)涉及到的相關(guān)基礎(chǔ)知識(shí)總結(jié) 1
737
2025-03-31
簡(jiǎn)介
作為一個(gè)程序員,只是寫(xiě)出好用的代碼是不夠的,我們還需要考慮到程序的安全性。在這個(gè)不能跟陌生人說(shuō)話世界,扶老奶奶過(guò)馬路都是一件很困難的事情。那么對(duì)于程序員來(lái)說(shuō),尤其是對(duì)于開(kāi)發(fā)那種對(duì)外可以公開(kāi)訪問(wèn)的網(wǎng)站的程序員,要承受的壓力會(huì)大很多。
任何人都可以訪問(wèn)我們的系統(tǒng),也就意味著如果我們的系統(tǒng)不夠健壯,或者有些漏洞,惡意攻擊者就會(huì)破門(mén)而入,將我們辛辛苦苦寫(xiě)的程序蹂躪的體無(wú)完膚。
所以,安全很重要,今天本文將會(huì)探討一下java中的安全編碼指南。
java平臺(tái)本身的安全性
作為一個(gè)強(qiáng)類型語(yǔ)言,java平臺(tái)本身已經(jīng)盡可能的考慮到了安全性的,為我們屏蔽了大多數(shù)安全性的細(xì)節(jié)。
比如可以為不同級(jí)別權(quán)限的代碼提供受限的執(zhí)行環(huán)境。 java程序是類型安全的,并且在運(yùn)行時(shí)提供了自動(dòng)內(nèi)存管理和數(shù)組邊界檢查,Java會(huì)盡可能的及早發(fā)現(xiàn)程序中的問(wèn)題,從而使Java程序具有很高的抵抗堆棧破壞的能力。
盡管Java安全體系結(jié)構(gòu)在許多情況下可以幫助保護(hù)用戶和系統(tǒng)免受惡意代碼或行為不當(dāng)?shù)墓簦鼰o(wú)法防御可信任代碼中發(fā)生的錯(cuò)誤。也就說(shuō)如果是用戶本身代碼的漏洞,java安全體系是無(wú)法進(jìn)行判斷的。
這些錯(cuò)誤可能會(huì)繞過(guò)java本身的安全體系結(jié)構(gòu)。在嚴(yán)重的情況下,可能會(huì)執(zhí)行本地程序或禁用Java安全性。從而會(huì)被用來(lái)從計(jì)算機(jī)和Intranet竊取機(jī)密數(shù)據(jù),濫用系統(tǒng)資源,阻止計(jì)算機(jī)的有用操作,協(xié)助進(jìn)一步的攻擊以及許多其他惡意活動(dòng)。
所以,最大的安全在程序員本身,不管外部機(jī)制如何強(qiáng)大,如果核心的程序員出了問(wèn)題,那么一切都將歸于虛無(wú)。
接下來(lái),我們看下java程序員應(yīng)該遵循一些什么行為準(zhǔn)則,來(lái)保證程序的安全性呢?
安全第一,不要寫(xiě)聰明的代碼
我們可能會(huì)在很多教科書(shū)甚至是JDK的源代碼中,看到很多讓人驚嘆的代碼寫(xiě)法,如果你真的真的明白你在做什么,那么這樣寫(xiě)沒(méi)什么問(wèn)題。但是很多情況下我們并不是很了解這樣寫(xiě)的原理,甚至不知道這樣寫(xiě)會(huì)出現(xiàn)什么樣的問(wèn)題。
并且現(xiàn)代系統(tǒng)是一個(gè)多人協(xié)作的過(guò)程,如果你寫(xiě)了這樣的聰明代碼,很有可能別人看不懂,最后導(dǎo)致未知的系統(tǒng)問(wèn)題。
給大家舉個(gè)例子:
:(){:|:&};:
上面是一個(gè)shell下面的fork炸彈,如果你在shell下面運(yùn)行上面的代碼,幾秒之后系統(tǒng)就會(huì)宕機(jī)或者運(yùn)行出錯(cuò)。
怎么分析上面的代碼呢?我們把代碼展開(kāi):
:()
{
:|:&
};
:
還是不明白? 我們把:替換成函數(shù)名:
fork()
{
fork|fork&
};
fork
上面的代碼就是無(wú)限的fork進(jìn)程,通過(guò)幾何級(jí)數(shù)的增長(zhǎng),最后導(dǎo)致程序崩潰。
java設(shè)計(jì)的很多大神把他們跳躍般的思想寫(xiě)到了JDK源代碼里面,大神們的思想經(jīng)過(guò)了千錘百煉,并且JDK是Java的核心,里面的代碼再優(yōu)化也不為過(guò)。
但是現(xiàn)在硬件技術(shù)的發(fā)展,代碼級(jí)別的優(yōu)化可能作用已經(jīng)比較少了。為了避免出現(xiàn)不可知的安全問(wèn)題,還是建議大家編寫(xiě)一眼就能看出邏輯的代碼。雖然可能不是那么快,但是安全性有了保證。除非你真的知道你在做什么。
在代碼設(shè)計(jì)之初就考慮安全性
安全性應(yīng)該是一個(gè)在編寫(xiě)代碼過(guò)程中非常重要的標(biāo)準(zhǔn),我們?cè)谠O(shè)計(jì)代碼的時(shí)候就應(yīng)該考慮到相關(guān)的安全性問(wèn)題,否則后面重構(gòu)起來(lái)會(huì)非常費(fèi)事。
舉個(gè)例子:
public final class SensitiveClass {
private final Behavior behavior;
// Hide constructor.
private SensitiveClass(Behavior behavior) {
this.behavior = behavior;
}
// Guarded construction.
public static SensitiveClass newSensitiveClass(Behavior behavior) {
// ... validate any arguments ...
// ... perform security checks ...
return new SensitiveClass(behavior);
}
}
上面的例子中我們使用了final關(guān)鍵字來(lái)防止我們的某些關(guān)鍵類被繼承擴(kuò)展。因?yàn)闆](méi)有擴(kuò)展性,所以安全性判斷會(huì)更加容易。
同時(shí),java提供了SecurityManager和一系列的Permission類,通過(guò)合理的配置,我們可以有效的控制java程序的訪問(wèn)權(quán)限。
避免重復(fù)的代碼
和重復(fù)代碼相關(guān)的一個(gè)關(guān)鍵詞就是重構(gòu)。為什么會(huì)出現(xiàn)重復(fù)代碼呢?
很簡(jiǎn)單,最開(kāi)始我們?cè)趯?shí)現(xiàn)一個(gè)功能的時(shí)候?qū)懥艘欢未a邏輯。結(jié)果后面還有一個(gè)方法要使用這段代碼邏輯。然后我們?yōu)榱藞D方便,就把代碼邏輯拷貝過(guò)去了。
看起來(lái)問(wèn)題好像解決了。但是一旦這段業(yè)務(wù)邏輯要修改,那可就是非常麻煩的一件事情。因?yàn)槲覀冃枰业匠绦蛑兴谐霈F(xiàn)這段代碼的地方,然后一個(gè)一個(gè)的修改。
為什么不把這段代碼提取出來(lái),做成一個(gè)單獨(dú)的方法來(lái)供其他的方法調(diào)用呢?這樣即使后面需要修改,也只用修改一處地方即可。
在現(xiàn)實(shí)的工作中,我們經(jīng)常會(huì)遇到這種問(wèn)題,尤其是那種年久失修的代碼,大家都不敢修改,因?yàn)闋恳话l(fā)而動(dòng)全身。往往是修改了這邊忘記了那邊,最后導(dǎo)致bug重重。
限制權(quán)限
JDK專門(mén)提供了一個(gè)SecurityManager類,來(lái)顯示的對(duì)安全性進(jìn)行控制,我們看下SecurityManager是怎么使用的:
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkXXX(argument, ...);
}
SecurityManager提供了一系列的check方法,來(lái)對(duì)權(quán)限進(jìn)行控制。
權(quán)限分為以下類別:文件、套接字、網(wǎng)絡(luò)、安全性、運(yùn)行時(shí)、屬性、AWT、反射和可序列化。管理各種權(quán)限類別的類是 :
java.io.FilePermission、
java.net.SocketPermission、
java.net.NetPermission、
java.security.SecurityPermission、
java.lang.RuntimePermission、
java.util.PropertyPermission、
java.awt.AWTPermission、
java.lang.reflect.ReflectPermission
java.io.SerializablePermission
JDK本身已經(jīng)使用了很多這些權(quán)限控制的代碼。比如說(shuō)我們最常用的File:
public boolean canRead() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return false;
}
return fs.checkAccess(this, FileSystem.ACCESS_READ);
}
上面是File類的canRead方法,我們會(huì)首先去判斷是否配置了SecurityManager,如果配置了,則去檢查是否可以read。
如果我們?cè)趯?xiě)代碼中,遇到文件、套接字、網(wǎng)絡(luò)、安全性、運(yùn)行時(shí)、屬性、AWT、反射和可序列化相關(guān)的操作時(shí),也可以考慮使用SecurityManager來(lái)進(jìn)行細(xì)粒度的權(quán)限控制。
構(gòu)建可信邊界
什么是可信邊界呢?邊界主要起攔截作用,邊界里邊的我們可以信任,邊界外邊的我們就不能信任了。
對(duì)于不能信任的外邊界請(qǐng)求,我們需要進(jìn)行足夠的安全訪問(wèn)控制。
比如說(shuō)web客戶端來(lái)訪問(wèn)web服務(wù)器。web客戶端是在全球各地的,各種環(huán)境都有,并且是不可控的,所以web客戶端訪問(wèn)web服務(wù)器端的請(qǐng)求需要進(jìn)行額外的安全控制。
而web服務(wù)器訪問(wèn)業(yè)務(wù)服務(wù)器又是不同的,因?yàn)閣eb服務(wù)器是我們自己控制的,所以安全程度相對(duì)較高,我們需要針對(duì)不同的可信邊界做不同的控制。
封裝
封裝(Encapsulation)是指一種將抽象性函式接口的實(shí)現(xiàn)細(xì)節(jié)部份包裝、隱藏起來(lái)的方法。
封裝可以被認(rèn)為是一個(gè)保護(hù)屏障,防止該類的代碼和數(shù)據(jù)被外部類定義的代碼隨機(jī)訪問(wèn)。通過(guò)對(duì)接口進(jìn)行訪問(wèn)控制,可以嚴(yán)格的包含類中的數(shù)據(jù)和方法。
并且封裝可以減少耦合,并且隱藏實(shí)現(xiàn)細(xì)節(jié)。
寫(xiě)文檔
最后一項(xiàng)也是非常非常重要的一項(xiàng)就是寫(xiě)文檔。為什么接別人的老項(xiàng)目那么痛苦,為什么讀源代碼那么困難。根本的原因就是沒(méi)有寫(xiě)文檔。
如果不寫(xiě)文檔,可能你自己寫(xiě)的代碼過(guò)一段時(shí)間之后也不知道為什么當(dāng)時(shí)這樣寫(xiě)了。
所以,寫(xiě)文檔很重要。
載自:程序那些事
Java 軟件開(kāi)發(fā)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。