개인정보-컴플라이언스-웹애플리케이션 (3. 데이터베이스 및 개인정보 보호)

2025. 2. 15. 20:45프로젝트

728x90
반응형

데이터베이스 및 개인정보 보호 – 시작하기

📌 이 단계의 목표:
데이터베이스 보안 강화 (암호화, 접근 제한, 백업 설정)
개인정보 보호 조치 (비밀번호 암호화, 민감 데이터 보호)
SQL Injection 및 권한 관리 최적화

 

데이터베이스 및 개인정보 보호 – 세부 항목

✅ 비밀번호 암호화 bcrypt 또는 argon2로 비밀번호 해싱 및 저장 미완료
✅ DB 접근 권한 제한 관리자만 중요 데이터 조회 가능 (role-based access) 미완료
✅ SQL Injection 방지 (추가 강화) ORM 사용 및 모든 입력값 검증 (Sequelize, Prepared Statement) 부분 완료
✅ 데이터 암호화 (민감 정보 보호) 이메일, 전화번호 등 AES-256 또는 환경변수 암호화 적용 미완료
✅ 백업 및 로그 보관 정책 DB 자동 백업 및 접근 로그 기록 (winston) 미완료

 

1. 비밀번호 암호화 – bcrypt 적용

📌 목표:
✅ 사용자 비밀번호를 안전하게 저장하기 위해 단방향 해시 적용
✅ bcrypt 사용하여 암호화 및 보안 강화
✅ 비밀번호 비교 함수 구현

 

설치 명령어

npm install bcrypt

비밀번호 해싱 및 저장 방법

📌 bcrypt를 사용하여 비밀번호 해싱 (암호화)

import bcrypt from "bcrypt";

const saltRounds = 10; // 해시 반복 횟수 (10~12 추천)

// 비밀번호 해싱 함수
export const hashPassword = async (password) => {
  return await bcrypt.hash(password, saltRounds);
};

 

📌 사용자 회원가입 시 비밀번호 암호화 (routes/auth.js)

import { hashPassword } from "../utils/security.js";

app.post("/register", async (req, res) => {
  try {
    const { email, password, name } = req.body;

    // ✅ 비밀번호 암호화
    const hashedPassword = await hashPassword(password);

    // ✅ DB에 저장 (Sequelize 예제)
    const newUser = await User.create({
      email,
      password: hashedPassword, // 암호화된 비밀번호 저장
      name,
    });

    res.status(201).json({ message: "회원가입 성공!" });
  } catch (error) {
    res.status(500).json({ message: "회원가입 중 오류 발생", error });
  }
});

 

비밀번호 비교 함수 (로그인 시 사용)

📌 로그인 시 입력된 비밀번호와 암호화된 비밀번호 비교

// 비밀번호 검증 함수
export const comparePassword = async (plainPassword, hashedPassword) => {
  return await bcrypt.compare(plainPassword, hashedPassword);
};

 

📌 로그인 라우트 (routes/auth.js)

import { comparePassword } from "../utils/security.js";

app.post("/login", async (req, res) => {
  try {
    const { email, password } = req.body;

    // ✅ 사용자 조회
    const user = await User.findOne({ where: { email } });
    if (!user) return res.status(401).json({ message: "이메일이 존재하지 않습니다." });

    // ✅ 비밀번호 검증
    const isMatch = await comparePassword(password, user.password);
    if (!isMatch) return res.status(401).json({ message: "비밀번호가 올바르지 않습니다." });

    // ✅ 로그인 성공
    req.session.user = { id: user.id, email: user.email };
    res.status(200).json({ message: "로그인 성공!" });
  } catch (error) {
    res.status(500).json({ message: "로그인 중 오류 발생", error });
  }
});

 

 

2. 데이터베이스 접근 권한 제한

📌 목표:
관리자(SuperUser)만 특정 데이터 접근 가능
일반 유저(User)는 본인 데이터만 조회 가능
SQL Injection 방지를 위한 ORM 권한 관리 강화

 

데이터베이스 접근 권한 모델링 (RBAC 적용)

RBAC(Role-Based Access Control, 역할 기반 접근 제어)를 적용하여 유저(User), 전문가(Expert), 슈퍼유저(SuperUser)의 데이터 접근을 제한해야 합니다.

 

📌 역할(Role)별 접근 권한 예시

User 본인이 등록한 시스템 조회, 자가진단 수행
Expert 슈퍼유저가 매칭한 시스템 피드백 제공
SuperUser 모든 시스템 조회, 유저 관리, 전문가 매칭

 

접근 권한 미들웨어 구현 (middlewares/auth.js)

📌 사용자 역할(Role) 기반 접근 제한 미들웨어

// ✅ 인증 미들웨어
const requireAuth = (req, res, next) => {
  if (!req.session?.user && !req.session?.expert && !req.session?.superuser) {
    return res.status(401).json({ message: "로그인이 필요합니다." });
  }
  next();
};

// ✅ 슈퍼유저 전용 인증 미들웨어
const requireSuperUser = (req, res, next) => {
  if (!req.session?.superuser) {
    return res.status(403).json({ message: "슈퍼유저 권한이 필요합니다." });
  }
  next();
};

 

SQL Injection 방지 및 ORM 권한 적용

📌 Sequelize ORM 사용으로 SQL Injection 방지 강화

