개인정보-컴플라이언스-웹애플리케이션 (1. 인증 및 세션 보안)

2025. 2. 14. 20:46프로젝트

728x90

지금 현재 기능 개발은 어느정도 완성이 되었고 이제는 보안을 신경을 쓸 차례입니다.

우리가 알아야할 보안쪽을 알아보겠습니다.

1. 인증 및 세션 보안  (필수)

  • 세션 보안 강화:
    • HttpOnly, Secure, SameSite=Strict 속성 적용
    • 세션 타임아웃: 유휴 30분, 최대 2시간
    • 세션 재발급(Session Fixation 방지): 로그인 시 기존 세션 파기 후 새 세션 발급
  • 2FA(이중 인증): 관리자 및 슈퍼유저 로그인 시 필수
  • 브루트 포스 공격 방지: 로그인 실패 횟수 제한 및 CAPTCHA 적용
  • 다중 로그인 차단: 한 계정으로 여러 기기에서 접속 시 이전 세션 종료

1-1 HttpOnly, Secure, SameSite=Strict 속성 적용

//  세션 미들웨어 설정 강화
app.use(
  session({
    secret: process.env.SESSION_SECRET || "default_secret",
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true, // 자바스크립트를 통한 쿠키 접근 차단 (XSS 방지)
      secure: process.env.NODE_ENV === "production", // HTTPS 에서만 쿠키 전송
      sameSite: "strict", // CSRF 방지 (엄격한 쿠키 정책)
      maxAge: 1000 * 60 * 30, // 세션 유지시간 (30분)
    },
  })
);

 

httpOnly: JavaScript로 접근 불가 → XSS 방지
secure: HTTPS에서만 전송
sameSite: 크로스사이트 요청 방지 (CSRF 방지)
maxAge: 세션 유효시간 명확히 지정

 

1. httpOnly가 왜 중요한가

XSS(크로스사이트 스크립트) 공격 방지

  • XSS 공격은 해커가 웹사이트에 악성 JavaScript 코드를 삽입하여 쿠키(Session ID)를 탈취하는 공격입니다.
  • httpOnly가 적용된 쿠키는 JavaScript(document.cookie)**를 통해 읽을 수 없기 때문에 세션 도용(Session Hijacking)을 예방할 수 있습니다.

 

2. secure 옵션 차이점

secure: false (HTTP 및 HTTPS 모두 쿠키 전송)

  • HTTP: 해커가 네트워크 패킷을 가로채서 세션 쿠키(Session ID)를 탈취할 수 있음.
  • 결과: 해커가 세션 도용(Session Hijacking)을 통해 관리자 권한을 탈취할 위험이 있음.

secure: true (HTTPS에서만 쿠키 전송)

  • HTTP: 브라우저가 쿠키를 전송하지 않음 → 해커가 세션 쿠키를 탈취할 기회 차단
  • HTTPS: 암호화된 연결을 통해서만 쿠키가 전송됨 → 세션 도용 방지

 

3. sameSite가 중요할까?

CSRF(Cross-Site Request Forgery) 공격이란?

  • 해커가 사용자의 인증된 세션을 도용해, 원치 않는 요청을 서버에 보내는 공격입니다.
  • 사용자가 로그인 상태(세션 유지)**일 때, 해커는 악성 사이트에서 자동 요청을 보내어 사용자의 의도와 상관없이   중요한 작업(계정 탈퇴, 송금, 데이터 삭제)을 수행할 수 있습니다.

 

1 - 2. 세션 타임아웃 및 재인증

  • 세션 만료 설정: 세션은 일정 시간 후 자동 만료
  • 재로그인 처리: 세션 만료 시 재로그인 요구
// 로그인 시 마지막 접속 시간 저장
req.session.lastAccess = Date.now();

// 미들웨어로 세션 타임아웃 처리
app.use((req, res, next) => {
  const now = Date.now();
  const sessionMaxAge = 1000 * 60 * 30; // 30분
  if (req.session.lastAccess && now - req.session.lastAccess > sessionMaxAge) {
    req.session.destroy(() => {
      res.status(401).send('세션이 만료되었습니다. 다시 로그인해 주세요.');
    });
  } else {
    req.session.lastAccess = now;
    next();
  }
});

 

1 - 3 CSRF(Cross-Site Request Forgery) 방지

CSRF 토큰 발급 및 검증: 세션 기반 CSRF 방어
SameSite 쿠키 설정: sameSite: 'Strict'로 외부 도메인 요청 차단

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.use(csrfProtection);

