실용 가이드

PBE를 이용한 인증 토큰 활용 가이드

1. 개요

Aspectran은 패스워드 기반 암호화(Password-Based Encryption, PBE)를 활용하여 안전하고 상태 비저장(stateless) 방식의 인증 토큰을 생성하고 검증하는 강력한 기능을 제공합니다. 이 방식은 웹 애플리케이션, 특히 API 서버나 웹소켓 기반의 실시간 서비스에서 클라이언트의 신원을 확인하는 데 매우 유용합니다.

이 문서에서는 Aspectran의 com.aspectran.utils.security 패키지를 중심으로 인증 토큰이 어떻게 생성되고 활용되는지, 그리고 이를 어떻게 커스터마이징할 수 있는지에 대해 설명합니다.

2. 핵심 컴포넌트

Aspectran의 토큰 기반 인증은 주로 다음 핵심 컴포넌트를 통해 이루어집니다.

2.1. PBEncryptionUtils

  • 설정: JVM 시스템 속성을 통해 암호화 알고리즘, 패스워드 및 솔트를 설정합니다. 이 설정은 애플리케이션 시작 시 한 번만 로드됩니다.
    • aspectran.encryption.password: 암호화에 사용할 패스워드
    • aspectran.encryption.algorithm: 암호화 알고리즘 (기본값: PBEWITHHMACSHA256ANDAES_128)
    • aspectran.encryption.salt: (선택 사항) 암호화에 사용할 고정 솔트
  • 특징: 최신 AES 기반 알고리즘을 사용할 경우, 보안 강화를 위해 필요한 초기화 벡터(IV)를 자동으로 관리합니다. 고정 솔트를 사용하면 레거시 알고리즘에서 결정론적 암호화 결과를 얻을 수 있습니다. (참고: AES 알고리즘은 고정 솔트를 사용하더라도 무작위 IV 때문에 암호화 결과가 매번 달라집니다.)

2.2. PBTokenIssuer

  • 역할: 만료 시간이 없는 기본 토큰을 발급합니다. 토큰이 자동으로 만료될 필요가 없거나, 다른 수단(예: 폐기 목록)을 통해 관리되는 시나리오에 유용합니다.
  • 기능: Parameters 객체를 암호화하여 토큰을 생성합니다. 전역적으로 설정된 패스워드를 사용하거나, 작업 단위로 패스워드를 직접 제공하여 토큰을 생성, 분석, 검증하는 메서드를 제공합니다.

2.3. TimeLimitedPBTokenIssuer

  • 역할: 인증에 주로 사용되는 시간제한이 있는 토큰을 발급하고 검증합니다.
  • 토큰 구조: 암호화(만료_타임스탬프 + "_" + 페이로드) 형태로 구성됩니다.
    • 만료 타임스탬프: 만료 시간(밀리초 단위의 Unix 타임스탬프)을 36진수 문자열로 변환하여 길이를 줄입니다.
    • 페이로드: Parameters 객체에 담긴 사용자 정보 등 부가 데이터입니다.
  • 기본 만료 시간: 만료 시간을 별도로 지정하지 않으면 기본값인 30초가 적용됩니다.

2.4. 예외 처리

토큰을 분석하거나 검증할 때, 실패 원인을 나타내기 위해 다음과 같은 특정 예외가 발생합니다.

  • InvalidPBTokenException: 토큰의 형식이 잘못되었거나, 변조되었거나, 복호화할 수 없는 경우에 발생하는 기본 예외입니다.
  • ExpiredPBTokenException: InvalidPBTokenException의 하위 클래스로, 시간제한이 있는 토큰이 만료 시간을 초과했을 때 특별히 발생합니다. 이를 통해 만료된 토큰을 유효하지 않은 다른 토큰과 다르게 처리할 수 있습니다(예: 사용자에게 세션 갱신을 요청).

3. 인증 토큰의 생성 및 활용 예시

실제 Aspectran 애플리케이션에서 토큰이 어떻게 설정되고 사용되는지 aspectow-appmon 데모 프로젝트를 예로 들어 살펴보겠습니다.

3.1. 암호화 패스워드 설정

먼저, aspectran-config.apon 파일에서 시스템 속성을 통해 암호화에 사용할 패스워드를 설정합니다.

system: {
    properties: {
        aspectran.encryption.password: demo!
        aspectran.encryption.salt: demo-salt
        # ... other properties
    }
}

