Remember-Me Authentication

김상욱·2024년 12월 7일

기억하기-나(Authentication)
기억하기-나 인증(remember-me 또는 persistent-login authentication)은 사용자가 세션 간에 신원을 유지하도록 웹사이트가 사용자를 기억하는 기능을 의미합니다. 이는 일반적으로 브라우저에 쿠키를 보내는 방식으로 이루어지며, 이후 세션에서 해당 쿠키를 감지하여 자동으로 로그인이 이루어지게 합니다. Spring Security는 이러한 작업이 가능하도록 필요한 후크(hook)를 제공하며, 두 가지 구체적인 기억하기-나 구현을 제공합니다. 하나는 쿠키 기반 토큰의 보안을 유지하기 위해 해싱(hashing)을 사용하고, 다른 하나는 데이터베이스 또는 기타 지속적 저장소(persistent storage)를 사용하여 생성된 토큰을 저장합니다.

간단한 해시 기반 토큰 방식
영속적 토큰 방식
기억하기-나 인터페이스 및 구현

  • TokenBasedRememberMeServices
  • PersistentTokenBasedRememberMeServices

두 구현 모두 UserDetailsService가 필요하다는 점에 유의해야 합니다. 만약 LDAP 공급자(LDAP provider)와 같이 UserDetailsService를 사용하지 않는 인증 공급자를 사용하는 경우, 애플리케이션 컨텍스트(application context)에 UserDetailsService 빈(bean)을 추가하지 않으면 작동하지 않습니다.


간단한 해시 기반 토큰 방식
이 방식은 해싱(hashing)을 활용하여 효과적인 기억하기-나 전략을 구현합니다. 기본적으로 대화형 인증이 성공적으로 이루어진 후 브라우저에 쿠키를 전송하며, 이 쿠키는 다음과 같은 구성 요소로 이루어져 있습니다:

기억하기-나 토큰 방식은 사용자가 세션을 종료하고 다시 접속하더라도 브라우저가 사용자를 식별하고 자동으로 로그인할 수 있도록 지원합니다. 아래는 이 방식의 주요 구성 요소와 작동 방식에 대한 자세한 설명입니다.

기억하기-나 토큰의 구조

기본 토큰 형식:

base64(username + ":" + expirationTime + ":" + algorithmName + ":" + 
algorithmHex(username + ":" + expirationTime + ":" password + ":" + key))

1. 구성 요소

  1. username

    • 사용자 이름으로 UserDetailsService에 의해 고유하게 식별됩니다.
    • 일반적으로 사용자의 로그인 ID입니다.
  2. password

    • 사용자의 실제 비밀번호와 일치해야 합니다.
    • 이는 보안을 강화하기 위해 사용되며, 비밀번호가 변경되면 토큰이 무효화됩니다.
  3. expirationTime

    • 토큰이 유효한 만료 시간을 밀리초 단위로 나타냅니다.
    • 설정된 기간이 지나면 토큰이 만료되고 사용자는 다시 로그인해야 합니다.
  4. key

    • 비밀 키로, 토큰이 변조되지 않도록 보호합니다.
    • 서버에만 저장되며, 외부에서 노출되지 않아야 합니다.
  5. algorithmName

    • 토큰의 서명을 생성하고 검증할 때 사용되는 알고리즘 이름입니다.
    • 예를 들어, HMAC-SHA256과 같은 해시 알고리즘이 주로 사용됩니다.

2. 토큰 생성 프로세스

  1. 사용자가 로그인하면 서버에서 위 구성 요소를 조합하여 하나의 문자열로 생성합니다.
  2. 이 문자열을 해싱 알고리즘으로 처리하여 서명을 생성합니다.
  3. 최종적으로 Base64로 인코딩된 토큰이 생성되어 브라우저에 쿠키 형태로 저장됩니다.

기억하기-나 토큰의 특징

  1. 유효 기간

    • 토큰은 지정된 expirationTime 동안만 유효합니다.
    • 만료되면 자동 로그인이 중단되고 사용자는 다시 로그인해야 합니다.
  2. 보안 제약

    • 캡처된 토큰은 만료되기 전까지 다른 사용자 에이전트(예: 다른 브라우저)에서 사용될 수 있습니다.
    • 이 문제는 HTTP Digest Authentication과 유사하며, 완전한 보안을 보장하지 못합니다.
  3. 비밀번호 변경 시 효과

    • 사용자가 비밀번호를 변경하면 기존의 모든 기억하기-나 토큰이 즉시 무효화됩니다.
    • 이는 캡처된 토큰이 악용되는 것을 방지합니다.
  4. 적용 방법

    • <remember-me> 요소를 Spring Security 설정에 추가하면 쉽게 활성화할 수 있습니다.

Spring Security에서 기억하기-나 인증 설정

  1. 설정 예시
    Spring Security에서 기억하기-나 인증을 사용하려면 <remember-me> 요소를 추가합니다:

    <http>
        ...
        <remember-me key="myAppKey"/>
    </http>
    • key: 비밀 키로, 토큰의 무결성을 보장합니다.
    • 서버에서 이 키가 동일해야만 토큰을 검증할 수 있습니다.
  2. UserDetailsService 연계

    • UserDetailsService는 사용자의 정보를 검색하는 데 필요한 인터페이스입니다.

    • 기억하기-나 인증은 반드시 UserDetailsService를 필요로 합니다.

    • 애플리케이션에 여러 개의 UserDetailsService가 있을 경우, user-service-ref 속성을 사용해 사용할 서비스 빈을 명시적으로 지정해야 합니다.

      <remember-me key="myAppKey" user-service-ref="customUserDetailsService"/>

