背景:最近公司生產(chǎn)環(huán)境通過堡壘機(jī)對(duì)數(shù)據(jù)庫(kù)進(jìn)行接管。通過堡壘機(jī)配置PL/SQL工具,啟動(dòng)后會(huì)自動(dòng)進(jìn)行用戶名密碼填充。這樣數(shù)據(jù)庫(kù)密碼就不需要暴露給運(yùn)維人員,而且可以通過堡壘機(jī)進(jìn)行日志審計(jì)。·
為了驗(yàn)證堡壘機(jī)是通過認(rèn)證模式進(jìn)行連接還是通過填充密碼的模式進(jìn)行連接,先將PL/SQL的密碼保存打開,然后通過堡壘機(jī)進(jìn)行連接。連接成功后關(guān)閉PL/SQL并退出堡壘機(jī)。下次直接通過選擇記住的用戶進(jìn)行登錄,發(fā)現(xiàn)可以直接登錄成功。說明,我司使用的堡壘機(jī)只是對(duì)用戶密碼,進(jìn)行填充,并非采用認(rèn)證轉(zhuǎn)發(fā)的模式。
如何設(shè)置PL/SQL密碼保存?
打開PL/SQL,依次進(jìn)入目錄:工具->首選項(xiàng)->登錄歷史;
勾選存儲(chǔ)歷史及帶密碼存儲(chǔ),點(diǎn)擊已確認(rèn),退出PL/SQL重新登錄。
成功登錄后會(huì)自動(dòng)保存密碼,下次登錄時(shí)可以點(diǎn)擊用戶名右邊...按鈕,通過下拉的用戶列表進(jìn)行選擇,選擇后PL/SQL會(huì)自動(dòng)登錄,不需要再輸入密碼。PL/SQL默認(rèn)存儲(chǔ)登錄信息,不存儲(chǔ)密碼。
如何解密PL/SQL保存的密碼?
數(shù)據(jù)
要想解密PL/SQL保存的密碼,首先,我們需要知道密碼存儲(chǔ)位置。PL/SQL將登錄信息及密碼存儲(chǔ)在user.prefs下。該文件一般存儲(chǔ)在如下位置。其中<username>是你的用戶名。
C:\Users<username>\AppData\Roaming\PLSQL Developer\Preferences<username>\
C:\Program Files\PLSQL Developer\Preferences<username>\
C:\Program Files (x86)\PLSQL Developer\Preferences<username>\
本人使用綠色版的PLSQL,存儲(chǔ)位置為:C:\Users\Administrator\AppData\Roaming\PLSQL Developer\Preferences\Administrator 打開user.prefs文件,其中標(biāo)題[LogonHistory]下面存在多行數(shù)字字符串,每一行即為一個(gè)登錄信息,這些信息是加密的,其明文格式如下:
<username>/<password>@<server>
算法
加密算法非常簡(jiǎn)單,主要由位移和xor組成 。生成的密文看起來像這樣:
273645624572423045763066456443024120413041724566408044424900419043284194407643904160
第一組四位數(shù)(2736)是密鑰值-他是有系統(tǒng)運(yùn)行時(shí)隨機(jī)生成的四位數(shù)(大于2000,小于3000),后面每一位字符根據(jù)下面算法進(jìn)行運(yùn)算得到4位數(shù)字,直至加密完畢。
下面是JAVA實(shí)現(xiàn)的加密代碼:
public static void cryptPassword() {
int key = 2736;
String plaintext = "user/password@server";
String result = Integer.toString(key);
for (int i = 0; i < plaintext.length(); i++) {
int mask = Integer.valueOf(plaintext.charAt(i)) << 4;
int n = (mask ^ (key + (i + 1) * 10)) + 1000;
result += Integer.toString(n);
}
System.out.println(result);
}
user/password@server加密結(jié)果為:
273645624572423045763066456443024120413041724566408044424900419043284194407643904160
下面是JAVA實(shí)現(xiàn)的解密代碼:
public static void decryptPassword(){
String cryptText = "273645624572423045763066456443024120413041724566408044424900419043284194407643904160";
String plainText = "";
ArrayList<Integer> values = new ArrayList<Integer>();
for (int i = 0; i < cryptText.length(); i+= 4){
values.add(Integer.parseInt((cryptText.substring(i, i + 4))));
}
int key = values.get(0);
values.remove(0);
for (int i = 0; i < values.size(); i++) {
int val = values.get(i) - 1000;
int mask = key + (10 * (i + 1));
int res = (val ^ mask) >> 4;
plainText += Character.toString((char)(res));
}
System.out.println("plainText:"+plainText);
}
注意:生產(chǎn)環(huán)境切記不要開啟保存密碼的選項(xiàng),否則本地環(huán)境一旦被人獲取,生產(chǎn)密碼將直接暴露,竊取者甚至不需要進(jìn)行解密,直接將密文拷貝至PLSQL對(duì)應(yīng)位置即可直接登錄。
通過上面的破解過程,說明這種模式的數(shù)據(jù)庫(kù)接管沒有實(shí)際意義,并不能規(guī)避密碼在內(nèi)部泄露的問題。
參考資料:https://adamcaudill.com/2016/02/02/plsql-developer-nonexistent-encryption/