입력한 값들을 담은 form을 갖고 요청을 보낼 때 중요 정보를 탈취하는 **"중간자 공격"**을 막고자 수행
java.security 패키지에서 제공
SHA
일방향 Hash 알고리즘
처리된 데이터는 유일성을 가지며 원래의 문자열로 돌아갈 수 없다.
MD2, MD5, SHA-1, SHA-224, SHA-512, PGP ->갈수록 키가 길어진다.
MD - MessageDigest
SHA - Secure Hash Algorithm
암호화(PKI, Public Key Infrastructure)
암호화된 문자열 키를 가지고 복호화 할 수 있는 것
DES, RSA
// 1. 알고리즘을 설정하여 객체를 얻는다.MessageDigest md = MessageDigest.getInstance("알고리즘"); // MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512// 2. 평문(Plain Text)을 알고리즘에 해당하는 문자열로 변경String text = "이히히";md.update(text.getBytes());// 3. 변경된 문자열을 얻는다.new String(md.digest()); // digest()는 byte[]을 반환
public class ShaUtil { public static String shaEncoding(String plainText) { String cipherText = ""; if (plainText != null || !"".equals(plainText)) { try { // 1. 알고리즘을 사용할 수 있는 개체를 얻는다. MessageDigest md = MessageDigest.getInstance("MD5"); // 2. 평문(Plain Text)를 암호문(Cipher Text)로 변환 md.update(plainText.getBytes()); // 3. 변환된 암호문 (Cipher Text) 얻기 cipherText = new String(md.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } } return cipherText; } public static void main(String[] args) { System.out.println(shaEncoding("ABCD")); }}
...private static Base64 base64 = new Base64(); // tomcat-util에 있는 Base 64 클래스public static String shaEncoding(String plainText) { String cipherText = ""; if (plainText != null || !"".equals(plainText)) { try { // 1. 알고리즘을 사용할 수 있는 개체를 얻는다. MessageDigest md = MessageDigest.getInstance("MD5"); // 2. 평문(Plain Text)를 암호문(Cipher Text)로 변환 md.update(plainText.getBytes()); // 3. 변환된 암호문 (Cipher Text) 얻기 , 알아볼 수 있도록 Base64 객체로 인코딩 cipherText = new String(base64.encode(md.digest())); ...
나머지 필수 회원정보 입력 후 가입 버튼 클릭 시 WebMemberJoin Servlet 요청
POST 방식으로 요청
Servlet에서 쿼리스트링에 있는 Parameter를 사용하여 검증 후 DB에 추가한 후 완료 페이지 응답
비연결성 문제 발생
테이블 설계서 작성
테이블 생성
CREATE TABLE web_member(id VARCHAR2(20) CONSTRAINT pk_web_member PRIMARY KEY, pass VARCHAR2(200) NOT NULL,name VARCHAR2(60) NOT NULL,ssn VARCHAR2(200) NOT NULL,gender CHAR(6) NOT NULL,birth VARCHAR2(10) NOT NULL,age NUMBER(3) NOT NULL,marriage CHAR(6) NOT NULL,marriage_date VARCHAR2(10) NULL,zipcode CHAR(5) NOT NULL,addr1 VARCHAR2(300) NOT NULL,addr2 VARCHAR2(300) NOT NULL,greeting VARCHAR2(4000) NOT NULL,input_date DATE DEFAULT SYSDATE, ip_addr VARCHAR2(15) NOT NULL,);CREATE TABLE web_lang(id VARCHAR2(20) CONSTRAINT fk_web_lang_id REFERENCES web_member(id),lang VARCHAR2(7) not null);COMMIT;
클래스 다이어그램
중복 체크 실행모습
웹에서 발생가능한 문제들
Web Browser에서 커서를 가질 수 있는 HTML Form Control이 하나인 경우 엔터키를 치면 JavaScript의 유효성 검증과 상관없이 submit이 된다.
"" 처리를 BackEnd(Servlet)에서 수행한다.(방어코드)
if (id != null && !"".equals(id)) { // 파라미터가 있는 경우, 아이디가 ""가 아닌 경우 처리
비연결성 문제
웹은 비연결성이기 때문에 중복확인한 동일한 아이디로 모든 여러 유저가 가입시도를 할 수 있음.
가장 먼저 가입버튼을 누른 사람만 처리해야 된다.
회원정보를 insert 하기 전 아이디를 먼저 사용중인지 다시 체크한 후 insert 수행
public class WebMemberJoin extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); // MIME Type 설정 PrintWriter out = response.getWriter(); //post방식 한글처리 request.setCharacterEncoding("UTF-8"); //사용자가 입력한 값(HTML Form Control)을 받기(getParameter) String id=request.getParameter("id"); String pass1=request.getParameter("pass1"); String name=request.getParameter("name"); String ssn1=request.getParameter("ssn1"); String ssn2=request.getParameter("ssn2"); String gender=request.getParameter("gender"); String b_year=request.getParameter("b_year"); String b_month=request.getParameter("b_month"); String b_day=request.getParameter("b_day"); String age=request.getParameter("age"); String[] interestlang=request.getParameterValues("interestlang"); String marriageFlag=request.getParameter("marriageFlag"); String m_year=request.getParameter("m_year"); String m_month=request.getParameter("m_month"); String m_day=request.getParameter("m_day"); String zipcode=request.getParameter("zipcode"); String addr1=request.getParameter("addr1"); String addr2=request.getParameter("addr2"); String greetings=request.getParameter("greetings"); // DB에 데이터를 저장하기 위해 파라메터값을 VO에 저장, 비밀번호와 ssn은 일방향 해쉬알고리즘으로 암호화해서 저장 // 암호화로 중간자 공격을 막을 수 있다. DB정보를 빼가더라도 암호화된 상태로 저장되기 때문에 안전 // DAO에서는 DB작업 처리만 집중하도록 해야 한다. // DAO보다 암호화는 DAO를 쓰는쪽(Servlet, Controller, Business Logic을 처리하는 곳)에서 처리 WebMemberVO wm_vo = new WebMemberVO(id, ShaUtil.shaEncoding(pass1), name, ShaUtil.shaEncoding(ssn1+"-"+ssn2), gender, b_year+"-"+b_month+"-"+b_day, age, marriageFlag, m_year+"-"+m_month+"-"+m_day, zipcode, addr1, addr2, greetings, request.getRemoteAddr(), interestlang); // 접속자의 IP를 얻는 getRemoteAddr() ... try { // 웹은 비연결성 // 비연결성의 문제 - 중복확인한 동일한 아이디로 모든 여러 유저가 가입시도를 할 수 있다! // 비연결성에 대한 해결코드를 준비를 해야 한다! (비연결성 문제가 발생할 가능성이 있다면 재조회 후 처리) // 회원 정보를 insert 전에 아이디를 사용중인지 다시 체크한 후 insert! addWebMember(wm_vo); out.write("\t<div id=\"resultTitle\">\r\n"); out.write("\t\t<span>"); out.print(name); out.print("</span>님께서 입력하신 정보는 아래와 같습니다.\r\n"); out.write("\t</div>\r\n"); out.write("\t<div id=\"resultInput\">\r\n"); out.write("\t\t<ul>\r\n"); out.write("\t\t\t<li>아이디 : <strong>"); out.print(id); out.print("</strong></li>\r\n"); out.write("\t\t\t<li>성별 : <strong>"); out.print(gender); out.print("</strong></li>\r\n"); out.write("\t\t\t<li>생년월일 : <strong>"); out.print(b_year+" "+b_month+" "+b_day); out.print("</strong></li>\r\n"); out.write("\t\t\t<li>결혼여부 : <strong>"); out.print(marriageFlag); out.print("</strong></li>\r\n"); out.write("\t\t\t<li>관심언어 : \r\n"); out.write("\t\t\t\t\t<strong>"); if(interestlang!=null) { for(int i=0; i<interestlang.length;i++) { out.write("\t\t\t\t\t<strong>"); out.print(interestlang[i]); out.print(" <strong>\r\n"); } } out.print("</strong>\r\n"); out.write("\t\t\t</li>\r\n"); out.write("\t\t\t<li><a href=\"#void\">로그인</a></li>\r\n"); out.write("\t\t</ul>\r\n"); out.write("\t</div>\r\n"); } catch(RuntimeException re) { // 중복 아이디가 존재해서 던진 RuntimeException 예외처리 out.println("아이디가 이미 사용중입니다. <br/>"); out.println("<a href='#void' onclick='history.back();'>다시 입력<a/>. <br/>"); } catch(SQLException se) { se.printStackTrace(); out.println("회원가입이 정상적으로 이루어지지 않았습니다.<br/>"); out.println("서비스가 원활하지 못한점 죄송합니다.<br/>"); out.println("정상적인 서비스를 위해서 저희 임직원들은 최선을 다하고 있습니다.<br/>"); out.println("신속하게 복구하겠습니다. 사용중 불편한 점은 010-디잘잘에게 연락해주세요.<br/>"); }//end catch ... }//doPost private void addWebMember(WebMemberVO wm_vo) throws SQLException,RuntimeException { WebMemberDAO wm_dao = WebMemberDAO.getInstance(); //추가전에 동일 아이디가 존재하는지 다시 검색하여 처리(비연결성의 처리) if(wm_dao.selectId(wm_vo.getId())){ throw new RuntimeException(); // 존재하면 런타임 예외 던짐 }//end if wm_dao.insertMember(wm_vo); }//addWebMember}