보안상의 주의점

  1. 토큰 탈취 위험

    • 토큰이 캡처되면 만료될 때까지 악용될 수 있습니다.
    • 민감한 데이터가 포함되지 않도록 쿠키 보안을 강화해야 하며, HttpOnly, Secure 옵션을 활성화하는 것이 좋습니다.
  2. 토큰 만료 정책

    • 짧은 만료 시간을 설정하거나, 세션에서 일정 시간 이상 활동이 없을 경우 토큰을 무효화하는 정책을 적용할 수 있습니다.
  3. 더 강력한 방식 필요 시

    • 보안이 더 중요한 경우, 다음과 같은 방식이 권장됩니다:
      • PersistentTokenBasedRememberMeServices: 토큰을 데이터베이스에 저장하여 더 안전하게 관리.
      • HTTPS를 사용하여 통신 보안을 강화.

요약

Spring Security의 간단한 해시 기반 토큰 방식은 설정이 간편하고 자동 로그인 기능을 제공하지만, 보안에 민감한 애플리케이션에서는 적합하지 않을 수 있습니다. 추가적인 보안 조치가 필요하다면 데이터베이스 기반 방식 또는 기억하기-나 기능을 사용하지 않는 것이 좋습니다.


영속적 토큰 방식 (Persistent Token Approach)는 사용자 인증을 유지하기 위한 안전하고 관리 가능한 방식으로, Spring Security에서 제공하는 기억하기-나(remember-me) 인증의 한 유형입니다. 이 방식은 클라이언트(브라우저)에 저장된 쿠키와 서버의 데이터베이스에 저장된 정보를 비교하여 사용자를 식별합니다. 아래는 이 방식의 상세한 작동 원리, 설정 방법, 그리고 보안 특징에 대한 자세한 설명입니다.

Persistent Token Approach 작동 원리

  1. 로그인 시 쿠키 발급

    • 사용자가 로그인하면 서버는 사용자 식별 정보를 포함하는 seriestoken 값을 생성합니다.
    • 이 두 값은 쿠키로 클라이언트에 전달되고, 동시에 데이터베이스에 저장됩니다.
  2. 다음 로그인 요청

    • 클라이언트가 서버에 요청을 보낼 때, 이전에 저장된 쿠키를 함께 전송합니다.
    • 서버는 클라이언트에서 받은 seriestoken 값을 데이터베이스의 persistent_logins 테이블에서 조회하여 비교합니다.
  3. 검증 성공

    • seriestoken 값이 일치하면 사용자를 인증하고 새로운 token 값을 생성합니다.
    • token은 데이터베이스에 업데이트되고, 클라이언트에 다시 쿠키로 전달됩니다.
  4. 검증 실패

    • series가 존재하지 않거나 token 값이 일치하지 않으면 로그인 요청이 거부됩니다.
    • 이는 쿠키가 조작되었거나 탈취되었을 가능성을 의미합니다.

Spring Security 설정

Spring Security에서 이 방식을 사용하려면 데이터베이스와 데이터 소스를 설정해야 합니다. 아래는 설정 과정입니다.

1. XML 네임스페이스 구성

Spring Security XML 파일에 <remember-me> 요소를 추가하여 설정합니다:

<http>
  ...
  <remember-me data-source-ref="dataSource" key="myAppKey"/>
</http>
  • data-source-ref: 데이터베이스 연결을 나타내는 DataSource 빈(bean)을 참조합니다.
  • key: 토큰의 무결성을 보장하는 비밀 키로, 서버에서만 알 수 있습니다.

2. 데이터베이스 테이블 생성

persistent_logins라는 이름의 테이블을 생성해야 합니다. 아래는 테이블 생성 SQL 명령어입니다:

CREATE TABLE persistent_logins (
    username VARCHAR(64) NOT NULL,
    series VARCHAR(64) PRIMARY KEY,
    token VARCHAR(64) NOT NULL,
    last_used TIMESTAMP NOT NULL
);
필드 설명
  1. username

    • 로그인한 사용자 이름(아이디)을 저장합니다.
    • UserDetailsService에서 식별할 수 있어야 합니다.
  2. series

    • 사용자의 특정 인증 세션을 고유하게 식별하기 위한 값입니다.
    • PRIMARY KEY로 설정되어 중복되지 않도록 합니다.
  3. token

    • 각 요청마다 갱신되는 인증 토큰입니다.
    • token이 일치하지 않으면 인증이 실패합니다.
  4. last_used

    • 마지막으로 사용된 시각을 저장합니다.
    • 토큰 만료 또는 활동 기록 확인에 사용됩니다.

3. 데이터 소스 설정

Spring 애플리케이션에서 사용할 데이터 소스를 설정합니다:

@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/mydatabase");
    dataSource.setUsername("dbuser");
    dataSource.setPassword("dbpassword");
    return dataSource;
}

PersistentTokenBasedRememberMeServices 클래스

이 방식은 PersistentTokenBasedRememberMeServices 클래스에 의해 구현됩니다. 주요 특징은 다음과 같습니다:

  1. 토큰 갱신

    • 사용자가 요청할 때마다 새로운 token 값을 생성하고 데이터베이스를 업데이트합니다.
    • 이를 통해 탈취된 토큰이 반복적으로 사용되는 것을 방지합니다.
  2. 토큰 삭제

    • 사용자가 명시적으로 로그아웃하면 해당 사용자의 모든 토큰이 데이터베이스에서 삭제됩니다.
  3. 보안 강화

    • 쿠키 값이 데이터베이스와 일치하지 않으면 즉시 로그인 요청을 거부합니다.

