사용자의 비밀번호는 모두 강력한 암호화 기법을 사용해 암호화 해야한다.
일반적인 방법으로 단방향 암호화 기법인 MD5를 사용하는데, 이 알고리즘은 출현한지 오래되어 이미 해킹이 가능한 수준이 되어 더 이상 안전한 방법이 아니다.
일반적으로 AES / SHA / DES를 많이 사용하는데 DES 알고리즘은 AES 알고리즘이 출현한 이후 더이상 사용하지 않는다.
그런데, 위 AES / SHA / DES 는 메시지 인증 및 무결성 체크를 위한 알고리즘이지, 패스워드 암호화를 위한 알고리즘이 아니다.
따라서, 이 알고리즘들을 패스워드 암호화에 사용한다면 "복호화"가 가능하다는 취약점이 있다.
원래 위 3개 알고리즘은 "단방향"의 복호화가 불가능한 암호화 알고리즘이지만, 이미 많은 해커들이 "복호화"를 시도했고, 그 시도는 성공을 이룬지 오래되었기 때문에 암호화에 사용해서는 안된다.
암호화에 사용되는 기법은 http://helloworld.naver.com/helloworld/textyle/318732 에서 소개하고 있는 PBKDF2 / bcrypt / scrypt 중 하나를 사용한다.
본 예제는 bscrypt를 사용하고 있다.
좌측 Check Salt 를 클릭하면 Salt를 생성하고 기존의 패스워드에 Salt를 추가해 암호화(Digest) 한다.
EncryptoPasswordController.makeSalt
- Salt 컬럼 추가
- Salt 생성
- Salt 값을 기존 Password에 추가하여 Digest
Salt 값이 추가되면 Login의 로직도 함께 변경되어야 한다
- dispatcherServlet.java 에 csrfInterceptor 주석 처리시켜야 실행 가능하다.
- 암호화 시키는 java 파일 - EncryptoPasswordServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 |
package kr.co.hucloud.security.code.example.encrypto.password.service.impl;
import java.util.List;
import org.mindrot.jbcrypt.BCrypt;
import kr.co.hucloud.security.code.example.encrypto.password.dao.EncryptoPasswordDAO;
import kr.co.hucloud.security.code.example.encrypto.password.service.EncryptoPasswordService;
import kr.co.hucloud.security.code.example.member.dao.MemberDAO;
import kr.co.hucloud.security.code.example.member.vo.MemberVO;
import kr.co.hucloud.security.code.example.valid.table.dao.TableValidDAO;
public class EncryptoPasswordServiceImpl implements EncryptoPasswordService {
private EncryptoPasswordDAO encryptoPasswordDAO;
private MemberDAO memberDAO;
private TableValidDAO tableValidDAO;
public void setEncryptoPasswordDAO(EncryptoPasswordDAO encryptoPasswordDAO) {
this.encryptoPasswordDAO = encryptoPasswordDAO;
}
public void setMemberDAO(MemberDAO memberDAO) {
this.memberDAO = memberDAO;
}
public void setTableValidDAO(TableValidDAO tableValidDAO) {
this.tableValidDAO = tableValidDAO;
}
@Override
public boolean isExistsSaltColumn() {
return encryptoPasswordDAO.isExistsSaltColumn();
}
@Override
public boolean makeSaltColumn() {
tableValidDAO.addSaltColumn();
String salt = "";
String bcryptPassword = "";
List<MemberVO> memberList = memberDAO.getAllMemberInfo();
// 암호화 시키는 부분
for(MemberVO member : memberList) {
salt = BCrypt.gensalt();
bcryptPassword = BCrypt.hashpw(member.getPassword(), salt);
member.setSalt(salt);
member.setPassword(bcryptPassword);
memberDAO.updateMemberPassword(member);
}
return true;
}
}
|
cs |
- 암호화를 위한 POM Dependency - pom.xml
1
2
3
4
5
6
7
8
9
10 |
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.3m</version>
</dependency>
<dependency>
<groupId>com.navercorp.lucy</groupId>
<artifactId>lucy-xss</artifactId>
<version>1.6.3</version>
</dependency> |
cs |
- MemberRegistryVO에 salt 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 |
package kr.co.hucloud.security.code.example.member.vo;
public class MemberRegistryVO {
private String userId;
private String userPassword;
private String userPasswordConfirm;
private String userName;
private String salt;
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserPasswordConfirm() {
return userPasswordConfirm;
}
public void setUserPasswordConfirm(String userPasswordConfirm) {
this.userPasswordConfirm = userPasswordConfirm;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
|
cs |
- MemberDAOImpl에 salt 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 |
package kr.co.hucloud.security.code.example.member.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import kr.co.hucloud.security.code.example.common.util.DBCloseUtil;
import kr.co.hucloud.security.code.example.member.dao.MemberDAO;
import kr.co.hucloud.security.code.example.member.vo.LoginVO;
import kr.co.hucloud.security.code.example.member.vo.MemberRegistryVO;
import kr.co.hucloud.security.code.example.member.vo.MemberVO;
public class MemberDAOImpl implements MemberDAO {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public void addMember(MemberRegistryVO memberVO) {
Connection conn = null;
PreparedStatement pstmt = null;
String query = "INSERT INTO USERS ( " +
" USER_ID, USER_NAME, USER_PASSWORD, " +
" IS_ADMIN_YN, CRT_DT, MDFY_DT, SALT) " +
"VALUES ( ?, ?, ?, ?, SYSDATE, SYSDATE,?) ";
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(query);
pstmt.setString(1, memberVO.getUserId());
pstmt.setString(2, memberVO.getUserName());
pstmt.setString(3, memberVO.getUserPassword());
pstmt.setString(4, "Y");
pstmt.setString(5, memberVO.getSalt());
pstmt.execute();
}
catch(SQLException sqle) {
throw new RuntimeException(sqle.getMessage(), sqle);
}
finally {
DBCloseUtil.close(conn, pstmt, null);
}
}
@Override
public MemberVO login(LoginVO loginVO) {
Connection conn = null;
//Statement stmt = null;
PreparedStatement stmt = null;
ResultSet rs = null;
// String query = " SELECT USER_ID, USER_NAME, USER_PASSWORD FROM USER WHERE USER_ID=
// +"'loginVO.getId()'"+ AND USER_PASSWORD = +"'loginVO.getPassword()'"";
String query = " SELECT USER_ID, USER_NAME, USER_PASSWORD FROM USERS WHERE USER_ID = ? AND USER_PASSWORD = ?";
try {
conn = dataSource.getConnection();
//stmt = conn.createStatement();
stmt = conn.prepareStatement(query);
stmt.setString(1, loginVO.getId());
stmt.setString(2, loginVO.getPassword());
//rs = stmt.executeQuery(query);
rs = stmt.executeQuery();
MemberVO memberVO = null;
if(rs.next()) {
memberVO = new MemberVO();
memberVO.setId(rs.getString(1));
memberVO.setUserName(rs.getString(2));
memberVO.setPassword(rs.getString(3));
}
return memberVO;
}
catch(SQLException sqle) {
throw new RuntimeException(sqle.getMessage(), sqle);
}
finally {
DBCloseUtil.close(conn, stmt, rs);
}
}
@Override
public List<MemberVO> getUserInfo(String parameter) {
Connection conn = null;
//Statement stmt = null;
PreparedStatement stmt = null;
ResultSet rs = null;
List<MemberVO> memberList = new ArrayList<MemberVO>();
String query = " SELECT USER_ID, USER_NAME, USER_PASSWORD FROM USERS WHERE USER_ID = ?";
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(query);
stmt.setString(1, parameter);
rs = stmt.executeQuery();
MemberVO memberVO = null;
while(rs.next()) {
memberVO = new MemberVO();
memberVO.setId(rs.getString(1));
memberVO.setUserName(rs.getString(2));
memberVO.setPassword(rs.getString(3));
memberList.add(memberVO);
}
return memberList;
}
catch(SQLException sqle) {
throw new RuntimeException(sqle.getMessage(), sqle);
}
finally {
DBCloseUtil.close(conn, stmt, rs);
}
}
@Override
public List<MemberVO> getAllMemberInfo() {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
List<MemberVO> memberList = new ArrayList<MemberVO>();
String query = " SELECT USER_ID, USER_PASSWORD FROM USERS ";
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(query);
rs = stmt.executeQuery();
MemberVO memberVO = null;
while(rs.next()) {
memberVO = new MemberVO();
memberVO.setId(rs.getString(1));
memberVO.setPassword(rs.getString(2));
memberList.add(memberVO);
}
return memberList;
}
catch(SQLException sqle) {
throw new RuntimeException(sqle.getMessage(), sqle);
}
finally {
DBCloseUtil.close(conn, stmt, rs);
}
}
@Override
public void updateMemberPassword(MemberVO memberVO) {
Connection conn = null;
PreparedStatement pstmt = null;
String query = "UPDATE SYSTEM.USERS " +
"SET USER_PASSWORD = ? " +
" , SALT = ? " +
" , MDFY_DT = SYSDATE " +
"WHERE USER_ID = ? ";
try {
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(query);
pstmt.setString(1, memberVO.getPassword());
pstmt.setString(2, memberVO.getSalt());
pstmt.setString(3, memberVO.getId());
pstmt.execute();
}
catch(SQLException sqle) {
throw new RuntimeException(sqle.getMessage(), sqle);
}
finally {
DBCloseUtil.close(conn, pstmt, null);
}
}
@Override
public String getSaltById(String id) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
String query = " SELECT SALT FROM USERS WHERE USER_ID = ? ";
try {
conn = dataSource.getConnection();
stmt = conn.prepareStatement(query);
stmt.setString(1, id);
rs = stmt.executeQuery();
String salt = "";
if(rs.next()) {
salt = rs.getString(1);
}
return salt;
}
catch(SQLException sqle) {
throw new RuntimeException(sqle.getMessage(), sqle);
}
finally {
DBCloseUtil.close(conn, stmt, rs);
}
}
}
|
cs |
- MemberServiceImpl에 addMember 메소드 수정
1
2
3
4
5
6
7
8
9
10
11
12 |
@Override
public void addMember(MemberRegistryVO memberVO) {
if(encryptoPasswordDAO.isExistsSaltColumn()) {
//String salt = memberDAO.getSaltById(loginVO.getId());
String salt = BCrypt.gensalt();
String hashedPassword = BCrypt.hashpw(memberVO.getUserPassword(), salt);
memberVO.setUserPassword(hashedPassword);
memberVO.setSalt(salt);
}
memberDAO.addMember(memberVO);
} |
cs |
'IT > Secure Coding' 카테고리의 다른 글
로그인 제한 (0) | 2015.04.24 |
---|---|
Open Redirect (0) | 2015.04.23 |
File Upload, File DownLoad (0) | 2015.04.23 |
XSS (0) | 2015.04.23 |
안행부 시큐어코딩 가이드 (0) | 2015.04.22 |