app.get('/csrf-token', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

// 예제: 자가진단 제출 시 CSRF 토큰 검사
app.post('/self-assessment', csrfProtection, (req, res) => {
  res.send('자가진단 제출 완료');
});

csrfProtection이 보호하는 방법

CSRF 토큰을 생성하여 클라이언트에 제공

  • 서버는 사용자가 요청을 보낼 때마다 고유한 CSRF 토큰을 생성해.
  • 이 토큰은 사용자 세션과 연결되며, GET 요청 시 클라이언트에게 전달됨.
  • 클라이언트는 이 CSRF 토큰을 POST, PUT, DELETE 요청에 함께 포함해야 해.

서버에서 CSRF 토큰을 확인 후 요청 처리

  • 클라이언트가 API 요청을 보낼 때 CSRF 토큰을 함께 보냄.
  • 서버는 클라이언트가 보낸 CSRF 토큰이 세션에 저장된 토큰과 일치하는지 검사.
  • 일치하면 요청을 처리하고, 일치하지 않으면 403 Forbidden 오류 반환.
  • 이를 통해 해커가 요청을 위조하여 중요한 작업(회원탈퇴, 송금, 데이터 변경 등)을 수행하는 것을 막을 수 있어!

 

1 - 4 세션 고정 공격(Session Fixation) 방지

로그인 후 세션 재발급: 로그인 시 기존 세션을 파기하고 새 세션 생성

app.post('/login', (req, res) => {
  req.session.regenerate((err) => {
    if (err) return res.status(500).send('세션 재생성 실패');
    req.session.user = { id: user.id, role: user.role };
    res.send('로그인 성공');
  });
});

 

 

1 - 5. 세션 종료 및 로그아웃 보안 강화

로그아웃 시 세션 파기 및 쿠키 제거
로그아웃 후 캐시된 페이지 접근 차단

// 로그아웃 처리
app.post('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) return res.status(500).send('로그아웃 실패');
    res.clearCookie('connect.sid');
    res.send('로그아웃 완료');
  });
});

// 캐시 방지
app.use((req, res, next) => {
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
  res.setHeader('Pragma', 'no-cache');
  next();
});

 

1 - 6  2FA(이중 인증) 구현 – 관리자 및 슈퍼유저 로그인 시 필수

목표

슈퍼유저 및 관리자 로그인 시 2FA(이중 인증)**을 적용해 보안 강화
OTP(One-Time Password) 방식으로 Google Authenticator와 연동

  1. 1단계: 관리자/슈퍼유저 로그인 (기존 세션 인증)
  2. 2단계: OTP(6자리) 입력 요청 (Google Authenticator 등)
  3. 검증 완료 후: 세션에 2FA_PASSED 플래그 저장
  4. 2FA 미완료 시: 모든 관리자 API 요청 차단

최종 동작 시나리오

관리자 로그인: /login → 세션 생성 (twoFactorAuthPassed = false)
QR 코드 발급: /2fa/generate-qr → Google Authenticator 등록
2FA 인증: /2fa/verify → OTP 입력 및 검증
API 접근: require2FA 미들웨어를 통과해야만 접근 가능

 

 

 

1 - 7 브루트 포스 공격 방지 및 다중 로그인 차단

목표

  1. 브루트 포스 공격 방지
    • 로그인 시도 횟수 제한 (express-rate-limit)
    • 일정 횟수 초과 시 일시적인 차단
  2. 다중 로그인 차단 
    • 세션 ID 기반으로 동일 계정 중복 로그인 시 이전 세션 강제 파기
    • 현재 세션만 유지 (최근 로그인 우선)
    •  

브루트 포스 공격 방지 & 다중 로그인 차단

💡 로그인 시도 횟수 제한 (express-rate-limit) + 다중 로그인 차단 (세션 관리)
📌 목표:
1️⃣ 로그인 시도 횟수 제한: express-rate-limit을 활용하여 로그인 실패 횟수 제한
2️⃣ 일정 횟수 초과 시 차단: 로그인 실패 5회 초과 시 10분간 차단
3️⃣ 다중 로그인 차단: 동일 계정이 여러 번 로그인하면 이전 세션 자동 삭제

 

1. express-rate-limit 적용 (브루트 포스 공격 방지)

🚀 로그인 시도를 제한하여 브루트 포스 공격 방지
설정:

  • 5회 로그인 실패 시 10분 동안 로그인 차단

📌 📂 middlewares/rateLimit.js (새 파일 생성)

import rateLimit from "express-rate-limit";