Persistent Token 방식의 보안 특징

  1. 토큰 탈취 방지

    • 매 요청마다 새로운 token을 생성하여 쿠키를 갱신하므로, 탈취된 토큰이 반복적으로 사용되는 것을 막습니다.
  2. 쿠키 보안 설정

    • Secure 옵션을 활성화하여 HTTPS 통신에서만 쿠키를 전송하도록 설정합니다.
    • HttpOnly 옵션을 활성화하여 JavaScript에서 쿠키 접근을 차단합니다.
  3. 토큰 만료

    • 오래된 토큰은 자동으로 무효화할 수 있도록 만료 정책을 설정합니다.
  4. 사용자 로그아웃

    • 사용자가 비밀번호를 변경하거나 명시적으로 로그아웃하면 기존의 모든 토큰을 무효화합니다.

Persistent Token 방식과 해시 기반 방식 비교

특징해시 기반 방식영속적 토큰 방식
토큰 저장클라이언트(쿠키)서버(데이터베이스)
보안 수준상대적으로 낮음높음
토큰 갱신없음요청마다 갱신
로그아웃 시 처리쿠키 삭제로 제한데이터베이스에서 토큰 삭제 가능
설정 복잡도간단데이터베이스 설정 필요

정리

Persistent Token 방식은 서버에서 토큰을 관리하기 때문에 더 높은 보안 수준을 제공합니다. 특히, 중요한 애플리케이션에서는 데이터베이스 기반 토큰 관리 방식을 권장하며, 이를 통해 사용자의 인증 상태를 안전하게 유지할 수 있습니다.


Remember-Me Interfaces and Implementations는 Spring Security에서 remember-me 기능을 관리하기 위한 인터페이스와 그 구현 방법을 설명합니다. 이 기능은 사용자가 브라우저를 닫았다가 다시 열어도 인증 상태를 유지하도록 돕습니다. 여기서는 RememberMeServices 인터페이스의 작동 방식과 Spring Security에서 이를 사용하는 방식에 대해 자세히 설명하겠습니다.

RememberMeServices 인터페이스와 주요 메서드

RememberMeServices는 인증과 관련된 다양한 이벤트를 처리하는 데 필요한 메서드를 정의합니다. 아래는 각 메서드의 역할과 작동 방식입니다:

1. Authentication autoLogin(HttpServletRequest request, HttpServletResponse response)

  • 역할:
    • 사용자가 이전에 로그인한 상태에서 remember-me 쿠키를 통해 자동 로그인을 처리합니다.
  • 호출 시점:
    • SecurityContextHolderAuthentication 객체가 없는 경우 RememberMeAuthenticationFilter가 호출합니다.
  • 작동 방식:
    • 브라우저에서 받은 요청에서 remember-me 쿠키를 확인합니다.
    • 쿠키 정보가 유효하면 자동으로 사용자를 인증하고, Authentication 객체를 반환합니다.
  • 예시 시나리오:
    • 사용자가 로그아웃하지 않고 브라우저를 닫은 후 다시 열어 사이트를 방문할 때 자동으로 로그인됩니다.

2. void loginFail(HttpServletRequest request, HttpServletResponse response)

  • 역할:
    • 사용자가 로그인에 실패했을 때 관련 작업을 처리합니다.
  • 호출 시점:
    • AbstractAuthenticationProcessingFilter가 로그인 실패 이벤트를 감지하면 호출됩니다.
  • 작동 방식:
    • 실패 정보를 기록하거나, 필요한 경우 remember-me 쿠키를 삭제합니다.
  • 예시 시나리오:
    • 사용자가 잘못된 비밀번호로 로그인하려고 시도했을 때, 이전의 remember-me 상태를 무효화합니다.

3. void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication)

  • 역할:
    • 사용자가 성공적으로 로그인했을 때 remember-me 관련 처리를 수행합니다.
  • 호출 시점:
    • 인증 성공 이벤트를 감지하면 AbstractAuthenticationProcessingFilter가 호출합니다.
  • 작동 방식:
    • 새로운 remember-me 쿠키를 생성하고 브라우저에 전달합니다.
    • 기존의 쿠키를 갱신하거나 대체하여 보안성을 유지합니다.
  • 예시 시나리오:
    • 사용자가 로그인에 성공한 후 브라우저가 해당 세션을 기억하도록 새로운 remember-me 쿠키를 설정합니다.

RememberMeServices의 설계 의도

RememberMeServices 인터페이스는 인증 이벤트에 따라 remember-me 관련 작업을 유연하게 처리할 수 있도록 설계되었습니다. 이를 통해 다양한 구현 전략을 적용할 수 있습니다.

핵심 특징

  1. 이벤트 기반 호출:
    • 인증 성공, 실패, 자동 로그인 요청 시 인터페이스 메서드가 호출됩니다.
  2. 다양한 구현 가능성:
    • 애플리케이션 요구 사항에 따라 여러 방식으로 remember-me 기능을 구현할 수 있습니다.
    • 예: 해시 기반 방식, 데이터베이스 기반 방식 등.
  3. 확장성:
    • 인터페이스를 구현하여 커스텀 RememberMeServices를 쉽게 만들 수 있습니다.

Spring Security의 Remember-Me 구현

Spring Security는 RememberMeServices 인터페이스의 두 가지 구체적인 구현체를 제공합니다:

1. TokenBasedRememberMeServices

  • 해시 기반 구현:
    • 쿠키에 사용자 이름, 만료 시간, 비밀 키 등을 해싱한 값을 저장합니다.
  • 장점:
    • 구현이 간단하고 데이터베이스가 필요 없습니다.
  • 단점:
    • 탈취된 쿠키는 만료되기 전까지 악용될 수 있습니다.

2. PersistentTokenBasedRememberMeServices

  • 데이터베이스 기반 구현:
    • 쿠키와 함께 사용할 토큰을 데이터베이스에 저장하여 관리합니다.
  • 장점:
    • 토큰 관리가 더 안전하며, 탈취된 토큰을 서버에서 무효화할 수 있습니다.
  • 단점:
    • 데이터베이스를 설정하고 관리해야 합니다.