애플리케이션이 시작되면 PBEncryptionUtils는 이 패스워드와 솔트를 읽어 기본 암호화기를 초기화합니다.

3.2. 토큰 발급

서비스 로직(예: 로그인 성공 후)에서 다음과 같이 간단하게 시간제한이 있는 토큰을 발급할 수 있습니다.

import com.aspectran.utils.security.TimeLimitedPBTokenIssuer;
import com.aspectran.utils.apon.Parameters;
import com.aspectran.utils.apon.VariableParameters;

public class AuthService {

    /**
     * 기본 만료 시간(30초)을 가진 토큰을 발급합니다.
     */
    public String issueSimpleToken() {
        return TimeLimitedPBTokenIssuer.createToken();
    }

    /**
     * 사용자 정보를 페이로드에 담고, 만료 시간을 1시간으로 지정하여 토큰을 발급합니다.
     */
    public String issueUserToken(String userId, String role) {
        Parameters payload = new VariableParameters();
        payload.putValue("userId", userId);
        payload.putValue("role", role);

        long expirationMillis = 1000 * 60 * 60; // 1 hour
        return TimeLimitedPBTokenIssuer.createToken(payload, expirationMillis);
    }

}

3.3. 토큰 검증

클라이언트(예: 웹소켓 클라이언트)는 발급받은 토큰을 요청에 포함하여 전송합니다. 서버에서는 이 토큰을 검증하여 인가된 요청인지 확인합니다. 만료된 토큰과 유효하지 않은 토큰 같은 다양한 실패 시나리오를 처리하는 견고한 구현이 필요합니다.

import com.aspectran.utils.apon.Parameters;
import com.aspectran.utils.security.ExpiredPBTokenException;
import com.aspectran.utils.security.InvalidPBTokenException;
import com.aspectran.utils.security.TimeLimitedPBTokenIssuer;

...

@Override
protected boolean checkAuthorized(@NonNull Session session) {
    // URL 경로에서 토큰을 추출합니다. (e.g., /backend/{token}/websocket)
    String token = session.getPathParameters().get("token");
    try {
        // 한 번에 토큰을 검증하고 페이로드를 분석합니다.
        Parameters payload = TimeLimitedPBTokenIssuer.parseToken(token);
        String userId = payload.getString("userId");
        // 나중에 사용하기 위해 세션에 사용자 정보를 저장합니다.
        session.setAttribute("userId", userId);
        logger.debug("사용자 {}에 대한 토큰 검증 성공", userId);
        return true;
    } catch (ExpiredPBTokenException e) {
        // 만료된 토큰을 구체적으로 처리합니다.
        logger.warn("만료된 토큰 수신: {}", e.getToken());
        // 선택적으로, 여기서 토큰 갱신 절차를 시작할 수 있습니다.
        return false;
    } catch (InvalidPBTokenException e) {
        // 다른 모든 유효하지 않은 토큰 오류(형식 오류, 변조 등)를 처리합니다.
        logger.error("유효하지 않은 토큰 수신: {}", e.getToken(), e);
        return false;
    }
}

4. 커스터마이징 및 고급 활용

4.1. 암호화 알고리즘 변경

기본 알고리즘인 PBEWITHHMACSHA256ANDAES_128은 보안성이 매우 높지만, 암호화된 문자열의 길이가 깁니다. 만약 높은 수준의 보안이 필요 없고, 토큰 길이를 줄이는 것이 더 중요하다면 다음과 같이 시스템 속성을 설정하여 구형 알고리즘으로 변경할 수 있습니다.

system: {
    properties: {
        aspectran.encryption.password: your-password
        aspectran.encryption.algorithm: PBEWithMD5AndTripleDES
    }
}

경고: 이 방식은 보안 수준을 크게 낮추므로, 민감한 데이터를 다루지 않는 제한적인 경우에만 사용해야 합니다.

4.2. 안전한 패스워드 관리

데모와 달리 프로덕션 환경에서는 절대로 설정 파일에 패스워드를 평문으로 저장해서는 안 됩니다. 대신, 다음과 같은 안전한 방법을 사용하세요.

  • 환경 변수 사용: 애플리케이션 실행 시 환경 변수로부터 패스워드를 읽어와 JVM 시스템 속성으로 전달합니다.

      java -Daspectran.encryption.password=$MY_APP_SECRET -jar my-app.jar
    
  • 외부 설정 관리 도구: HashiCorp Vault, AWS Secrets Manager 등과 같은 외부 시크릿 관리 도구와 연동하여 패스워드를 동적으로 주입받습니다.

