icon

안동민 개발노트

13장 : 웹 보안과 성능 최적화

인증과 권한 관리 기초


이번 절에서는 인증(Authentication)권한 부여(Authorization)를 다룹니다. 대부분의 웹 애플리케이션은 로그인 기반 개인화와 역할별 기능 제한이 필요하며, 이 과정에서 인증/권한 관리는 웹 보안의 핵심이 됩니다.

이번 장에서는 인증과 권한 부여 개념을 구분하고, 세션 기반/토큰 기반 인증 방식과 권한 관리 기본 원칙을 정리합니다.


인증 vs 권한 부여

이 두 용어는 자주 혼용되지만, 웹 보안에서는 명확히 다른 의미를 가집니다.

  • 인증 (Authentication)
    • 누구인지 확인하는 과정입니다. 사용자가 너는 누구냐?라고 물었을 때, 본인임을 증명하는 과정입니다.
    • 사용자가 주장하는 신원(ID)이 실제와 일치하는지 확인하는 프로세스입니다.
    • 예시: 사용자 ID와 비밀번호를 입력하여 로그인하는 것, 지문 인식, OTP(일회용 비밀번호) 입력 등.
  • 권한 부여 (Authorization)
    • 무엇을 할 수 있는지 결정하는 과정입니다. 사용자가 너는 무엇을 할 수 있느냐?라고 물었을 때, 허용된 작업이 무엇인지 확인하는 과정입니다.
    • 인증된 사용자에게 특정 자원(데이터, 기능)에 대한 접근 권한을 부여하거나 거부하는 프로세스입니다.
    • 예시: 로그인한 사용자가 자신의 프로필을 수정할 수 있는지, 관리자만 특정 게시물을 삭제할 수 있는지, 유료 구독자만 프리미엄 콘텐츠를 볼 수 있는지 등.

요약: 인증은 신원 확인이고, 권한 부여는 허가된 작업의 범위 결정입니다. 인증이 선행되어야 권한 부여가 가능합니다. (신원이 확인되어야 뭘 할 수 있는지 따져볼 수 있으니까요!)


사용자 인증 구현 방식

웹 애플리케이션에서 사용자를 인증하는 대표적인 두 가지 방식이 있습니다.

세션 기반 인증

세션 기반 인증 (Session-based Authentication)은 전통적인 웹 애플리케이션에서 널리 사용되던 방식입니다.

  • 동작 방식

    사용자가 ID와 비밀번호로 로그인 요청을 보냅니다.

    서버는 사용자의 신원을 확인한 후, 고유한 세션 ID(Session ID)를 생성합니다.

    이 세션 ID는 서버의 메모리나 데이터베이스에 저장되고, 사용자에게는 이 세션 ID를 포함하는 쿠키(Cookie)를 응답으로 보냅니다.

    클라이언트(브라우저)는 이 쿠키를 저장하고, 이후 모든 요청에 이 쿠키(세션 ID)를 함께 서버로 전송합니다.

    서버는 요청에 포함된 세션 ID를 확인하여, 서버에 저장된 세션 정보와 일치하는지 검증합니다. 일치하면 해당 사용자가 로그인된 것으로 간주하고 요청을 처리합니다.

    로그아웃 시 서버에서 세션 정보를 삭제합니다.

  • 장점
    • 보안성: 세션 ID 자체가 큰 의미를 가지지 않고, 실제 사용자 정보는 서버에 저장되므로 클라이언트 측에 민감한 정보가 노출되지 않습니다.
    • 세션 관리의 용이성 (서버에서 세션 만료, 강제 로그아웃 등 제어).
  • 단점
    • 확장성 (Scalability): 서버에서 세션 상태를 유지해야 하므로, 여러 대의 서버(로드 밸런싱)를 사용하는 경우 모든 서버가 세션 정보를 공유해야 하는 복잡성이 발생합니다 (Sticky Session 또는 Redis와 같은 외부 세션 저장소 필요).
    • CORS (Cross-Origin Resource Sharing) 문제: 다른 도메인 간의 요청 시 쿠키가 전송되지 않거나 보안 제약이 있어 복잡해질 수 있습니다.
    • 모바일 앱 대응 어려움: 모바일 앱에서는 웹 쿠키를 직접 관리하기 어렵습니다.

토큰 기반 인증