RememberMeServices의 작동 흐름

1. 로그인 성공 시

  • 사용자가 성공적으로 로그인하면 loginSuccess() 메서드가 호출됩니다.
  • 새로운 remember-me 쿠키가 생성되어 브라우저에 전달됩니다.
  • 쿠키 내용은 서버에서 검증할 수 있는 정보로 구성됩니다(예: 해시 값, 토큰 등).

2. 자동 로그인 요청 시

  • 사용자가 다시 사이트를 방문할 때 브라우저는 remember-me 쿠키를 전송합니다.
  • autoLogin() 메서드는 쿠키 정보를 분석하고, 유효성을 검증한 후 사용자 인증을 처리합니다.

3. 로그인 실패 시

  • 사용자가 인증에 실패하면 loginFail() 메서드가 호출됩니다.
  • 필요에 따라 기존의 remember-me 쿠키를 삭제하거나 관련 작업을 수행합니다.

RememberMeServices와 관련된 필터

Spring Security는 인증과 관련된 다양한 필터에서 RememberMeServices를 활용합니다.

  1. UsernamePasswordAuthenticationFilter

    • 사용자 이름과 비밀번호로 인증하는 기본 필터입니다.
    • 로그인 성공 또는 실패 시 RememberMeServices의 적절한 메서드를 호출합니다.
  2. BasicAuthenticationFilter

    • HTTP 기본 인증을 처리합니다.
    • remember-me 쿠키를 사용하여 자동 로그인을 지원합니다.
  3. RememberMeAuthenticationFilter

    • SecurityContextHolder가 비어 있을 때 자동 로그인을 처리합니다.
    • autoLogin() 메서드를 호출하여 remember-me 쿠키를 분석하고 인증을 처리합니다.

커스텀 RememberMeServices 구현

Spring Security에서 기본 제공 구현 외에도 애플리케이션 요구에 맞게 RememberMeServices 인터페이스를 구현할 수 있습니다. 예를 들어:

public class CustomRememberMeServices implements RememberMeServices {

    @Override
    public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
        // 커스텀 자동 로그인 로직
        return null;
    }

    @Override
    public void loginFail(HttpServletRequest request, HttpServletResponse response) {
        // 커스텀 로그인 실패 처리
    }

    @Override
    public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
                             Authentication successfulAuthentication) {
        // 커스텀 로그인 성공 처리
    }
}

결론

RememberMeServices 인터페이스는 Spring Security에서 기억하기-나 기능을 처리하기 위한 핵심 역할을 하며, 확장성과 유연성을 제공합니다. 기본 제공되는 두 가지 구현 방식(해시 기반, 데이터베이스 기반)은 애플리케이션의 보안 요구 사항에 따라 선택할 수 있습니다. 추가적인 보안 또는 커스텀 요구 사항이 있는 경우, 직접 구현하여 remember-me 전략을 강화할 수 있습니다.


TokenBasedRememberMeServices는 Spring Security에서 제공하는 간단한 해시 기반 remember-me 구현입니다. 이 방식은 별도의 데이터베이스 설정 없이 작동하며, 쿠키와 키를 활용해 사용자의 인증 상태를 유지합니다. 아래는 이 방식의 상세한 작동 원리와 구성 요소에 대한 설명입니다.

TokenBasedRememberMeServices 작동 원리

1. 로그인 성공 시

  • 사용자가 로그인하면 TokenBasedRememberMeServicesRememberMeAuthenticationToken을 생성합니다.
  • 쿠키에 사용자의 인증 정보를 포함한 서명(signature)을 저장합니다.
  • 쿠키는 다음과 같은 데이터로 구성됩니다:
    Base64(username + ":" + expirationTime + ":" + signature)
    • username: 사용자의 고유 식별자.
    • expirationTime: 토큰의 만료 시간.
    • signature: 서명은 사용자 이름, 만료 시간, 비밀번호, 키(key)를 해싱하여 생성됩니다.

2. 다음 요청 시

  • 브라우저가 remember-me 쿠키를 서버로 전송합니다.
  • TokenBasedRememberMeServices는 쿠키 데이터를 분석하고 서명을 검증합니다.
    • usernameexpirationTime을 추출합니다.
    • UserDetailsService를 사용해 데이터베이스에서 사용자의 usernamepassword를 가져옵니다.
    • 서명을 다시 생성하여 쿠키의 서명과 비교합니다.
  • 검증에 성공하면 새로운 RememberMeAuthenticationToken을 생성하고, 인증이 완료됩니다.

3. 로그아웃 시

  • 사용자가 로그아웃하면 TokenBasedRememberMeServicesLogoutHandler로 작동하여 브라우저의 remember-me 쿠키를 자동으로 삭제합니다.

구성 요소와 주요 기능

1. RememberMeAuthenticationToken

  • TokenBasedRememberMeServices가 생성하는 인증 토큰입니다.
  • 인증이 완료되면 Spring Security의 SecurityContextHolder에 저장됩니다.
  • 이 토큰은 다음을 포함합니다:
    • 사용자 이름
    • 사용자 권한(GrantedAuthority)
    • 인증 서명(signature)

2. RememberMeAuthenticationProvider

  • RememberMeAuthenticationToken을 처리하는 인증 제공자입니다.
  • TokenBasedRememberMeServices공유 키(key)를 사용하여 서명을 검증합니다.
  • 키가 맞지 않거나 서명이 유효하지 않으면 인증을 거부합니다.

3. UserDetailsService

  • 사용자의 인증 정보를 조회하기 위한 서비스입니다.
  • TokenBasedRememberMeServices는 이 서비스를 사용하여 사용자 이름과 비밀번호를 검색하고 서명을 생성하거나 검증합니다.