const loginLimiter = rateLimit({
  windowMs: 10 * 60 * 1000, // 🔥 10분 동안
  max: 5, // 🔥 최대 5번 시도 가능
  message: { resultCode: "F-2", msg: "로그인 시도가 너무 많습니다. 10분 후 다시 시도해주세요." },
  standardHeaders: true, // 🛡️ Rate-Limit 헤더 제공
  legacyHeaders: false, // 🛡️ X-Rate-Limit 헤더 제거
});

export default loginLimiter;

2. 로그인 API에 express-rate-limit 적용

📌 middlewares/rateLimit.js에서 만든 loginLimiter를 로그인 라우트에 적용

 

routes/auth.js

import express from "express";
import { login, logout } from "../controllers/auth.js";
import loginLimiter from "../middlewares/rateLimit.js"; // ✅ 브루트 포스 방지 미들웨어 추가

const router = express.Router();

router.post("/login", loginLimiter, login); // 🔥 로그인 시도 횟수 제한 적용
router.post("/logout", logout);

export default router;

3. 다중 로그인 차단 (이전 세션 강제 종료)

📌 세션 ID를 저장하고, 중복 로그인 시 기존 세션을 삭제하는 방식
동일 계정 중복 로그인 시, 가장 최근 세션만 유지

 

middlewares/sessionManager.js

const activeSessions = new Map(); // 🛡️ 사용자 ID별 활성 세션 저장

const preventMultipleSessions = (req, res, next) => {
  const userId = req.session?.user?.id || req.session?.expert?.id || req.session?.superuser?.id;

  if (userId) {
    // ✅ 동일 유저가 기존 세션으로 로그인한 경우, 기존 세션 제거
    if (activeSessions.has(userId)) {
      console.log(`🔴 [SESSION] 기존 세션 강제 종료: User ${userId}`);
      activeSessions.get(userId).destroy(); // 🔥 기존 세션 강제 파기
    }

    // ✅ 현재 세션을 저장하여 이후 로그인 시 제거할 수 있도록 함
    activeSessions.set(userId, req.session);
  }

  next();
};

export default preventMultipleSessions;

4. 로그인 API에 다중 로그인 차단 적용

📌 middlewares/sessionManager.js에서 만든 **preventMultipleSessions**를 로그인 시 실행

controllers/auth.js 

import pool from "../config/db.js";
import bcrypt from "bcrypt";
import preventMultipleSessions from "../middlewares/sessionManager.js"; // ✅ 다중 로그인 차단 미들웨어 추가

// ✅ 로그인
const login = async (req, res) => {
  const { email, password } = req.body;

  try {
    const [user] = await pool.query("SELECT * FROM User WHERE email = ?", [email]);
    if (!user || user.length === 0) {
      return res.status(400).json({ message: "이메일 또는 비밀번호가 잘못되었습니다." });
    }

    const isMatch = await bcrypt.compare(password, user[0].password);
    if (!isMatch) {
      return res.status(400).json({ message: "이메일 또는 비밀번호가 잘못되었습니다." });
    }

    req.session.user = {
      id: user[0].id,
      email: user[0].email,
      name: user[0].representative_name,
      member_type: "user",
    };

    preventMultipleSessions(req, res, () => { // ✅ 다중 로그인 차단 미들웨어 실행
      res.status(200).json({
        resultCode: "S-1",
        message: "로그인 성공",
        data: req.session.user,
      });
    });
  } catch (error) {
    console.error("❌ [LOGIN] 로그인 오류:", error);
    res.status(500).json({ resultCode: "F-1", msg: "서버 에러 발생", error: error.message });
  }
};

export { login };

 

 

인증 및 세션 보안 점검표

세션 보안 강화: httpOnly, secure, sameSite 완료 세션 쿠키 보안 설정 (XSS, CSRF 방지)
세션 타임아웃 및 재인증 (30분 유휴, 최대 2시간) 완료 일정 시간 후 자동 로그아웃 및 재로그인 요구
CSRF 방지 (csurf 토큰 및 sameSite) 완료 크로스사이트 요청 방지 (CSRF 방어)
세션 고정 공격(Session Fixation) 방지 완료 로그인 시 req.session.regenerate()로 세션 재발급
로그아웃 보안 강화: 세션 파기 및 캐시 무효화 완료 req.session.destroy() 및 캐싱 방지
2FA(이중 인증) – 관리자 및 슈퍼유저 필수 완료 speakeasy 기반 OTP 및 2FA 미들웨어 적용
브루트 포스 공격 방지 (express-rate-limit) 완료 로그인 시도 횟수 제한 및 속도 제한(slowDown)
다중 로그인 차단 완료 서버 메모리(activeSessions) 기반 중복 로그인 차단