app.post("/login", async (req, res) => {
  try {
    const { email, password } = req.body;

    // ✅ SQL Injection 방지: ORM 사용
    const user = await User.findOne({ where: { email } });
    if (!user) return res.status(401).json({ message: "이메일이 존재하지 않습니다." });

    // ✅ 비밀번호 검증
    const isMatch = await comparePassword(password, user.password);
    if (!isMatch) return res.status(401).json({ message: "비밀번호가 올바르지 않습니다." });

    req.session.user = { id: user.id, email: user.email, role: user.role };
    res.status(200).json({ message: "로그인 성공!" });
  } catch (error) {
    res.status(500).json({ message: "로그인 중 오류 발생", error });
  }
});

 

Sequelize ORM을 사용하면 SQL Injection을 자동으로 방지할 수 있음!
직접 SQL을 쓰지 말고 where: {}을 이용하면 안전함!
로그인, 회원가입, 조회 API에서 ORM 방식으로 데이터 조회하기!

 

 

 

3. 데이터 암호화 (민감 정보 보호) – AES-256 적용

📌 목표:
비밀번호뿐만 아니라 민감한 개인정보(이메일, 전화번호 등)도 암호화
AES-256 암호화 적용하여 데이터 보호
데이터 복호화(복원) 기능 추가

 

데이터 암호화가 필요한 이유

일반적으로 데이터는 DB에 평문(Plain Text)으로 저장되면 보안에 취약합니다
예를 들어, 해커가 DB를 탈취하면 모든 유저의 이메일과 전화번호가 그대로 노출될 수 있습니다.

그래서 AES-256 같은 강력한 암호화 알고리즘을 사용해서 데이터를 보호해야 합니다
이렇게 하면 해커가 데이터를 훔쳐도 복호화 키(Secret Key)가 없으면 읽을 수 없습니다

 

AES-256 암호화를 위한 crypto 모듈 설치

📌 Node.js 내장 crypto 모듈 사용 (추가 라이브러리 설치 불필요!)

import crypto from "crypto";
import dotenv from "dotenv";

dotenv.config();

 

📌 .env 파일에 암호화 키 추가

ENCRYPTION_KEY=your_secret_32_character_key
IV_KEY=your_16_character_iv

 

✔️ ENCRYPTION_KEY는 32바이트(256비트), IV_KEY는 **16바이트(128비트)**여야 함!

 

✔️ 키를 무작위로 생성하고 싶다면

console.log(crypto.randomBytes(32).toString("hex")); // 32바이트 키 생성
console.log(crypto.randomBytes(16).toString("hex")); // 16바이트 IV 생성

 

암호화 & 복호화 함수 만들기 (utils/encryption.js)

AES-256 암호화 함수

import crypto from "crypto";
import dotenv from "dotenv";

dotenv.config();

const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY; // 32바이트 키
const IV_KEY = process.env.IV_KEY; // 16바이트 IV

export const encryptData = (text) => {
  const iv = Buffer.from(IV_KEY, "hex");
  const cipher = crypto.createCipheriv("aes-256-cbc", Buffer.from(ENCRYPTION_KEY, "hex"), iv);
  let encrypted = cipher.update(text, "utf8", "hex");
  encrypted += cipher.final("hex");
  return encrypted;
};

export const decryptData = (encryptedText) => {
  const iv = Buffer.from(IV_KEY, "hex");
  const decipher = crypto.createDecipheriv("aes-256-cbc", Buffer.from(ENCRYPTION_KEY, "hex"), iv);
  let decrypted = decipher.update(encryptedText, "hex", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
};

 

✔️ AES-256-CBC 알고리즘을 사용해 데이터 암호화!
✔️ 암호화된 데이터는 복호화 키 없이는 절대 읽을 수 없음!

 

 

DB 저장 시 민감 정보 암호화 (routes/auth.js)

📌 유저 회원가입 시 이메일 & 전화번호 암호화

import { encryptData } from "../utils/encryption.js";

app.post("/register", async (req, res) => {
  try {
    const { email, phone_number, password } = req.body;

    // ✅ 이메일 & 전화번호 암호화
    const encryptedEmail = encryptData(email);
    const encryptedPhone = encryptData(phone_number);

    const newUser = await User.create({
      email: encryptedEmail,
      phone_number: encryptedPhone,
      password, // 비밀번호는 이미 bcrypt 해싱 적용됨
    });

    res.status(201).json({ message: "회원가입 성공!" });
  } catch (error) {
    res.status(500).json({ message: "회원가입 중 오류 발생", error });
  }
});

 

 

유저 정보 조회 시 자동 복호화

(routes/user.js)

📌 유저 정보 조회 시 복호화된 데이터 제공

import { decryptData } from "../utils/encryption.js";

app.get("/user", requireAuth, async (req, res) => {
  try {
    const user = await User.findOne({ where: { id: req.session.user.id } });

    if (!user) return res.status(404).json({ message: "사용자를 찾을 수 없습니다." });

    // ✅ 이메일 & 전화번호 복호화
    const decryptedEmail = decryptData(user.email);
    const decryptedPhone = decryptData(user.phone_number);

    res.json({
      id: user.id,
      email: decryptedEmail,
      phone_number: decryptedPhone,
    });
  } catch (error) {
    res.status(500).json({ message: "사용자 정보 조회 오류", error });
  }
});

 

✔️ DB에서 꺼내올 때 자동으로 복호화해서 유저에게 제공!
✔️ DB 내부에는 암호화된 데이터만 저장되므로 보안이 강화됨!

 

최종 점검표 – 데이터 암호화 적용 완료

보안항목 현재상태 설명
✅ AES-256 암호화 함수 (encryptData, decryptData) 완료 crypto 모듈을 사용해 강력한 암호화 적용
✅ 회원가입 시 이메일 & 전화번호 암호화 완료 DB에 평문 데이터 저장 금지
✅ 유저 정보 조회 시 자동 복호화 완료 DB에서 조회된 데이터는 자동 복호화