ERP-node/.cursor/rules/security-guide.mdc

248 lines
6.5 KiB
Plaintext
Raw Permalink Normal View History

2025-08-21 09:41:46 +09:00
---
description:
globs:
alwaysApply: true
---
# 보안 가이드
## 인증 및 인가
### 세션 기반 인증
현재 시스템은 세션 기반 인증을 사용합니다:
```java
// 사용자 인증 정보 저장
PersonBean person = new PersonBean();
person.setUserId(userId);
person.setUserName(userName);
request.getSession().setAttribute(Constants.PERSON_BEAN, person);
// 인증 정보 조회
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
if (person == null) {
// 로그인 페이지로 리다이렉트
}
```
### 권한 관리 구조
- **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);
}
```
## 데이터 보안
### SQL 인젝션 방지
**올바른 방법** - 파라미터 바인딩 사용:
```xml
<select id="selectUser" parameterType="map" resultType="map">
SELECT * FROM user_info
WHERE user_id = #{userId} <!-- 안전한 파라미터 바인딩 -->
</select>
```
**위험한 방법** - 직접 문자열 치환:
```xml
<select id="selectUser" parameterType="map" resultType="map">
SELECT * FROM user_info
WHERE user_id = '${userId}' <!-- SQL 인젝션 위험 -->
</select>
```
### 패스워드 보안
```java
// 패스워드 암호화 (EncryptUtil 사용)
String encryptedPassword = EncryptUtil.encrypt(plainPassword);
// 패스워드 검증
boolean isValid = EncryptUtil.matches(plainPassword, encryptedPassword);
```
### 입력값 검증
```java
// CommonUtils를 사용한 입력값 정제
String safeInput = CommonUtils.checkNull(request.getParameter("input"));
if (StringUtils.isEmpty(safeInput)) {
throw new IllegalArgumentException("필수 입력값이 누락되었습니다.");
}
```
## 세션 보안
### 세션 설정
[web.xml](mdc:WebContent/WEB-INF/web.xml)에서 세션 타임아웃 설정:
```xml
<session-config>
<session-timeout>1440</session-timeout> <!-- 24시간 -->
</session-config>
```
### 세션 무효화
```java
// 로그아웃 시 세션 무효화
@RequestMapping("/logout.do")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:/login.do";
}
```
## 파일 업로드 보안
### 파일 확장자 검증
```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);
}
```
## 보안 체크리스트
### 코드 레벨
- [ ] SQL 인젝션 방지 (#{} 파라미터 바인딩 사용)
- [ ] XSS 방지 (출력값 이스케이프)
- [ ] CSRF 방지 (토큰 검증)
- [ ] 입력값 검증 및 정제
- [ ] 패스워드 암호화 저장
### 설정 레벨
- [ ] 세션 타임아웃 설정
- [ ] 파일 업로드 제한
- [ ] 오류 페이지 설정
- [ ] HTTPS 사용 (운영 환경)
- [ ] 보안 헤더 설정
### 운영 레벨
- [ ] 정기적인 보안 점검
- [ ] 로그 모니터링
- [ ] 권한 정기 검토
- [ ] 패스워드 정책 적용
- [ ] 백업 데이터 암호화