chore: Remove obsolete .classpath file

- Deleted the .classpath file as it is no longer needed in the project structure.
- This cleanup helps maintain a tidy codebase by removing unnecessary files that may cause confusion or clutter.
This commit is contained in:
kjs
2026-03-04 15:31:45 +09:00
parent 96637a9cb6
commit ea8b4ce5dc
2303 changed files with 480 additions and 850602 deletions
+79 -201
View File
@@ -1,248 +1,126 @@
---
description:
globs:
description:
globs:
alwaysApply: true
---
# 보안 가이드
## 인증 및 인가
### 세션 기반 인증
현재 시스템은 세션 기반 인증을 사용합니다:
### JWT 기반 인증
현재 시스템은 JWT 토큰 기반 인증을 사용합니다:
```java
// 사용자 인증 정보 저장
PersonBean person = new PersonBean();
person.setUserId(userId);
person.setUserName(userName);
request.getSession().setAttribute(Constants.PERSON_BEAN, person);
```typescript
// 토큰 생성 (로그인 시)
const token = jwt.sign(
{ userId, companyCode, role },
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
);
// 인증 정보 조회
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
if (person == null) {
// 로그인 페이지로 리다이렉트
}
// 토큰 검증 (미들웨어)
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
```
### 권한 관리 구조
- **AUTH_GROUP**: 권한 그룹 정의
- **MENU_AUTH_GROUP**: 메뉴별 권한 그룹 매핑
- **USER_AUTH**: 사용자별 권한 할당
- **auth_group**: 권한 그룹 정의
- **menu_auth_group**: 메뉴별 권한 그룹 매핑
- **user_auth**: 사용자별 권한 할당
### 메뉴 접근 권한 체크
```java
public HashMap<String, Object> checkUserMenuAuth(HttpServletRequest request, Map<String, Object> paramMap) {
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
String userId = person.getUserId();
paramMap.put("userId", userId);
paramMap.put("menuUrl", request.getRequestURI());
// 권한 체크 쿼리 실행
return sqlSession.selectOne("admin.checkUserMenuAuth", paramMap);
}
```
### 인증 미들웨어
`backend-node/src/middleware/authMiddleware.ts` 참조
## 데이터 보안
### SQL 인젝션 방지
**올바른 방법** - 파라미터 바인딩 사용:
```xml
<select id="selectUser" parameterType="map" resultType="map">
SELECT * FROM user_info
WHERE user_id = #{userId} <!-- 안전한 파라미터 바인딩 -->
</select>
```typescript
const result = await pool.query(
'SELECT * FROM user_info WHERE user_id = $1',
[userId]
);
```
**위험한 방법** - 직접 문자열 치환:
```xml
<select id="selectUser" parameterType="map" resultType="map">
SELECT * FROM user_info
WHERE user_id = '${userId}' <!-- SQL 인젝션 위험 -->
</select>
**위험한 방법** - 직접 문자열 삽입:
```typescript
const result = await pool.query(
`SELECT * FROM user_info WHERE user_id = '${userId}'`
);
```
### 패스워드 보안
```java
// 패스워드 암호화 (EncryptUtil 사용)
String encryptedPassword = EncryptUtil.encrypt(plainPassword);
```typescript
import bcrypt from 'bcryptjs';
// 패스워드 검증
boolean isValid = EncryptUtil.matches(plainPassword, encryptedPassword);
// 해싱
const hashedPassword = await bcrypt.hash(plainPassword, 10);
// 검증
const isValid = await bcrypt.compare(plainPassword, hashedPassword);
```
### 입력값 검증
```java
// CommonUtils를 사용한 입력값 정제
String safeInput = CommonUtils.checkNull(request.getParameter("input"));
if (StringUtils.isEmpty(safeInput)) {
throw new IllegalArgumentException("필수 입력값이 누락되었습니다.");
```typescript
import Joi from 'joi';
const schema = Joi.object({
userId: Joi.string().required().max(50),
email: Joi.string().email().required(),
});
const { error, value } = schema.validate(req.body);
if (error) {
return res.status(400).json({ success: false, message: error.message });
}
```
## 세션 보안
## 보안 미들웨어
### 세션 설정
[web.xml](mdc:WebContent/WEB-INF/web.xml)에서 세션 타임아웃 설정:
```xml
<session-config>
<session-timeout>1440</session-timeout> <!-- 24시간 -->
</session-config>
### Helmet (보안 헤더)
```typescript
import helmet from 'helmet';
app.use(helmet());
```
### 세션 무효화
```java
// 로그아웃 시 세션 무효화
@RequestMapping("/logout.do")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/login.do";
}
### Rate Limiting
```typescript
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
});
app.use('/api/', limiter);
```
## 파일 업로드 보안
### 파일 확장자 검증
```java
public boolean isAllowedFileType(String fileName) {
String[] allowedExtensions = {".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".xls", ".xlsx"};
String extension = fileName.toLowerCase().substring(fileName.lastIndexOf("."));
return Arrays.asList(allowedExtensions).contains(extension);
}
```
### 파일 크기 제한
```java
// web.xml에서 파일 업로드 크기 제한
<multipart-config>
<max-file-size>10485760</max-file-size> <!-- 10MB -->
<max-request-size>52428800</max-request-size> <!-- 50MB -->
</multipart-config>
```
### 안전한 파일명 생성
```java
// FileRenameClass 사용하여 안전한 파일명 생성
String safeFileName = FileRenameClass.rename(originalFileName);
```
## XSS 방지
### 출력값 이스케이프
JSP에서 사용자 입력값 출력 시:
```jsp
<!-- 안전한 출력 -->
<c:out value="${userInput}" escapeXml="true"/>
<!-- 위험한 출력 -->
${userInput} <!-- XSS 공격 가능 -->
```
### JavaScript에서 데이터 처리
```javascript
// 안전한 방법
var safeData = $('<div>').text(userInput).html();
// 위험한 방법
var dangerousData = userInput; // XSS 공격 가능
```
## CSRF 방지
### 토큰 기반 CSRF 방지
```jsp
<!-- 폼에 CSRF 토큰 포함 -->
<form method="post" action="save.do">
<input type="hidden" name="csrfToken" value="${csrfToken}"/>
<!-- 기타 입력 필드 -->
</form>
```
```java
// 컨트롤러에서 CSRF 토큰 검증
@RequestMapping("/save.do")
public String save(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
String sessionToken = (String)request.getSession().getAttribute("csrfToken");
String requestToken = (String)paramMap.get("csrfToken");
if (!sessionToken.equals(requestToken)) {
throw new SecurityException("CSRF 토큰이 일치하지 않습니다.");
}
// 정상 처리
}
```
## 로깅 및 감사
### 보안 이벤트 로깅
```java
private static final Logger securityLogger = LoggerFactory.getLogger("SECURITY");
public void logSecurityEvent(String event, String userId, String details) {
securityLogger.info("Security Event: {} | User: {} | Details: {}", event, userId, details);
}
// 사용 예시
logSecurityEvent("LOGIN_SUCCESS", userId, request.getRemoteAddr());
logSecurityEvent("ACCESS_DENIED", userId, request.getRequestURI());
```
### 민감 정보 마스킹
```java
public String maskSensitiveData(String data) {
if (data == null || data.length() < 4) {
return "****";
}
return data.substring(0, 2) + "****" + data.substring(data.length() - 2);
}
```
## 환경별 보안 설정
### 개발 환경
- 상세한 오류 메시지 표시
- 디버그 모드 활성화
- 보안 제약 완화
### 운영 환경
- 일반적인 오류 메시지만 표시
- 디버그 모드 비활성화
- 엄격한 보안 정책 적용
```java
// 환경별 설정 예시
if (Constants.IS_PRODUCTION) {
// 운영 환경 설정
response.sendError(HttpServletResponse.SC_FORBIDDEN, "접근이 거부되었습니다.");
} else {
// 개발 환경 설정
response.sendError(HttpServletResponse.SC_FORBIDDEN, "권한 부족: " + detailedMessage);
}
### CORS 설정
```typescript
import cors from 'cors';
app.use(cors({
origin: process.env.CORS_ORIGIN,
credentials: true,
}));
```
## 보안 체크리스트
### 코드 레벨
- [ ] SQL 인젝션 방지 (#{} 파라미터 바인딩 사용)
- [ ] XSS 방지 (출력값 이스케이프)
- [ ] CSRF 방지 (토큰 검증)
- [ ] 입력값 검증 및 정제
- [ ] 패스워드 암호화 저장
- [ ] SQL 인젝션 방지 ($1 파라미터 바인딩 사용)
- [ ] XSS 방지 (React 자동 이스케이프 활용)
- [ ] 입력값 검증 (Joi 스키마)
- [ ] 패스워드 bcrypt 해싱
- [ ] JWT 토큰 만료 설정
### 설정 레벨
- [ ] 세션 타임아웃 설정
- [ ] 파일 업로드 제한
- [ ] 오류 페이지 설정
- [ ] Helmet 보안 헤더
- [ ] Rate Limiting 적용
- [ ] CORS 적절한 설정
- [ ] HTTPS 사용 (운영 환경)
- [ ] 보안 헤더 설정
- [ ] 환경 변수로 시크릿 관리
### 운영 레벨
- [ ] 정기적인 보안 점검
- [ ] 로그 모니터링
- [ ] winston 로깅 모니터링
- [ ] 권한 정기 검토
- [ ] 패스워드 정책 적용
- [ ] 백업 데이터 암호화
- [ ] 백업 데이터 암호화