[Spring] 스프링 비밀번호 암호화

Posted by 신희준 on December 10, 2017



2017 - 12 - 10 (토)


데이터베이스 암호화 방식 vs 스프링 시큐리티

개발자가 패스워드 암호화에서 알아야 할점 : http://egloos.zum.com/springmvc/v/520257

  1. 복호화(디코딩)가 불가능한 단방향 암호코드로 만들어져야 한다.
  2. 암호는 개발자나 관리자도 알 수 없어야 하며 이용자 이외에는 누구도 접근할 수 없는 형태여야 한다.
  3. 공격자가 예측할 수 없도록 솔트 처리를 해주어야 한다.
  • 데이터베이스에서 제공하는 (mysql , oracle 등) 암호화에 의존해서는 안된다.

물론 많은 중소 웹사이트들이 위와 같이 데이터베이스 단계에서 암호화를 많이 활용하고 있다. 그 이유는 코드 상의 복잡함을 줄이기 위한 불가피한 선택이었을 것이다. 하지만 이에 대한 치명적인 문제점이 발생할 수 있다. 만약 mysql 이나 oracle 등 의 특정 데이터베이스를 이용하고 있다고할 경우 스프링은 해당 DBMS에 독립될 수 없다. 즉, 갑자기 mysql 5대의 버전에서 oracle 10g로 넘어간다고 하자, 이 경우 데이터베이스에서 디자인한 비밀번호 암호화 프로세스를 다시 설정해주어야 한다. 허나 스프링 시큐리티를 이용한다면 이런 복잡한 암호화 단계를 개발영역으로 끌어들임과 동시에 코드 상의 복잡함도 대부분 축소할 수 있게 된다. 더욱이 스프링 시큐리티는 암호화 클래스를 빈으로 설정한 뒤 따로 주입받아 사용하기 때문에 기능의 확장에 있어서도 매우 자유롭다. 즉, DBMS에 종속되지 않고 높은 확장성을 지닐 수 있게된다는 장점이 있다.

결론 : 스프링시큐리티 > 데이터베이스 암호화

스프링 시큐리티로 비밀번호 암호화하기


1 . 스프링 시큐리티 라이브러리 추가하기 : 이 라이브러리를 추가할 때 조심해야하는 것은 스프링 버전과 잘 맞는지 확인해야하는 것이다. 만일 잘 물리지 않는 버전을 사용했을 때 라이브러리를 날리고 다시설치해야한다.

<!--스프링시큐리티 web 라이브러리-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.1.0.RELEASE</version>
  </dependency>
  <!--스프링시큐리티 core 라이브러리-->
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>4.1.0.RELEASE</version>
  </dependency>
  <!--스프링시큐리티 config 라이브러리-->
  <dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.1.0.RELEASE</version>
</dependency>

2 . spring-security.xml 생성 : 해당 xml 문서에는 비밀번호 암호화인코딩을 위한 bean을 추가해준다. 스프링 시큐리티 라이브러리에서 제공하는 BcryptPasswordEncoder 는 비밀번호 암호화 메서드, 인코딩된 비밀번호와 Raw 형태의 비밀번호를 비교해주는 메서드를 제공한다. 하지만 비밀번호를 디코딩하는 메서드는 지원하지 않는다.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security.xsd">

    <beans:bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />  

</beans:beans>

3 . web.xml 에 로드시 spring-security.xml 을 읽어오도록 파라미터를 추가한다.

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
      /WEB-INF/spring/root-context.xml
      /WEB-INF/spring/spring-security.xml
  </param-value>

4 . 이제 BCryptPasswordEncoder 클래스에서 제공해주는 메서드를 의존성주입을 통해 사용할 수 있게되었다. 서비스 단에서 회원가입 시 비밀번호를 암호화 해주는 코드를 추가한다.

@Override
public void create(UserVO vo) throws Exception {

  String encPassword = passwordEncoder.encode(vo.getUser_password());
  vo.setUser_password(encPassword);
  logger.info("암호화된 비밀번호 : "+vo.getUser_password());

  dao.insertUser(vo); // 회원가입 DAO
}

5 . 이 후 DAO 에서 서비스에서 전달받은 vo 를 전달하면 데이터베이스에 인코딩된 데이터가 저장된다.

6 . 스프링시큐리티의 bcrypt 인코딩 비밀번호와 인코딩 되지 않은 비밀번호를 비교하는 메서드를 통해 로그인 기능을 구현할 수 있다. BCryptPasswordEncoder 빈의 matches(rawData , encodedData) 메서드를 활용하면 로우 데이터와 인코딩 데이터를 비교할 수 있게 된다.

@Override
public UserVO login(LoginDTO dto) throws Exception {
  // TODO Auto-generated method stub

  String pw = dao.getUserPw(dto.getUser_email()).getUser_password();
  logger.info("암호화 비밀번호"+pw);
  String rawPw = dto.getUser_password();
  logger.info("비밀번호"+rawPw);
  if(passwordEncoder.matches(rawPw, pw)) {
    logger.info("비밀번호 일치");
    dto.setUser_password(pw);
  }else {
    logger.info("비밀번호 불일치");    
  }  
  return dao.login(dto);
}

7 . 스프링 시큐리티의 BCryptPasswordEncoder 를 필요로 하는 작업 들 ( 비밀번호 변경, 비밀번호 찾기 등의 작업에 해당 작업을 추가한다. )