4. LogoutHandler

  • TokenBasedRememberMeServices는 Spring Security의 LogoutHandler 인터페이스를 구현합니다.
  • 이를 통해 LogoutFilter와 연동하여 사용자가 로그아웃할 때 remember-me 쿠키를 자동으로 삭제합니다.

보안 메커니즘

1. 서명(signature)

  • 서명은 사용자 정보와 비밀 키(key)를 조합한 후 해싱하여 생성됩니다.
  • 예를 들어, 아래 데이터를 조합하여 서명을 생성합니다:
    username + ":" + expirationTime + ":" + password + ":" + key
  • 이 방식은 쿠키가 변조되거나 탈취되어도 서명을 위조하지 못하게 보호합니다.

2. 만료 시간(expirationTime)

  • 쿠키에 저장된 인증 토큰은 expirationTime 필드로 유효 기간이 제한됩니다.
  • 만료 시간이 지나면 토큰이 자동으로 무효화됩니다.

3. 키(key)

  • 키는 TokenBasedRememberMeServicesRememberMeAuthenticationProvider 간에 공유됩니다.
  • 이 키는 서버에만 저장되며, 외부에 노출되지 않아야 합니다.

설정 방법

1. Spring Security 설정

Spring Security 설정 파일(XML 또는 Java)에서 TokenBasedRememberMeServices를 활성화할 수 있습니다:

<http>
  ...
  <remember-me key="mySecretKey"/>
</http>
  • key: 서명 생성과 검증에 사용되는 비밀 키입니다.
  • 설정된 키는 서버에서만 관리하며, 인증 서명과 쿠키 검증에 사용됩니다.

2. Java 기반 설정

Java Config를 사용하는 경우 아래와 같이 설정할 수 있습니다:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .rememberMe()
                .key("mySecretKey")
                .tokenValiditySeconds(86400) // 쿠키 유효 시간 (초 단위)
                .userDetailsService(userDetailsService());
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return new InMemoryUserDetailsManager(
            User.withUsername("user")
                .password("{noop}password")
                .roles("USER")
                .build()
        );
    }
}

장점과 단점

장점

  1. 구현이 간단:
    • 별도의 데이터베이스 설정 없이 작동하므로 설정과 관리가 간편합니다.
  2. 자동 로그아웃:
    • 로그아웃 시 쿠키를 자동으로 삭제합니다.
  3. 효율적:
    • 모든 데이터를 쿠키에 저장하므로 서버에 부하를 덜 줍니다.

단점

  1. 쿠키 탈취 위험:
    • 탈취된 쿠키는 만료되기 전까지 악용될 수 있습니다.
    • HTTPS와 Secure, HttpOnly 옵션으로 보호를 강화해야 합니다.
  2. 유연성 부족:
    • 사용자 로그아웃 후에도 탈취된 쿠키는 여전히 유효할 수 있습니다.
    • 서버에서 토큰을 개별적으로 무효화할 방법이 없습니다.

TokenBasedRememberMeServices와 PersistentTokenBasedRememberMeServices 비교

특징TokenBasedRememberMeServicesPersistentTokenBasedRememberMeServices
토큰 저장쿠키 (클라이언트 측)데이터베이스 (서버 측)
보안 수준상대적으로 낮음상대적으로 높음
로그아웃 처리쿠키 삭제로 제한데이터베이스에서 토큰 삭제 가능
설정 복잡도간단데이터베이스 설정 필요

결론

TokenBasedRememberMeServices는 간단한 구현 방식과 빠른 설정이 장점인 remember-me 구현입니다. 그러나 보안이 중요한 애플리케이션에서는 데이터베이스 기반의 PersistentTokenBasedRememberMeServices가 더 적합할 수 있습니다. 간단한 프로젝트나 높은 보안 요구 사항이 없는 경우에 적합한 선택입니다.

Spring Security의 TokenBasedRememberMeServices에서 기본적으로 SHA-256 알고리즘이 사용되지만, 이 알고리즘을 사용자 지정으로 변경하거나 검증용과 인코딩용 알고리즘을 분리할 수 있습니다. 이는 보안성을 유지하면서 새로운 서명 알고리즘으로 전환할 수 있는 유연성을 제공합니다. 아래에서 이 설정의 작동 방식, 적용 방법, 그리고 활용 예제를 자세히 설명합니다.


Token 서명과 검증 알고리즘의 동작 원리

  1. 서명 생성 (Encoding)

    • 사용자의 username, password, expirationTime, key를 결합하여 서명을 생성합니다.
    • 기본적으로 SHA-256 알고리즘을 사용하지만, 다른 알고리즘으로 변경할 수 있습니다.
  2. 서명 검증 (Matching)

    • 브라우저에서 전송된 쿠키의 서명과 서버에서 생성한 서명을 비교합니다.
    • 쿠키에 명시적으로 algorithmName이 포함되어 있으면 해당 알고리즘으로 서명을 검증합니다.
    • algorithmName이 없는 경우, 기본 검증 알고리즘(SHA-256)을 사용합니다.
  3. 알고리즘 변경의 유연성

    • 새로운 알고리즘으로 전환할 때 기존 쿠키가 이전 알고리즘을 사용해 생성된 경우에도 검증이 가능하도록 설계되었습니다.
    • 검증용 알고리즘과 생성용 알고리즘을 다르게 설정할 수 있습니다.

Java 설정 예제

다음 Java 설정 예제는 TokenBasedRememberMeServices에서 서명 생성 알고리즘과 검증 알고리즘을 사용자 지정하는 방법을 보여줍니다.