토큰 기반 인증 (Token-based Authentication)은 SPA, 모바일 앱, 그리고 마이크로서비스 아키텍처에서 현재 가장 널리 사용되는 방식입니다. 특히 JWT (JSON Web Token)가 대표적입니다.

  • 동작 방식

    사용자가 ID와 비밀번호로 로그인 요청을 보냅니다.

    서버는 사용자의 신원을 확인한 후, 사용자 정보(payload)를 담고 서버의 비밀 키로 서명(signature)한 토큰(Token)을 생성합니다. (예: JWT)

    이 토큰을 클라이언트에게 응답으로 보냅니다. 서버는 토큰을 따로 저장하지 않습니다 (무상태성).

    클라이언트(브라우저/앱)는 이 토큰을 로컬 스토리지, 세션 스토리지 또는 쿠키(보안을 위해 HttpOnly 쿠키 권장)에 저장합니다.

    이후 모든 요청 시 HTTP Authorization 헤더에 이 토큰을 포함하여 서버로 전송합니다. (예: Authorization: Bearer <token>)

    서버는 요청에 포함된 토큰의 유효성(서명 검증, 만료 시간 확인 등)을 검증합니다. 토큰이 유효하면 해당 사용자의 권한을 확인하고 요청을 처리합니다.

  • JWT (JSON Web Token): 토큰 기반 인증의 표준 중 하나.

    • 세 부분으로 구성: Header.Payload.Signature
    • Header: 토큰 타입(JWT), 서명 알고리즘(HMAC SHA256 등)
    • Payload: 사용자 ID, 만료 시간 등 실제 정보(클레임)
    • Signature: Header와 Payload를 Base64Url로 인코딩한 값과 서버의 비밀 키를 결합하여 암호화된 서명. 토큰의 무결성(변조 여부)을 검증하는 데 사용됩니다.
  • 장점
    • 확장성 (Scalability): 서버가 클라이언트의 상태를 저장할 필요가 없으므로(Stateless), 여러 서버 간에 부하 분산이 용이하며 서버 확장이 매우 유연합니다.
    • CORS 문제 해소: 토큰은 HTTP 헤더를 통해 전송되므로 쿠키 기반의 CORS 제약에서 자유롭습니다.
    • 모바일 앱 친화적: 모바일 앱에서도 쉽게 토큰을 저장하고 전송할 수 있습니다.
    • 분리된 프론트엔드/백엔드: 프론트엔드와 백엔드가 완전히 분리되어 개발될 때 이상적입니다.
  • 단점
    • 토큰 탈취 위험: 토큰이 클라이언트 측에 저장되므로 XSS 공격 등으로 토큰이 탈취될 경우 보안에 취약해질 수 있습니다. (Local Storage보다는 HttpOnly 쿠키에 저장하는 것이 더 안전함)
    • 토큰 무효화 어려움: 한 번 발행된 토큰은 만료 시간까지 유효하므로, 서버에서 특정 토큰을 강제로 무효화하기 어렵습니다 (블랙리스트 관리 또는 짧은 만료 시간 + Refresh Token 사용).
    • 페이로드 크기: 페이로드에 많은 정보를 담으면 토큰의 크기가 커져 네트워크 트래픽에 영향을 줄 수 있습니다.

세션 vs. 토큰 요약

특징세션 기반 인증토큰 기반 인증 (JWT)
상태 유지서버가 세션 상태 유지 (Stateful)서버가 상태 유지 안 함 (Stateless)
확장성어려움 (세션 공유 필요)용이 (분산 시스템에 적합)
CORS어려움 (쿠키 제약)용이 (헤더를 통해 토큰 전송)
모바일 앱어려움용이
보안세션 ID만 노출, 서버가 제어토큰 탈취 시 위험 (블랙리스트 필요)
주 사용처전통적인 웹 페이지, 단일 서버SPA, 모바일 앱, 마이크로서비스

현대 웹 개발에서는 대부분 토큰 기반 인증을 선호하며, 특히 JWT를 많이 사용합니다.


권한 부여 구현 방식

인증된 사용자가 특정 자원에 접근하거나 특정 작업을 수행할 수 있는지 확인하는 과정입니다.

역할 기반 접근 제어

역할 기반 접근 제어 (RBAC: Role-Based Access Control)은 가장 흔하게 사용되는 권한 부여 방식입니다.

  • 개념: 사용자에게 역할(Role)을 부여하고, 각 역할에 특정 권한(Permission)을 할당합니다. 사용자는 자신이 가진 역할에 따라 접근 가능한 자원이나 수행 가능한 작업이 결정됩니다.
  • 예시
    • 역할: 관리자(Admin), 일반 사용자(User), 게스트(Guest)
    • 권한: 게시물 생성, 게시물 수정, 게시물 삭제, 사용자 관리
    • Admin 역할은 모든 권한을 가짐.
    • User 역할은 게시물 생성, 게시물 수정 권한을 가짐.
    • Guest 역할은 게시물 조회 권한만 가짐.
  • 구현 방법
    • 데이터베이스: users 테이블, roles 테이블, permissions 테이블, 그리고 이들을 연결하는 중간 테이블(예: user_roles, role_permissions)을 생성하여 관계를 정의합니다.
    • 백엔드 로직: API 엔드포인트에 접근할 때, 요청을 보낸 사용자의 역할을 확인하고, 해당 역할이 요청된 작업에 대한 권한을 가지고 있는지 검사합니다.
      • 예: DELETE /api/posts/:id 엔드포인트에 접근하는 사용자가 Admin 역할인지 확인.
      • Express.js에서는 미들웨어(Middleware)를 사용하여 특정 라우트에 접근하기 전에 권한을 검사하는 로직을 구현할 수 있습니다.
