지난 게시글은 여기 >>
Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(13) - iframe과 파싱을 이용해 유튜브 파싱하기
로그인 할 때 비밀번호를 세션에 넣어가지 않는다고 전에 이야기 한 적이 있다. 그런데 생각해보면, 비밀번호를 데이터베이스에서 비교할때도 비밀번호가 넘어가기도 하고.. 하튼 참 위험한 친구다. 비밀번호를 정보통신법에 의거해서 주인만 알고 있어야 하는 값이라..
그럼 이걸 어쩌냐면.. 비밀번호를 해싱해서 데이터베이스를 관리하는 관리자도 이 사람의 비밀번호를 모르게 해버리면 된다(만약 개인정보를 입력하고 비밀번호를 가르쳐주는 사이트는 신고 때려도 ㄱㅊ).
해시는 무엇일까?
해시는 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 암호화 함수이다. 예를 들어 16자리로 해싱하는 함수라면 비밀번호가 32자리건, 125자리건, 13자리건 무조건 16자리로 해싱해버린다.
해시는 비밀번호보다는 정보의 무결성에 영향을 더 많이 주고 더 많이 쓰이는데,
해시는 같은 정보를 가지고 있다면 무조건 같은 값으로 해싱된다. 따라서, 만일 조금이라도 내부 데이터가 변경된다면 즉시 해시값이 변하기 때문에 원본 데이터의 해시 값과 현재 데이터를 해시해서 얻은 해시 값을 비교해 데이터의 무결성을 증명한다.
그래서 아주 쉬운 비밀번호(ex. 1234) 등은 해커들은 이미 해싱된 값을 가지고 있다. 이렇게 해시함수로 만들 수 있는 값들을 대량으로 저장해놓은 테이블이 있는데 해커들은 이 테이블을 이용해서 해킹을 하는데 이 테이블을 '레인보우 테이블' 이라고 한다.
해시를 왜 비밀번호에 쓰냐면, 이걸 참.. 복호화 하기 힘들기 때문이다. 또한 데이터가 16진수로 변환되기 때문에 충돌할 염려도 크게 없는 편이고. 하여튼 우리는 이제부터 비밀번호를 해시해서 데이터베이스에 넣을거고, 우리는 앞으로 입력된 비밀번호를 해시해서 데이터베이스 내부의 해싱된 값이랑 비교할 것이다.
우리는 비밀번호 해시 기법 중 제일 대중적으로 쓰는 SHA256 이라는 기법을 이용할 것이다.
이제 비밀번호를 해싱하는 함수를 만들어보자.
새 자바파일을 만들어야겠지 ^^..
- SHA256.java
package com.cos.blog.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
//1234 -> 해시화
//해시암호 : SHA256, HMAC256
//암호화 + 복호화 : Base64
//레인보우 테이블
public class SHA256 {
private final static String msalt = "코스";
public static String encodeSha256(String source) {
String result = "";
byte[] a = source.getBytes();
byte[] salt=msalt.getBytes();
byte[] bytes = new byte[a.length + salt.length];
System.arraycopy(a, 0, bytes, 0, a.length);
System.arraycopy(salt, 0, bytes, a.length, salt.length);
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(bytes);
byte[] byteData = md.digest();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < byteData.length; i++) {
sb.append(Integer.toString((byteData[i] & 0xFF) + 256, 16).substring(1));
}
result = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return result;
}
}
뜬금없는 byte에 잉? 할 수 있을것이다. 이게 참.. ㅎㅎ...
해싱하는 방법이.. 받은 데이터를 byte화 해서 이걸 또 안에 256을 더하고 이걸 다시 16진수로 바꾸고 이걸 앞자리만 자를 값이라던데 하여튼.. 때문인데(이건 프로그래머가 할 일이 아니야 수학자가 할 일이다..!).
그래서 먼저 비트를 진행한다. 먼저 원래 비밀번호 + 개인키(salt)를 byte화 해서 이걸 arraycopy 함수로 bytes배열에 복사해서 넣는다(그러니까 개인키는 털리면 안되겠지).
이제 이걸 MessageDigest md = MessageDigest.getInstance("SHA-256"); 로 해싱하는데, 이건 또 보안때문에 get set 함수로 도출 못하고 get 격인 md.update(bytes); 로만 도출할 수 있다.
이제 이걸 일일히 for문으로, 배열에 있는거 하나하나하나 다 방금처럼 툭툭툭 다 잘라서 stringBuffer에 넣는다.
Builder을 쓰지 않는 이유는 Buffer은 critical section이 있어서 다른 프로세스가 여기로 접근하지 못한다. buffer 써라..
이제 이렇게 만들어진 함수를.. 그냥 적용하면 된다.
이제 로그인 하는데랑 회원가입 하는 곳, 값을 request.getParameter로 받은 값을 다시 해싱해서 최종적인 password를 만든다.
- UserLoginProcAction.java 중 파라메터 가져오는 부분
String username =request.getParameter("username");
String rawpassword =request.getParameter("password");
String password=SHA256.encodeSha256(rawpassword);
- UserJoinProcAction.java 중 파라메터 가져오는 부분
//1. 파라메터 받기(x-www-form-urlencoded mime type -> key = value)
String username = request.getParameter("username");
String rawPassword = request.getParameter("password");
String password=SHA256.encodeSha256(rawPassword);
String email = request.getParameter("email");
String address=request.getParameter("address");
String userRole=RoleType.USER.toString();
이게 해싱된 비밀번호이다.
비밀번호 수정 부분도 마찬가지다. 전에 지웠던 비밀번호 필드를 다시 만들고..
-update.java
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ include file="../include/nav.jsp"%>
<div class="container">
<form action="/blog/user?cmd=updateProc" method="POST" class="was-validated">
<div class="form-group">
<label for="username">Username:</label>
<input value="${sessionScope.principal.username}" type="text" class="form-control" id="username"
placeholder="Enter username" name="username" required readonly>
<div class="valid-feedback">Valid.</div>
<div class="invalid-feedback">Please fill out this field.</div>
</div>
<div class="form-group">
<label for="pwd">Password:</label>
<input type="password" class="form-control" id="password" placeholder="Enter password" name="password" required>
<div class="valid-feedback">Valid.</div>
<div class="invalid-feedback">Please fill out this field.</div>
</div>
<div class="form-group">
<label for="email">Email:</label> <input value="${sessionScope.principal.email}" type="email" class="form-control" id="email" placeholder="Enter Email" name="email" required>
<div class="valid-feedback">Valid.</div>
<div class="invalid-feedback">Please fill out this field.</div>
</div>
<div class="form-group">
<label for="address">Address:</label> <button type="button" class="btn btn-warning float-right" onclick="goPopup()">주소검색</button>
<input value = "${sessionScope.principal.address}" type="text" class="form-control" id="address" placeholder="Enter Address" name="address" required readonly>
<div class="valid-feedback">Valid.</div>
<div class="invalid-feedback">Please fill out this field.</div>
</div>
<button type="submit" class="btn btn-primary">수정하기</button>
</form>
</div>
<script src="/blog/js/join.js"></script>
<%@ include file="../include/footer.jsp"%>
여기서 submit 받은 비밀번호를 다시 해싱해서 집어넣으면 된다.
-UserUpdatdProcAction.java
import com.cos.blog.model.RoleType;
import com.cos.blog.model.Users;
import com.cos.blog.repository.UsersRepository;
import com.cos.blog.util.SHA256;
import com.cos.blog.util.Script;
import sun.font.CreatedFontTracker;
@@ -30,6 +31,8 @@ public void execute(HttpServletRequest request, HttpServletResponse response) th
if(
request.getParameter("username").equals("")||
request.getParameter("username")==null||
request.getParameter("password").equals("")||
request.getParameter("password")==null||
request.getParameter("email").equals("")||
request.getParameter("email")==null||
request.getParameter("address").equals("")||
@@ -42,6 +45,8 @@ public void execute(HttpServletRequest request, HttpServletResponse response) th
//1. 파라메터 받기(x-www-form-urlencoded mime type -> key = value)
Users users=(Users)session.getAttribute("principal");
String rawpassword=request.getParameter("password");
String password=SHA256.encodeSha256(rawpassword);
String username = request.getParameter("username");
String email = request.getParameter("email");
String address=request.getParameter("address");
@@ -50,6 +55,7 @@ public void execute(HttpServletRequest request, HttpServletResponse response) th
Users user=Users.builder()
.id(users.getId())
.username(username)
.password(password)
.address(address)
.email(email)
.userRole(users.getUserRole())
@@ -63,7 +69,7 @@ public void execute(HttpServletRequest request, HttpServletResponse response) th
if(result==1) {
session.setAttribute("principal", user);
Script.href("수정에 성공하였습니다.", "/blog/board?cmd=home", response);
Script.href("수정에 성공하였습니다.", "/blog/board?cmd=home&page=0", response);
}else {
Script.back("수정에 실패하셨습니다.", response);
}