在MySQL上使用帶密碼的GlassFish JDBC安全性

我在該博客上最成功的文章之一是有關在GlassFish上使用基于表單的身份驗證來建立JDBC安全領域的文章 。 對這篇文章的一些評論使我意識到,要真正使它安全,應該做的還很多。

開箱即用的安全性

圖片: TheKenChan ( CC BY-NC 2.0 )

GlassFish已經附帶了GlassFish JDBC領域 。 您所要做的就是初始化數據庫并正確獲得安全性配置,然后就可以完成。 在標準配置中,您可以選擇定義摘要算法(包括編碼和字符集)。 摘要算法可以是任何JDK支持的 MessageDigest(MD2,MD5,SHA-1,SHA-256,SHA-384,SHA-512)。 比較我的JDBC Security Realm帖子以獲得完整的設置。

什么是弱項或缺失項?

開箱即用的解決方案非常簡單。 它只是對密碼進行哈希處理。 有很多方法可以非常快速地從普通哈希中恢復密碼。 破解哈希的最簡單方法是嘗試猜測密碼,對每個猜測進行哈希處理,并檢查猜測的哈希是否等于被破解的哈希。 如果哈希值相等,則猜測為密碼。 猜測密碼的兩種最常見方式是字典攻擊和蠻力攻擊。 查找表也是眾所周知的。 它們是一種非常快速地破解許多相同類型哈希的有效方法。 總體思路是在密碼字典中預先計算密碼的哈希值,并將它們及其對應的密碼存儲在查找表數據結構中。 但是我們現在還沒有完成。 您還會發現稱為反向查找表的內容。 這種攻擊使攻擊者可以同時對多個散列應用字典或蠻力攻擊,而不必預先計算查找表。 最后但并非最不重要的彩虹表攻擊。 它們就像查找表,只不過它們犧牲了哈希破解速度以使查找表更小。 令人印象深刻的方法列表。 顯然,這不能滿足我個人對密碼保護的需求。

加一些鹽

上述方法之所以有效,是因為每個密碼都以完全相同的方式進行哈希處理。 每次通過安全哈希函數運行密碼時,都會產生完全相同的輸出。 防止這種情況的一種方法是在其中添加一些鹽。 在對哈希進行哈希運算之前,在密碼前添加或添加隨機字符串即可解決此問題。 該隨機字符串稱為“鹽”。 請注意,對于所有密碼重用salt并不安全。 您仍然可以使用彩虹表或字典攻擊來破解它們。 因此,您必須為每個密碼隨機分配鹽,并將其存儲在哈希密碼旁邊。 每次用戶更新密碼時,它都需要更改。 關于長度的簡短句子。 鹽不要太短。 對于最有效的長度,其長度將與密碼哈希相同。 如果使用SHA512(512/8位= 64字節),則應選擇長度至少為64個隨機字節的鹽。

準備工作

我們現在顯然已經離開了標準的JDBCRealm功能。 這意味著我們必須實現自己的安全領域。 從現在開始,我們將其稱為UserRealm。 讓我們從與JDBCRealm相同的設置開始。 具有“ jdbcrealmdb”架構的MySQL數據庫。 唯一的區別是,我們準備使用每個密碼來保存鹽。