1. 전체 코드 설명

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated() // 모든 요청 인증 필요
        )
        .rememberMe((remember) -> remember
            .rememberMeServices(rememberMeServices) // 사용자 지정 RememberMeServices 사용
        );
    return http.build();
}
  • SecurityFilterChain 설정:
    • HTTP 보안 필터 체인을 구성합니다.
    • 모든 요청이 인증을 필요로 하며, 사용자 정의 RememberMeServices를 연결합니다.
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
    RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
    TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
    rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5); // 검증용 알고리즘 설정
    return rememberMe;
}
  • RememberMeServices 설정:
    • TokenBasedRememberMeServices를 생성하며, 인코딩용 알고리즘과 검증용 알고리즘을 설정합니다.
    • myKey: 토큰 생성 시 사용되는 비밀 키입니다.
    • RememberMeTokenAlgorithm.SHA256: 기본적으로 서명을 생성할 때 사용할 알고리즘입니다.
    • setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5): 검증 시 사용할 알고리즘입니다.

설정의 동작 흐름

  1. 로그인 시

    • TokenBasedRememberMeServices는 SHA-256 알고리즘을 사용해 서명을 생성하고 쿠키에 저장합니다.
    • 쿠키는 username, expirationTime, 그리고 SHA-256 서명으로 구성됩니다.
  2. 로그아웃 시

    • LogoutFilter를 통해 remember-me 쿠키가 삭제됩니다.
  3. 다음 요청 시

    • 브라우저가 전송한 remember-me 쿠키에서 algorithmName을 확인합니다.
      • algorithmName이 있으면 해당 알고리즘을 사용해 서명을 검증합니다.
      • algorithmName이 없으면 기본 검증 알고리즘(SHA-256)을 사용합니다.

알고리즘 변경 시나리오

예시: MD5에서 SHA-256으로 업그레이드

  • 이전에 MD5를 사용해 생성된 쿠키는 여전히 유효해야 합니다.
  • 새로운 사용자에게는 SHA-256 알고리즘을 사용해 서명을 생성합니다.
  • 이를 구현하려면 아래와 같이 설정합니다:
@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
    RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256; // 새로 생성할 서명에 사용
    TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
    rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5); // 기존 서명을 검증하는 데 사용
    return rememberMe;
}

이 설정으로:
1. 새 쿠키는 SHA-256으로 생성됩니다.
2. 기존 쿠키는 MD5로 검증되므로 여전히 유효합니다.

XML 설정 예제

XML로도 동일한 구성을 설정할 수 있습니다:

<http>
    <remember-me 
        key="mySecretKey" 
        data-source-ref="dataSource" 
        token-validity-seconds="86400"
    />
</http>

장점과 단점

장점

  1. 알고리즘 변경의 유연성:
    • 서명 생성 알고리즘과 검증 알고리즘을 분리하여 안전하게 전환 가능.
  2. 보안 강화:
    • SHA-256과 같은 강력한 알고리즘을 기본으로 사용.
    • 더 강력한 알고리즘으로 쉽게 업그레이드 가능.
  3. Spring Security 통합:
    • RememberMeServices가 Spring Security 필터 체인과 쉽게 연동됨.

단점

  1. 복잡성 증가:
    • 여러 알고리즘을 사용할 경우 관리가 어려울 수 있음.
  2. 보안 의존성:
    • 키 관리와 HTTPS 설정이 제대로 되지 않으면 보안이 약화될 수 있음.

결론

TokenBasedRememberMeServices는 기본적으로 SHA-256을 사용해 안전한 토큰 기반 기억하기-나 인증을 제공합니다. 사용자는 필요에 따라 알고리즘을 변경하거나 업그레이드할 수 있으며, 사용자 정의 RememberMeServices를 통해 더 높은 보안성과 유연성을 달성할 수 있습니다. 이는 사용자 경험을 유지하면서 보안 위협을 최소화하는 데 유용합니다.


Spring Security에서 remember-me 기능을 활성화하려면 여러 구성 요소가 필요합니다. 이 설정은 인증 필터와 제공자를 올바르게 구성하고, remember-me 서비스를 처리할 수 있도록 필요한 빈(Bean)을 정의하는 과정입니다. 아래에서 각각의 빈이 하는 역할과 전체 흐름에 대해 자세히 설명하겠습니다.

구성 요소 설명

1. RememberMeAuthenticationFilter

  • 역할:
    • HTTP 요청에서 remember-me 쿠키를 확인하고, 이를 사용해 사용자를 자동으로 인증합니다.
  • 구성 방법:
    @Bean
    RememberMeAuthenticationFilter rememberMeFilter() {
        RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter();
        rememberMeFilter.setRememberMeServices(rememberMeServices()); // RememberMeServices 설정
        rememberMeFilter.setAuthenticationManager(theAuthenticationManager); // AuthenticationManager 설정
        return rememberMeFilter;
    }
    • setRememberMeServices(rememberMeServices()):
      • RememberMeServices는 쿠키를 읽고 검증하는 데 필요한 서비스입니다.
      • 여기에서 구현된 TokenBasedRememberMeServices를 사용합니다.
    • setAuthenticationManager(theAuthenticationManager):
      • 이 필터는 인증을 처리하기 위해 AuthenticationManager를 사용합니다.

2. TokenBasedRememberMeServices

  • 역할:
    • remember-me 쿠키를 생성하고 검증합니다.
    • 사용자의 인증 상태를 유지하기 위해 쿠키 기반 서명(Signature)을 생성합니다.
  • 구성 방법:
    @Bean
    TokenBasedRememberMeServices rememberMeServices() {
        TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices();
        rememberMeServices.setUserDetailsService(myUserDetailsService); // UserDetailsService 설정
        rememberMeServices.setKey("springRocks"); // 서명에 사용할 비밀 키 설정
        return rememberMeServices;
    }
    • setUserDetailsService(myUserDetailsService):
      • 사용자 정보를 가져오는 데 필요한 UserDetailsService를 설정합니다.
      • 이를 통해 사용자 이름과 비밀번호를 조회하고, 서명을 생성하거나 검증합니다.
    • setKey("springRocks"):
      • 쿠키 서명을 생성할 때 사용할 비밀 키를 설정합니다.
      • 이 키는 서버에서만 관리하며, 클라이언트에 노출되지 않아야 합니다.