4.3. 토큰별로 다른 패스워드 및 솔트 사용하기

PBEncryptionUtils가 전역 기본 설정을 가지지만, 특정 토큰에 대해 다른 패스워드나 솔트를 사용해야 하는 경우가 있을 수 있습니다. 모든 토큰 발급기 메소드는 encryptionPasswordsalt를 인자로 받는 오버로드를 제공합니다.

이는 멀티테넌트 환경이나 자체 암호화 시크릿을 가진 외부 시스템과 통합할 때 유용하며, 고정 솔트를 활용한 결정론적 암호화(레거시 알고리즘 기준)가 필요한 경우에도 활용할 수 있습니다.

사용자 정의 패스워드와 솔트로 토큰 발급하기:

import com.aspectran.utils.security.TimeLimitedPBTokenIssuer;
import com.aspectran.utils.apon.Parameters;
import com.aspectran.utils.apon.VariableParameters;

...

String customPassword = "a-very-secret-password-for-a-tenant";
String customSalt = "fixed-salt-value";
Parameters payload = new VariableParameters();
payload.putValue("data", "confidential");

String token = TimeLimitedPBTokenIssuer.createToken(payload, 3600 * 1000, customPassword, customSalt);

사용자 정의 패스워드와 솔트로 토큰 검증하기:

try {
    Parameters payload = TimeLimitedPBTokenIssuer.parseToken(token, customPassword, customSalt);
    // 토큰이 유효하고 페이로드가 추출되었습니다.
} catch (InvalidPBTokenException e) {
    // 예외 처리
}

4.4. 사용자 정의 페이로드 타입으로 작업하기

더 나은 타입 안정성과 코드 구성을 위해, com.aspectran.utils.apon.Defaultarameters를 확장하는 사용자 정의 클래스를 정의하여 토큰 페이로드를 나타낼 수 있습니다. 토큰을 분석할 때 이 클래스를 지정하면 페이로드가 자동으로 사용자 정의 타입에 매핑됩니다.

1. 사용자 정의 페이로드 클래스 정의:

import com.aspectran.utils.apon.Defaultarameters;
import com.aspectran.utils.apon.ParameterKey;
import com.aspectran.utils.apon.ParameterValueType;

public class UserPayload extends Defaultarameters {

    private static final ParameterKey userId = new ParameterKey("userId", ParameterValueType.STRING);
    private static final ParameterKey role = new ParameterKey("role", ParameterValueType.STRING);

    public UserPayload() {
        super(userId, role);
    }

    public String getUserId() {
        return getString(userId);
    }

    public String getRole() {
        return getString(role);
    }

}

2. 사용자 정의 타입으로 토큰 발급 및 분석:

// 발급
UserPayload payloadToIssue = new UserPayload();
payloadToIssue.putValue("userId", "testuser");
payloadToIssue.putValue("role", "admin");
String token = TimeLimitedPBTokenIssuer.createToken(payloadToIssue);

// 분석
try {
    UserPayload parsedPayload = TimeLimitedPBTokenIssuer.parseToken(token, UserPayload.class);
    String userId = parsedPayload.getUserId(); // 타입-세이프 접근
    String role = parsedPayload.getRole();
    System.out.println("User: " + userId + ", Role: " + role);
} catch (InvalidPBTokenException e) {
    // 예외 처리
}

5. 결론

Aspectran이 제공하는 PBE 기반 토큰 인증 방식은 다음과 같은 장점을 가집니다.

  • 단순성: 복잡한 설정 없이 몇 줄의 코드로 안전한 인증 토큰을 생성하고 검증할 수 있습니다.
  • 상태 비저장(Stateless): 토큰 자체에 만료 시간과 사용자 정보가 포함되므로, 서버에 별도의 세션 정보를 저장할 필요가 없어 확장성이 뛰어납니다.
  • 유연성: 시스템 속성을 통해 암호화 알고리즘과 패스워드를 쉽게 변경할 수 있어 다양한 환경 요구사항에 맞게 조정할 수 있습니다.

이 가이드를 통해 Aspectran의 강력한 보안 기능을 애플리케이션에 효과적으로 적용하시길 바랍니다.