USE jdbcrealmdb;
CREATE TABLE `jdbcrealmdb`.`users` (
`username` varchar(255) NOT NULL,
`salt` varchar(255) NOT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `jdbcrealmdb`.`groups` (
`username` varchar(255) DEFAULT NULL,
`groupname` varchar(255) DEFAULT NULL)
ENGINE=InnoDB DEFAULT CHARSET=utf8; 
CREATE INDEX groups_users_FK1 ON groups(username ASC);

現在,我們實現了基本領域。 以下代碼僅顯示了強制成員。 我將在接下來的幾天中提供該資源。 直到今天,這篇文章仍然可供您使用。

public class UserRealm extends AppservRealm {
/**
* Init realm from properties
*/
protected void init(Properties props) 
/**
* Get JAASContext
*/
public String getJAASContext() 
/**
* Get AuthType
*/
public String getAuthType() 
/**
* Get DB Connection
*/
private Connection getConnection()
/**
* Close Connection
*/
private void closeConnection(Connection cn)
/** 
* Close prepared statement
*/
private void closeStatement(PreparedStatement st)
/** 
* Make the compiler happy.
*/
public Enumeration getGroupNames(String string)
/** 
* Authenticate the user
*/
public String[] authenticate(String userId, String password) 
}

但是最重??要的部分在這里丟失了。

設置一些測試

我不是那種受測試驅動的人,但在這種情況下,這確實有意義。 因為我將在此處實現的領域不支持通過GlassFish管理控制臺進行用戶管理。 因此,基本要求是要有一個準備好的數據庫,其中包含所有用戶,密碼和鹽。 我們走吧。 添加sql-maven-plugin,并使其在測試編譯階段創建表。

 <plugin><groupId>org.codehaus.mojo</groupId><artifactId>sql-maven-plugin</artifactId><version>1.3</version><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.18</version></dependency></dependencies><configuration><driver>${driver}</driver><url>${url}</url><username>${username}</username><password>${password}</password><skip>${maven.test.skip}</skip><srcFiles><srcFile>src/test/data/drop-and-create-table.sql</srcFile></srcFiles></configuration><executions><execution><id>create-table</id><phase>test-compile</phase><goals><goal>execute</goal></goals></execution></executions></plugin>

您可以使用一些db-unit magic將測試數據插入數據庫中,也可以在測試用例中執行此操作。 我決定走這條路。 首先,讓我們將所有相關的JDBC內容放到一個稱為SecurityStore的單獨位置。 我們基本上需要三種方法。 添加一個用戶,為該用戶添加鹽并驗證該用戶。

 private final static String ADD_USER = "INSERT INTO users VALUES(?,?,?);";private final static String SALT_FOR_USER = "SELECT salt FROM users u WHERE username = ?;";private final static String VERIFY_USER = "SELECT username FROM users u WHERE username = ? AND password = ?;";
//...
public void addUser(String name, String salt, String password) {try {PreparedStatement pstm = con.prepareStatement(ADD_USER);pstm.setString(1, name);pstm.setString(2, salt);pstm.setString(3, password);pstm.executeUpdate();} catch (SQLException ex) {LOGGER.log(Level.SEVERE, "Create User failed!", ex);}}public String getSaltForUser(String name) {String salt = null;try {PreparedStatement pstm = con.prepareStatement(SALT_FOR_USER);pstm.setString(1, name);ResultSet rs = pstm.executeQuery();if (rs.next()) {salt = rs.getString(1);}} catch (SQLException ex) {LOGGER.log(Level.SEVERE, "User not found!", ex);}return salt;}public boolean validateUser(String name, String password) {try {PreparedStatement pstm = con.prepareStatement(VERIFY_USER);pstm.setString(1, name);pstm.setString(2, password);ResultSet rs = pstm.executeQuery();if (rs.next()) {return true;}} catch (SQLException ex) {LOGGER.log(Level.SEVERE, "User validation failed!", ex);}return false;}

為了在這里不要實現太多,我決定有兩個單獨的構造函數:

public SecurityStore(String dataSource) 
public SecurityStore(String user, String passwd)

因此,這將與應用程序服務器和本地測試一起使用。 接下來是實際的密碼和鹽邏輯。

使用密碼,哈希和鹽

這是我想出的:

public class Password {private SecureRandom random;private static final String CHARSET = "UTF-8";private static final String ENCRYPTION_ALGORITHM = "SHA-512";private BASE64Decoder decoder = new BASE64Decoder();private BASE64Encoder encoder = new BASE64Encoder();public byte[] getSalt(int length) {random = new SecureRandom();byte bytes[] = new byte[length];random.nextBytes(bytes);return bytes;}public byte[] hashWithSalt(String password, byte[] salt) {byte[] hash = null;try {byte[] bytesOfMessage = password.getBytes(CHARSET);MessageDigest md;md = MessageDigest.getInstance(ENCRYPTION_ALGORITHM);md.reset();md.update(salt);md.update(bytesOfMessage);hash = md.digest();} catch (UnsupportedEncodingException | NoSuchAlgorithmException ex) {Logger.getLogger(Password.class.getName()).log(Level.SEVERE, "Encoding Problem", ex);}return hash;}public String base64FromBytes(byte[] text) {return encoder.encode(text);}public byte[] bytesFrombase64(String text) {byte[] textBytes = null;try {textBytes = decoder.decodeBuffer(text);} catch (IOException ex) {Logger.getLogger(Password.class.getName()).log(Level.SEVERE, "Encoding failed!", ex);}return textBytes;}
}

很簡單,對不對? 老實說:使用byte []可以更好地隱藏,但是我認為您會更容易理解這里發生的事情。 salt()方法返回配置長度的安全隨機鹽。 hashWithSalt()方法將所有內容放入一個SHA-512哈希密碼中。

關于結束碼

我決定對它進行Base64編碼,并且使用的是專有API(sun.misc.BASE64Decoder,Encoder)。 您應該在這里考慮使用Apache Commons。 但這是最簡單的方法。 另一種方法是簡單地對所有內容進行十六進制編碼(零填充)。 Base64和HEX之間的區別實際上只是字節的表示方式。 十六進制是表示“ Base16”的另一種方式。 十六進制將為每個字節占用兩個字符– Base64每三個字節將占用4個字符,因此它比十六進制更有效。 假設您使用UTF-8編碼XML文檔,則100K文件將需要200K進行十六進制編碼,而在Base64中則需要133K。

最后是UserRealm中缺少的方法

這篇冗長的文章的最后一部分是UserRealm類中的authenticate方法。

    /*** Authenticates a user against GlassFish** @param name The user name* @param givenPwd The password to check* @return String[] of the groups a user belongs to.* @throws Exception*/public String[] authenticate(String name, String givenPwd) throws Exception {SecurityStore store = new SecurityStore(dataSource);// attempting to read the users-saltString salt = store.getSaltForUser(name);// Defaulting to a failed login by setting nullString[] result = null;if (salt != null) {Password pwd = new Password();// get the byte[] from the saltbyte[] saltBytes = pwd.bytesFrombase64(salt);// hash password and saltbyte[] passwordBytes = pwd.hashWithSalt(givenPwd, saltBytes);// Base64 encode to StringString password = pwd.base64FromBytes(passwordBytes);_logger.log(Level.FINE, "PWD Generated {0}", password);// validate password with the dbif (store.validateUser(name, password)) {result[0] = "ValidUser";}}return result;}

這就是所有要做的事情。 如果給定用戶名帶有鹽,我們將生成一個哈希密碼,該密碼將與數據庫中的密碼進行核對。 getSaltForUser()也是我們對用戶是否存在的隱式檢查。

使密碼破解更加困難:哈希函數慢

如果安全性不增加更多,則不會被稱為安全性。 因此,加鹽的密碼比簡單的散列密碼要好得多,但可能仍然不夠,因為它們仍然允許對任何單個散列進行暴力破解或字典攻擊。 但是您可以添加更多保護。 關鍵字是key-stretching 。 也稱為慢散列函數。 這里的想法是使計算速度足夠慢,從而不再允許CPU / GPU驅動的攻擊。 它使用特殊的CPU密集哈希函數實現。 PBKDF2 (基于密碼的密鑰派生功能2)就是其中之一。 您可以用不同的方式使用它,但只能警告一個:切勿自己嘗試這樣做。 使用像的測試并提供實現方式的一個PBKDF2WithHmacSHA1從JDK或PKCS5S2ParametersGenerator從BouncyCastle的庫。 一個示例可能如下所示:

    public byte[] hashWithSlowsalt(String password, byte[] salt) {SecretKeyFactory factory;Key key = null;try {factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");KeySpec keyspec = new PBEKeySpec(password.toCharArray(), salt, 1000, 512);key = factory.generateSecret(keyspec);} catch (NoSuchAlgorithmException | InvalidKeySpecException ex) {Logger.getLogger(Password.class.getName()).log(Level.SEVERE, null, ex);}return key.getEncoded();}

為什么那樣呢?

我們聽說密碼和用戶數據庫泄漏很多。 每天。 一些大型站點遭到了攻擊,而實現者為其用戶提供適當的安全性基本上取決于實施者。 坦白地說,使用提供的功能很難知道在哪里進行調整以及如何進行調整,從而使您感到不舒服。 不要停止學習安全功能,并時刻注意可能出現的問題。 我個人希望GlassFish為用戶提供一套更全面的默認領域。 但只要不是這種情況,我的博客就是引導您朝正確方向發展的唯一途徑。 希望你喜歡!

參考: JCG合作伙伴 Markus Eisele在Java企業軟件開發博客上的MySQL上帶有咸密碼的GlassFish JDBC安全性 。

翻譯自: https://www.javacodegeeks.com/2012/07/glassfish-jdbc-security-with-salted.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374318.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374318.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374318.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mgo寫入安全機制

mgo寫入安全機制 mongo寫入安全mgo寫入安全mongo寫入安全 mongo本身也有一整套的寫入安全機制,但是在這篇的內容里只介紹一小部分相關部分.先放一個鏈接可以跳過本節不看直接看這個 鏈接. WriteConcern.NONE:沒有異常拋出WriteConcern.NORMAL:僅拋出網絡錯誤異常&#xff0c;沒…

C學習雜記(二)筆試題:不使用任何中間變量如何將a、b的值進行交換

常見的方法如下 void swap1(int *a, int *b) {int temp *a;*a *b;*b temp; } 不使用中間變量的方法 void swap2(int *a, int *b) {*a *a *b;*b *a - *b;*a *a - *b; } 這種方法是不可取的&#xff0c;因為ab和a-b的運算可能會導致數據溢出。 void swap3(int *a, in…

利用python進行數據分析_利用python進行數據分析復現(1)

&#xfeff;一直以來&#xff0c;都想學習python數據分析相關的知識&#xff0c;總是拖拖拉拉&#xff0c;包括這次這個分享也是。《利用python進行數據分析 第2版》是一次無意之間在簡書上看到的一個分享&#xff0c;我決定將很詳細。一直都想著可以復現一下。但總有理由&…

在運行時交換出Spring Bean配置

如今&#xff0c;大多數Java開發人員都定期與Spring打交道&#xff0c;而我們當中的許多人已經熟悉了Spring的功能和局限性。 最近&#xff0c;我遇到了一個我從未遇到過的問題&#xff1a;引入了基于運行時引入的配置來重新連接Bean內部的功能。 這對于簡單的配置更改或交換掉…

Proximal Algorithms--Accelerated proximal gradient method

4.3 Accelerated proximal gradient method&#xff1a; 加速近端梯度方法&#xff1a; 基本的近端梯度方法的所謂的“加速”版本&#xff0c;就是在算法中包含了一個外推(extrapolation)步驟&#xff0c;一個簡單的版本是&#xff1a; yk1:xkωk(xk?xk?1)xk1:proxλkg(yk1?…

C語言代碼規范(七)#define

#define 宏定義的使用 #define MAX(x, y) ( ((x) > (y)) ? (x) : (y) ) #define MIN(x, y) ( ((x) < (y)) ? (x) : (y) ) 在宏定義中要把參數用括號擴起來( ((x) > (y)) ? (x) : (y) )。 因為宏只是簡單的文本替換&#xff0c;如果不注意&#xff0c;很容…

http 二進制_淺談HTTP協議

HTTP一、HTTP協議http協議&#xff0c;是超文本傳輸協議&#xff0c;此協議是基于TCP/IP的協議&#xff0c;是互聯網上應用最為廣泛的一直網絡協議是一種無狀態協議&#xff0c;默認端口為80,。設計HTTP的最初目的是為了提供一種發布和接受HTML頁面的方法。通過HTTP或者HTTPS協…

登陸注冊

登陸注冊&#xff0c;注冊的賬號存在服務器的數據庫里&#xff0c;成功了就給你返回成功&#xff0c;失敗了就返回失敗 有三種登陸方式&#xff1a;普通注冊&#xff0c;手機號注冊&#xff0c;第三方注冊轉載于:https://www.cnblogs.com/SensenCoder/p/4885606.html

Java并發教程–線程池

Java 1.5中提供的最通用的并發增強功能之一是引入了可自定義的線程池。 這些線程池使您可以對諸如線程數&#xff0c;線程重用&#xff0c;調度和線程構造之類的東西進行大量控制。 讓我們回顧一下。 首先&#xff0c;線程池。 讓我們直接進入java.util.concurrent.ExecutorSer…

HTTPPost/AFNetWorking/JSONModel/NSPredicate

一、HTTPPost 1. POST方式發送請求 HTTP協議下默認數據發送請求方法是GET方式&#xff0c;若需要使用POST方法&#xff0c;則需要對發送的請求也就是request對象&#xff0c;進行屬性設置。 步驟如下&#xff1a; > 要發送的請求對象&#xff0c;需要使用可變請求對象 [[NSM…

C語言代碼規范(八)使用const修飾值不允許改變的變量

使用const限定一個變量的值不允許被改變&#xff0c;從而保護被修飾的東西&#xff0c;防止意外&#xff0c;提高程序的可靠性和安全性。

教育小思

父母的時代是“攢錢&#xff0c;買房&#xff0c;生子&#xff0c;終老”&#xff0c;而現在的時代是“教育&#xff0c;創造&#xff0c;傳承&#xff0c;成長”。 改變世界&#xff0c;從教育起步。 傳統教育的不足之處&#xff1a; 1. 學習體驗不佳&#xff0c;學習者被迫…

linux redis客戶端_為什么單線程Redis能那么快?

我們通常說&#xff0c;Redis 是單線程&#xff0c;主要是指 Redis 的網絡 IO 和鍵值對讀寫是由一個線程來完成的&#xff0c;這也是 Redis 對外提供鍵值存儲服務的主要流程。但 Redis 的其他功能&#xff0c;比如持久化、異步刪除、集群數據同步等&#xff0c;其實是由額外的線…

servlet中文亂碼處理

servlet中文亂碼處理 如果是post設置req.setCharacterEncoding("utf-8");如果是get&#xff0c;不去修改服務器配置的情況下new String(name.getBytes("iso-8859-1"),"utf-8")數據庫亂碼?useUnicodetrue&characterEncodingUTF-8轉載于:http…

C語言開發筆記(七)const和指針

const修飾變量是常用的&#xff0c;不容易犯錯&#xff0c;而const和指針一起使用時很容易混淆。 (一)const int *p #include <stdio.h>int main(void) {int a 10;int b 20;const int *p &a;*p b;return 0; } const在int *的左側&#xff0c;即指針指向內容為…

從JavaFX 1.3遷移到JavaFX 2.0

幾天前&#xff0c;我完成了將Modellus的源代碼從JavaFX 1.3腳本遷移到JavaFX 2.0 Java語言的過程。 因此&#xff0c;我認為寫關于我在此過程中學到的知識會很好。 我想指出&#xff0c;如果您想繼續在JavaFX 2.0中使用JavaFX腳本&#xff0c;則可以使用Visage&#xff1a; ht…

九度OJ 1034:尋找大富翁 (排序)

時間限制&#xff1a;1 秒 內存限制&#xff1a;32 兆 特殊判題&#xff1a;否 提交&#xff1a;5925 解決&#xff1a;2375 題目描述&#xff1a;浙江桐鄉烏鎮共有n個人,請找出該鎮上的前m個大富翁.輸入&#xff1a;輸入包含多組測試用例.每個用例首先包含2個整數n&#xff08…

ubuntu php 無法執行exec_利用webhook使php項目自動部署

php中文網最新課程每日17點準時技術干貨分享1.先來講一下自動部署的原理&#xff0c;一般在我們push代碼的時候&#xff0c;可以自動請求webhook中設置的url&#xff0c;完成一次請求與響應。那么只要我們設置的url地址請求的php文件內容是執行命令行git push命令&#xff0c;則…

android-verticalseekbar——Android可視化SeekBar類庫

android-verticalseekbar——Android可視化SeekBar類庫轉載于:https://www.cnblogs.com/zhujiabin/p/5706246.html

C語言開發筆記(八)static

在C語言中&#xff0c;static有3個作用&#xff1a; &#xff08;1&#xff09;在函數體&#xff0c;一個被聲明為靜態的變量在這一函數體內被調用的過程中維持其值不變。 #include <stdio.h>void test(void) {static int i 0;printf("%d\n", i); }int main…