3. RememberMeAuthenticationProvider

  • 역할:
    • RememberMeAuthenticationFilter가 생성한 RememberMeAuthenticationToken을 검증합니다.
  • 구성 방법:
    @Bean
    RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
        RememberMeAuthenticationProvider rememberMeAuthenticationProvider = new RememberMeAuthenticationProvider();
        rememberMeAuthenticationProvider.setKey("springRocks"); // TokenBasedRememberMeServices와 동일한 키 사용
        return rememberMeAuthenticationProvider;
    }
    • setKey("springRocks"):
      • TokenBasedRememberMeServices와 동일한 비밀 키를 설정해야 합니다.
      • 이 키를 사용해 서명을 검증하고, 유효하지 않은 토큰은 인증을 거부합니다.

설정 흐름

  1. 로그인 성공 시:

    • 사용자가 로그인에 성공하면, TokenBasedRememberMeServicesremember-me 쿠키를 생성하고 브라우저에 전달합니다.
    • 이 쿠키는 사용자 이름, 만료 시간, 서명(Signature) 등으로 구성됩니다.
  2. 다음 요청에서 쿠키 처리:

    • 사용자가 사이트를 다시 방문하면 브라우저는 remember-me 쿠키를 전송합니다.
    • RememberMeAuthenticationFilter가 이 쿠키를 감지하고, TokenBasedRememberMeServices를 사용해 서명을 검증합니다.
    • 검증에 성공하면 RememberMeAuthenticationToken을 생성해 인증을 완료합니다.
  3. 로그아웃 처리:

    • 사용자가 로그아웃하면 remember-me 쿠키가 삭제됩니다. 이는 LogoutHandler에 의해 처리됩니다.

설정 시 주의사항

1. UsernamePasswordAuthenticationFilter와의 통합

  • UsernamePasswordAuthenticationFilter에서 setRememberMeServices()를 호출하여 TokenBasedRememberMeServices를 연결해야 합니다.
    usernamePasswordAuthenticationFilter.setRememberMeServices(rememberMeServices());

2. AuthenticationManager와 RememberMeAuthenticationProvider 연동

  • RememberMeAuthenticationProviderAuthenticationManager에 추가하여, 인증 토큰을 검증할 수 있도록 설정해야 합니다.
    authenticationManager.setProviders(Arrays.asList(rememberMeAuthenticationProvider()));

3. FilterChainProxy에 RememberMeAuthenticationFilter 추가

  • FilterChainProxyRememberMeAuthenticationFilter를 추가합니다.
  • 일반적으로 UsernamePasswordAuthenticationFilter 바로 뒤에 배치해야 합니다.

전체 흐름 요약

  1. RememberMeAuthenticationFilter: HTTP 요청에서 remember-me 쿠키를 처리하고, 인증을 시도합니다.
  2. TokenBasedRememberMeServices: 쿠키를 생성하고 검증하며, 인증 정보를 관리합니다.
  3. RememberMeAuthenticationProvider: 생성된 토큰의 유효성을 검증하고 인증 상태를 업데이트합니다.
  4. Spring Security 설정:
    • 모든 구성 요소를 Spring Security의 필터 체인에 연결합니다.

장점

  • 간단하고 설정이 비교적 직관적입니다.
  • 데이터베이스가 필요 없는 경우에도 사용할 수 있습니다.
  • Spring Security와 완전히 통합되어 동작합니다.

단점

  • TokenBasedRememberMeServices 방식은 탈취된 쿠키를 만료 시간 이전에 악용할 가능성이 있습니다.
  • 쿠키 보안 설정(예: HttpOnly, Secure, HTTPS)이 반드시 필요합니다.

이 설정은 작은 애플리케이션에서는 간단하게 적용할 수 있는 방식이며, 보안 요구 사항에 따라 더 강화된 방식(Persistent Token)으로 전환할 수도 있습니다.


PersistentTokenBasedRememberMeServices는 Spring Security에서 remember-me 인증을 구현하는 방식 중 하나로, 데이터베이스를 사용하여 토큰을 영구적으로 관리합니다. 이는 TokenBasedRememberMeServices보다 보안성이 높고, 토큰 탈취에 대응할 수 있는 기능을 제공합니다. 아래에서 이 구현 방식의 주요 구성 요소, 설정 방법, 그리고 작동 원리에 대해 자세히 설명하겠습니다.

PersistentTokenBasedRememberMeServices의 특징

  1. PersistentTokenRepository 필요

    • 토큰을 저장하고 관리하기 위해 PersistentTokenRepository 인터페이스를 구현한 저장소가 필요합니다.
    • Spring Security는 기본적으로 두 가지 구현체를 제공합니다:
      • InMemoryTokenRepositoryImpl: 메모리에 토큰을 저장하며, 테스트용으로만 사용됩니다.
      • JdbcTokenRepositoryImpl: 데이터베이스에 토큰을 저장하는 구현체로, 실무에서 주로 사용됩니다.
  2. 데이터베이스를 활용한 보안

    • remember-me 쿠키에는 사용자 이름이 포함되지 않으며, 대신 토큰의 고유 식별자와 서명만 포함됩니다.
    • 데이터베이스에서 토큰을 조회하고 검증하기 때문에 클라이언트 측에서 토큰이 노출되더라도 사용자의 계정을 안전하게 보호할 수 있습니다.
  3. 토큰 기반 인증과의 차이점

    • TokenBasedRememberMeServices는 쿠키에 모든 인증 정보를 저장하지만, PersistentTokenBasedRememberMeServices는 쿠키에 최소한의 정보만 저장하고 나머지는 서버에서 관리합니다.