Express.js 권한 미들웨어 예시 (간략)
// app.js 또는 routes/auth.js

// 인증 미들웨어 (JWT 토큰 검증 후 req.user에 사용자 정보 추가)
const authenticateToken = (req, res, next) => {
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1]; // Bearer token

    if (token == null) return res.sendStatus(401); // 토큰 없음

    jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
        if (err) return res.sendStatus(403); // 유효하지 않은 토큰
        req.user = user; // req.user에 사용자 정보(id, role 등) 저장
        next();
    });
};

// 권한 부여 미들웨어 (특정 역할만 허용)
const authorizeRoles = (roles) => {
    return (req, res, next) => {
        if (!req.user || !roles.includes(req.user.role)) {
            return res.status(403).json({ message: 'Forbidden: You do not have the required role.' });
        }
        next();
    };
};

// 라우트에 미들웨어 적용
app.get('/api/admin-dashboard', authenticateToken, authorizeRoles(['admin']), (req, res) => {
    res.json({ message: 'Welcome to admin dashboard!', user: req.user });
});

app.post('/api/posts', authenticateToken, authorizeRoles(['admin', 'user']), (req, res) => {
    // 게시물 생성 로직
    res.status(201).json({ message: 'Post created.' });
});

속성 기반 접근 제어

속성 기반 접근 제어 (ABAC: Attribute-Based Access Control)은 RBAC보다 더 유연하고 세밀한 권한 부여 방식입니다.

  • 개념: 사용자, 자원, 환경 등 다양한 속성(Attribute)을 조합하여 접근 결정을 내립니다.
  • 예시: 오전 9시부터 오후 5시 사이에만 접근 가능한 A 부서의 팀장이 작성한 문서, IP 주소가 특정 범위 내에 있을 경우만 접근 가능한 파일 등.
  • 장점: 매우 유연하고 세밀한 제어 가능.
  • 단점: 구현 및 관리가 복잡하여 대규모 엔터프라이즈 환경에서 주로 사용됩니다.

비밀번호 보안 (다시 강조)

인증의 가장 기본적인 요소인 비밀번호 보안은 아무리 강조해도 지나치지 않습니다.

  • 해싱(Hashing): 비밀번호를 데이터베이스에 저장하기 전에 반드시 강력한 단방향 해시 함수(예: bcrypt, scrypt)를 사용하여 해싱해야 합니다.
    • 솔트(Salt): 각 비밀번호마다 고유한 랜덤 값인 솔트를 추가하여 해싱합니다. 이를 통해 동일한 비밀번호라도 다른 해시 값을 가지게 하여 레인보우 테이블 공격 등을 방어할 수 있습니다.
    • 반복 횟수 증가: 해시 함수를 여러 번 반복하여 계산 시간을 늘려 무작위 대입 공격(Brute-force attack)을 어렵게 만듭니다.
  • 비밀번호 정책: 최소 길이, 특수 문자 포함, 대소문자/숫자 조합 등의 복잡성 요구.
  • 재사용 금지: 이전에 사용했던 비밀번호 재사용 금지.
  • 정기적인 변경 권장: 사용자가 주기적으로 비밀번호를 변경하도록 유도.

인증·권한 부여 정리

정리 구간에서는 인증(신원 확인)과 권한 부여(행위 허용)를 분리해 설계하는 기준을 다시 확인합니다. 이번 장에서는 웹 애플리케이션의 사용자 관리에서 가장 중요한 두 가지 개념인 인증(Authentication)권한 부여(Authorization)의 기초를 학습했습니다.

여러분은 누구인지 확인하는 인증과 무엇을 할 수 있는지 결정하는 권한 부여의 차이를 명확히 이해했습니다. 또한, 웹에서 사용자를 인증하는 주요 방식인 세션 기반 인증토큰 기반 인증(JWT 포함)의 동작 원리, 장단점을 비교하여 현대 SPA 및 모바일 앱 개발에서 토큰 기반 인증이 왜 선호되는지 파악했습니다. 마지막으로, 인증된 사용자의 접근 권한을 관리하는 가장 일반적인 방법인 역할 기반 접근 제어(RBAC)의 개념과 이를 Express.js 미들웨어로 구현하는 간략한 예시를 살펴보았으며, 비밀번호 보안의 중요성을 다시 한번 강조했습니다.

안전한 웹 서비스를 제공하기 위해서는 강력한 인증 시스템과 세밀한 권한 관리 체계를 구축하는 것이 필수적입니다. 이 장에서 배운 지식들을 바탕으로 사용자의 데이터를 보호하고, 안전한 서비스를 제공하는 개발자가 되시길 바랍니다.

목차