feat: Implement password masking and encryption in data services
- Added a new function `maskPasswordColumns` to mask password fields in data responses, ensuring sensitive information is not exposed. - Integrated password handling in `DynamicFormService` to encrypt new passwords and maintain existing ones when empty values are provided. - Enhanced logging for better tracking of password field updates and masking failures, improving overall security and debugging capabilities.
This commit is contained in:
@@ -18,6 +18,45 @@ import { pool } from "../database/db"; // 🆕 Entity 조인을 위한 pool impo
|
||||
import { buildDataFilterWhereClause } from "../utils/dataFilterUtil"; // 🆕 데이터 필터 유틸
|
||||
import { v4 as uuidv4 } from "uuid"; // 🆕 UUID 생성
|
||||
|
||||
/**
|
||||
* 비밀번호(password) 타입 컬럼의 값을 빈 문자열로 마스킹
|
||||
* - table_type_columns에서 input_type = 'password'인 컬럼을 조회
|
||||
* - 데이터 응답에서 해당 컬럼 값을 비워서 해시값 노출 방지
|
||||
*/
|
||||
async function maskPasswordColumns(tableName: string, data: any): Promise<any> {
|
||||
try {
|
||||
const passwordCols = await query<{ column_name: string }>(
|
||||
`SELECT DISTINCT column_name FROM table_type_columns
|
||||
WHERE table_name = $1 AND input_type = 'password'`,
|
||||
[tableName]
|
||||
);
|
||||
if (passwordCols.length === 0) return data;
|
||||
|
||||
const passwordColumnNames = new Set(passwordCols.map(c => c.column_name));
|
||||
|
||||
// 단일 객체 처리
|
||||
const maskRow = (row: any) => {
|
||||
if (!row || typeof row !== "object") return row;
|
||||
const masked = { ...row };
|
||||
for (const col of passwordColumnNames) {
|
||||
if (col in masked) {
|
||||
masked[col] = ""; // 해시값 대신 빈 문자열
|
||||
}
|
||||
}
|
||||
return masked;
|
||||
};
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(maskRow);
|
||||
}
|
||||
return maskRow(data);
|
||||
} catch (error) {
|
||||
// 마스킹 실패해도 원본 데이터 반환 (서비스 중단 방지)
|
||||
console.warn("⚠️ password 컬럼 마스킹 실패:", error);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
interface GetTableDataParams {
|
||||
tableName: string;
|
||||
limit?: number;
|
||||
@@ -622,14 +661,14 @@ class DataService {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: normalizedGroupRows, // 🔧 배열로 반환!
|
||||
data: await maskPasswordColumns(tableName, normalizedGroupRows), // 🔧 배열로 반환! + password 마스킹
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: normalizedRows[0], // 그룹핑 없으면 단일 레코드
|
||||
data: await maskPasswordColumns(tableName, normalizedRows[0]), // 그룹핑 없으면 단일 레코드 + password 마스킹
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -648,7 +687,7 @@ class DataService {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: result[0],
|
||||
data: await maskPasswordColumns(tableName, result[0]), // password 마스킹
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`레코드 상세 조회 오류 (${tableName}/${id}):`, error);
|
||||
|
||||
Reference in New Issue
Block a user