구성 요소

1. PersistentTokenRepository

  • 토큰을 저장, 조회, 삭제하는 데 사용되는 인터페이스입니다.
  • 주요 메서드:
    • createNewToken(PersistentRememberMeToken token): 새 토큰 생성.
    • updateToken(String series, String tokenValue, Date lastUsed): 기존 토큰 업데이트.
    • getTokenForSeries(String seriesId): 특정 시리즈 ID에 대한 토큰 조회.
    • removeUserTokens(String username): 사용자의 모든 토큰 삭제.

2. JdbcTokenRepositoryImpl

  • PersistentTokenRepository를 구현한 데이터베이스 기반 클래스입니다.
  • 데이터베이스에 토큰을 저장하고 관리합니다.
  • 데이터베이스 스키마는 다음과 같습니다:
    CREATE TABLE persistent_logins (
        username VARCHAR(64) NOT NULL,
        series VARCHAR(64) PRIMARY KEY,
        token VARCHAR(64) NOT NULL,
        last_used TIMESTAMP NOT NULL
    );
    • username: 사용자 계정.
    • series: 고유한 시리즈 ID.
    • token: 인증에 사용되는 토큰 값.
    • last_used: 토큰이 마지막으로 사용된 시점.

작동 원리

1. 로그인 성공 시

  • 사용자가 로그인에 성공하면, 서버는 다음 작업을 수행합니다:
    1. 새로운 토큰(PersistentRememberMeToken)을 생성합니다.
    2. PersistentTokenRepository를 사용하여 데이터베이스에 토큰을 저장합니다.
    3. 쿠키에 시리즈 ID와 토큰 값을 저장합니다.

2. 다음 요청 시

  • 사용자가 사이트를 다시 방문하면:
    1. 클라이언트가 전송한 쿠키에서 시리즈 ID와 토큰 값을 읽어옵니다.
    2. PersistentTokenRepository를 사용하여 데이터베이스에서 해당 시리즈 ID를 조회합니다.
    3. 데이터베이스에 저장된 토큰 값과 쿠키의 토큰 값을 비교합니다.
    4. 값이 일치하면 인증을 완료하고, 토큰을 갱신합니다(새로운 토큰 값을 생성하여 업데이트).

3. 로그아웃 시

  • 사용자가 로그아웃하면, PersistentTokenRepository를 사용해 데이터베이스에서 해당 사용자의 모든 토큰을 삭제합니다.

Java 설정 예제

@Bean
PersistentTokenBasedRememberMeServices rememberMeServices() {
    PersistentTokenBasedRememberMeServices rememberMeServices =
            new PersistentTokenBasedRememberMeServices("myAppKey", myUserDetailsService(), tokenRepository());
    rememberMeServices.setTokenValiditySeconds(86400); // 토큰 유효 시간 설정 (1일)
    return rememberMeServices;
}

@Bean
PersistentTokenRepository tokenRepository(DataSource dataSource) {
    JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    tokenRepository.setDataSource(dataSource); // 데이터베이스 연결 설정
    return tokenRepository;
}

주요 설정 설명:

  1. PersistentTokenBasedRememberMeServices:

    • myAppKey: 토큰 서명을 생성하는 데 사용되는 비밀 키.
    • myUserDetailsService(): 사용자 인증 정보를 조회하는 UserDetailsService.
    • tokenRepository(): 데이터베이스에 토큰을 저장하고 관리하는 PersistentTokenRepository.
  2. JdbcTokenRepositoryImpl:

    • 데이터베이스 기반의 토큰 저장소.
    • DataSource를 설정하여 데이터베이스와 연결.

XML 설정 예제

<http>
    <remember-me key="myAppKey" data-source-ref="dataSource" />
</http>
  • key: 서명 생성에 사용되는 비밀 키.
  • data-source-ref: 데이터베이스 연결을 나타내는 DataSource 빈.

PersistentTokenBasedRememberMeServices와 TokenBasedRememberMeServices 비교

특징PersistentTokenBasedRememberMeServicesTokenBasedRememberMeServices
토큰 저장 위치서버(데이터베이스)클라이언트(쿠키)
보안 수준높음낮음
토큰 탈취 대응탈취된 토큰은 서버에서 즉시 무효화 가능만료 시간 이전에는 악용 가능
구현 복잡도데이터베이스 설정 필요간단
로그아웃 처리데이터베이스에서 토큰 삭제쿠키 삭제

장점과 단점

장점

  1. 높은 보안성:
    • 사용자 이름이 쿠키에 포함되지 않으며, 데이터베이스에서 토큰을 관리하므로 탈취된 토큰에 대한 대응이 가능합니다.
  2. 중앙 관리:
    • 모든 토큰 정보를 서버에서 중앙 관리하므로 더 안전하게 인증 상태를 유지할 수 있습니다.

단점

  1. 구현 복잡성:
    • 데이터베이스 설정과 추가적인 관리가 필요합니다.
  2. 성능:
    • 데이터베이스를 매 요청마다 조회해야 하므로, 성능 저하가 발생할 수 있습니다.

결론

PersistentTokenBasedRememberMeServices보안이 중요한 애플리케이션에서 권장되는 방식입니다. 데이터베이스를 통해 토큰을 관리함으로써 탈취된 쿠키로 인한 위험을 최소화할 수 있으며, 서버 측에서 인증 상태를 제어할 수 있습니다. 구현이 다소 복잡할 수 있지만, 보안 요구 사항이 높은 시스템에서는 적합한 선택입니다.

0개의 댓글