Access Token

Access Token은 마이크로서비스 아키텍처(MSA)에서 인증과 권한 부여를 위해 사용되는 보안 메커니즘이다.

Access Token은 사용자의 인증 정보를 담고 있는 암호화된 문자열이다. 이 토큰은 클라이언트가 서버의 보호된 리소스에 접근할 수 있는 권한을 증명하는 데 사용된다.

Access Token은 MSA 환경에서 효율적이고 안전한 인증 메커니즘을 제공한다.
그러나 적절한 구현과 보안 조치가 필수적이며, 시스템의 요구사항에 맞게 신중하게 설계해야 한다.

Access Token의 특징

  1. 유한한 수명: 보통 짧은 유효 기간(예: 1시간)을 가진다.
  2. Stateless: 서버에 상태를 저장하지 않아 확장성이 높다.
  3. 암호화: 대개 JWT(JSON Web Token) 형식으로 구현된다.
  4. 포함 정보: 사용자 ID, 권한 범위, 만료 시간 등을 포함할 수 있다.

Access Token의 동작 방식

  1. 사용자 인증: 사용자가 로그인하면 서버는 Access Token을 발급한다.
  2. 토큰 저장: 클라이언트는 받은 토큰을 안전하게 저장한다(예: 로컬 스토리지).
  3. 요청 시 사용: API 요청 시 Authorization 헤더에 토큰을 포함시킨다.
  4. 서버 검증: 서버는 토큰의 유효성을 검사하고 요청을 처리한다.

Access Token의 장점

  1. 확장성: Stateless 특성으로 서버 확장이 용이하다.
  2. 보안성: 암호화된 정보로 중요 데이터를 안전하게 전송한다.
  3. 효율성: 매 요청마다 사용자 정보를 조회할 필요가 없다.

Access Token의 단점

  1. 토큰 탈취 위험: XSS 공격 등으로 토큰이 탈취될 수 있다.
  2. 제한된 정보량: 토큰 크기 제한으로 포함할 수 있는 정보가 제한적이다.

Access Token과 Refresh Token

보안 강화를 위해 Access Token과 함께 Refresh Token을 사용한다:

  1. Access Token: 짧은 유효기간, 실제 리소스 접근에 사용
  2. Refresh Token: 긴 유효기간, Access Token 재발급에 사용

MSA에서의 Access Token 활용

  1. API Gateway: 토큰 검증 및 라우팅을 중앙화한다.
  2. 서비스 간 통신: 서비스 간 호출 시 토큰을 전달하여 인증을 유지한다.
  3. 권한 관리: 토큰에 포함된 권한 정보로 세밀한 접근 제어가 가능하다.

구현 시 주의사항

  1. 토큰 저장: 클라이언트에서 안전하게 저장해야 한다(HttpOnly 쿠키 권장).
  2. HTTPS 사용: 토큰 전송 시 반드시 암호화된 통신을 사용해야 한다.
  3. 토큰 갱신: Refresh Token을 이용한 자동 갱신 메커니즘을 구현해야 한다.
  4. 토큰 폐기: 로그아웃 시 토큰을 무효화하는 방법을 고려해야 한다.

구현 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# JWT 기반 액세스 토큰 생성 및 검증
import jwt
from datetime import datetime, timedelta

class TokenService:
    def __init__(self, secret_key):
        self.secret_key = secret_key
    
    def generate_access_token(self, user_id, permissions):
        payload = {
            'user_id': user_id,
            'permissions': permissions,
            'exp': datetime.utcnow() + timedelta(hours=1),
            'iat': datetime.utcnow()
        }
        return jwt.encode(payload, self.secret_key, algorithm='HS256')
    
    def validate_token(self, token):
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=['HS256'])
            return payload
        except jwt.ExpiredSignatureError:
            raise TokenExpiredError("Token has expired")
        except jwt.InvalidTokenError:
            raise InvalidTokenError("Invalid token")

토큰 인증 시스템의 실제 구현

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 토큰 인증 미들웨어
class AuthenticationMiddleware {
    constructor(tokenVerifier) {
        this.tokenVerifier = tokenVerifier;
    }

    async authenticate(req, res, next) {
        const token = this.extractToken(req);
        if (!token) {
            return res.status(401).json({ error: 'No token provided' });
        }

        try {
            const decoded = await this.tokenVerifier.verify(token);
            req.user = decoded;
            next();
        } catch (error) {
            return res.status(401).json({ error: 'Invalid token' });
        }
    }

    extractToken(req) {
        const authHeader = req.headers.authorization;
        if (authHeader && authHeader.startsWith('Bearer ')) {
            return authHeader.substring(7);
        }
        return null;
    }
}

참고 및 출처