在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.mzph.cn/news/374318.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用python进行数据分析_利用python进行数据分析复现(1)

&#xfeff;一直以来&#xff0c;都想学习python数据分析相关的知识&#xff0c;总是拖拖拉拉&#xff0c;包括这次这个分享也是。《利用python进行数据分析 第2版》是一次无意之间在简书上看到的一个分享&#xff0c;我决定将很详细。一直都想着可以复现一下。但总有理由&…

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协…

linux redis客户端_为什么单线程Redis能那么快?

我们通常说&#xff0c;Redis 是单线程&#xff0c;主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的&#xff0c;这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能&#xff0c;比如持久化、异步删除、集群数据同步等&#xff0c;其实是由额外的线…

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;即指针指向内容为…

ubuntu php 无法执行exec_利用webhook使php项目自动部署

php中文网最新课程每日17点准时技术干货分享1.先来讲一下自动部署的原理&#xff0c;一般在我们push代码的时候&#xff0c;可以自动请求webhook中设置的url&#xff0c;完成一次请求与响应。那么只要我们设置的url地址请求的php文件内容是执行命令行git push命令&#xff0c;则…

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…

ppt修复无法读取_移动硬盘故障分析以及建议修复方法

移动硬盘中存储了大量的重要数据&#xff0c;一旦出现什么问题&#xff0c;会让人急得焦头烂额。换个硬盘倒是件小事&#xff0c;但其中资料、数据的丢失更令人懊恼。而在硬盘使用的过程中&#xff0c;由于使用者一时的不注意&#xff0c;往往就很容易造成意外的问题。接下来&a…

(原创)c#学习笔记04--流程控制01--布尔逻辑03--运算符优先级

转载于:https://www.cnblogs.com/wodehao0808/p/4896018.html

m.2接口和nvme区别_NVMe/SATA SSD有啥不一样?萌新怎么选

随着NAND技术的升级迭代&#xff0c;堆栈层数不断提高使得SSD单位容量成本不断下降&#xff0c;消费级市场基本已经成为了SSD的天下。目前主流的SSD大致有两种接口&#xff0c;分别是M.2和SATA两种类型。NVMe/SATA有啥区别SATA接口的SSD执行的AHCI协议标准&#xff0c;是目前较…

带有NetBeans 7.1 RC 2的WebLogic 12c快速入门

WebLogic服务器12c停运了几天。 它是针对“裸露”的Java开发人员的–花哨的Fusion Middleware东西将继续沿线升至12c。 因此&#xff0c;这基本上是我要运行的版本。 今天&#xff0c;我为您提供了一个最新的NetBeans 7.1&#xff08;RC 2&#xff09;和WebLogic的快速入门 &am…

python爬虫反爬机制_Python Scrapy突破反爬虫机制(项目实践)

对于 BOSS 直聘这种网站&#xff0c;当程序请求网页后&#xff0c;服务器响应内容包含了整个页面的 HTML 源代码&#xff0c;这样就可以使用爬虫来爬取数据。但有些网站做了一些“反爬虫”处理&#xff0c;其网页内容不是静态的&#xff0c;而是使用 JavaScript 动态加载的&…

自定义URL Scheme完全指南

iPhone / iOS SDK 最酷的特性之一就是应用将其自身”绑定”到一个自定义 URL scheme 上&#xff0c;该 scheme 用于从浏览器或其他应用中启动本应用。 注册自定义 URL Scheme 注册自定义 URL Scheme 的第一步是创建 URL Scheme — 在 Xcode Project Navigator 中找到并点击工程…

P6 音频格式—— AAC

目录 前言 01 AAC是什么&#xff1f; 02 为什么需要进行AAC进行音频压缩处理&#xff1f; 03 AAC的特点以及优势 04 AAC格式详解&#xff1a; 4.1. ADIF的数据结构&#xff1a; 4.1.1 ADIF Header具体的表格: 4.2. ADTS的结构&#xff08;重点&#xff09;&#xff1a; …

Android开发笔记——ListView模块、缓存及性能

ListView是Android开发中最常用的组件之一。本文将重点说明如何正确使用ListView&#xff0c;以及使用过程中可能遇到的问题。 ListView开发模块图片缓存可能遇到的问题一、ListView开发模块 从项目实践的角度来看&#xff0c;ListView适合“自底向上”的开发模式&#xff0c;即…

python实现excel筛选功能并输出_python如何实现excel按颜色筛选功能

离岛 2020-07-09 09:37 已采纳 不太了解具体需求&#xff0c;提供一些示例代码和思路供你参考&#xff1a; 整体思路&#xff1a;首先已知excel中的颜色值&#xff0c;根据编码实现颜色筛选的功能 示例&#xff1a; 1、首先安装pip install openpyxl 2、示例代码可以获取Excel中…

C学习杂记(七)extern声明可省略变量类型

工作三年&#xff0c;看C的书也不少。第一次知道extern可以省略变量类型。 b.c有一个全局变量unsigned int data_length&#xff0c;a.c想要调用它&#xff0c;通常使用: extern unsigned int data_length&#xff1b; 在声明时可以把外部变量类型去掉&#xff1a;extern da…

linux 内存取证_【取证流程】电子数据证据远程勘验

原创&#xff1a;弘连网络电子数据证据远程勘验在日常的取证工作中必不可少&#xff0c;但由于存在信息安全差、数据可能被篡改的问题。取证过程中&#xff0c;有明确的取证要求来确保取证过程的规范显得至关重要&#xff0c;今天我们就一起来回顾下遇到远程勘验的取证场景&…

python2 print_Python2和Python3中print的不同点

在Python2和Python3中都提供print()方法来打印信息,但两个版本间的print稍微有差异 主要体现在以下几个方面&#xff1a; 1.python3中print是一个内置函数&#xff0c;有多个参数&#xff0c;而python2中print是一个语法结构&#xff1b; 2.Python2打印时可以不加括号&#xff…

java 与 c#的 中 字符串比较“==”与“equals”的差异

.net中&#xff0c;其字符串特有的驻留机制&#xff0c;保证了在同一进程中&#xff0c;相同字符序列的字符串&#xff0c;只有一个实例&#xff0c;这样能避免相同内容的字符串重复实例化&#xff0c;以减少性能开销。 先来回顾一下c#中的代码&#xff1a; public static void…