- 💡해당 게시글은 최범균님의 ‘주니어 백엔드 개발자가 반드시 알아야 할 실무 지식’을 개인 공부목적으로 메모하였습니다.
8장에서 다루는 내용
- 중요한 보안
- 인증과 인가
- 암호화
- HMAC을 이용한 데이터 검증
- 방화벽으로 트래픽 제한
- 감사 로그(Audit Log) 남기기
- 데이터 노출 줄이기
- 비정상 접근 처리
- 시큐어 코딩
- 개인 보안
중요한 보안
보안 사고의 현황
- 최근 몇 년간 대규모 개인정보 유출 사고가 빈번하게 발생하고 있음
- 주요 유출 경로
- 수백만 건의 계정 정보가 노출되는 대형 서비스 해킹
- 부적절한 접근 제어로 인한 고객 정보 유출
- 취약한 인증 시스템을 통한 무단 접근
URL 파라미터 기반 취약점
- 많은 웹 서비스에서 발견되는 전형적인 보안 취약점
-
문제가 되는 URL 구조 예시
1 2
https://example.com/user/info?userId=12345 https://example.com/order/detail?orderId=98765
- 취약점의 원인
- URL 파라미터에 사용자 식별 정보를 직접 노출
- 요청자가 해당 리소스에 접근할 권한이 있는지 검증하지 않음
- 파라미터 값만 변경하면 다른 사용자의 정보에 접근 가능
IDOR (Insecure Direct Object Reference) 취약점
- IDOR은 권한 검증 없이 직접적인 객체 참조를 허용하는 보안 취약점임
- 공격 시나리오
- 공격자가 자신의 계정으로 로그인
- URL이나 API 파라미터의 식별값을 다른 값으로 변경
- 권한 검증이 없으면 타인의 정보에 무단 접근 가능
- 대표적인 예시
- 주문 번호만 바꿔서 다른 사람의 주문 내역 조회
- 사용자 ID만 변경하여 다른 회원의 개인정보 열람
- 문서 ID를 조작하여 타인의 문서 다운로드
API 파라미터 검증 미흡 사례
- 인증은 되어있으나 인가 검증이 없는 API의 문제점
-
비밀번호 변경 API 예시
1 2 3 4 5 6
// 취약한 API 예시 POST /api/user/changePassword { "userId": "12345", "newPassword": "newPassword123" }
- 이러한 API의 문제점
- 현재 요청한 사용자가 로그인한 본인인지만 확인하고,
userId가 본인의 ID인지는 검증하지 않음 - 다른 사용자의
userId를 입력하면 타인의 비밀번호를 변경할 수 있음 - 현재 비밀번호 확인 과정이 없어 계정 탈취 시 즉시 악용 가능
- 현재 요청한 사용자가 로그인한 본인인지만 확인하고,
보안 취약점의 공통 패턴
- 인증(Authentication)과 인가(Authorization)의 혼동
- 인증
- 사용자가 누구인지 확인 (로그인 여부)
- 인가
- 해당 사용자가 특정 리소스에 접근할 권한이 있는지 확인
- 인증
- 클라이언트에서 전달받은 값을 맹목적으로 신뢰
- 서버 측에서 적절한 권한 검증 로직 누락
인증과 인가
개념
- 서버 개발에서 가장 기본적인 보안은 인증과 인가임
- 인증(Authentication)
- 사용자가 누구인지 확인하는 과정
- 인가(Authorization)
- 사용자에게 자원에 접근할 수 있는 권한을 부여하는 것

로그인과 2단계 인증
- 아이디와 암호를 입력하는 로그인은 인증의 한 형태임
- 보안을 강화하기 위해 2단계 인증(Two-Factor Authentication)을 사용하기도 함
- SMS 인증 코드
- 이메일 인증 링크
- OTP(One-Time Password) 앱
- 생체 인증(지문, 안면 인식)
토큰 기반 인증
- 사용자가 누구인지 확인하는 데 성공하면 서버는 클라이언트에 문자열로 된 토큰을 제공함
- 클라이언트는 이후 각 요청마다 이 토큰을 함께 보내 자신이 누구인지 증명함

토큰 저장 방식
- 서버는 토큰과 사용자 식별 정보를 DB나 레디스와 같은 별도 저장소에 보관함
- 별도 저장소를 사용하면 서버를 재시작해도 토큰 데이터가 유지됨

HTTP 세션 예제
1
2
3
4
5
6
7
8
9
10
11
// 기존 세션 조회 (새로 생성하지 않음)
HttpSession session = request.getSession(false);
if (session == null) {
throw new AuthenticationException(); // 세션이 없으면 인증 실패
}
// 세션에서 사용자 데이터 조회
UserSessionData data = (UserSessionData) session.getAttribute("userSessionData");
if (data == null) {
throw new AuthenticationException(); // 사용자 데이터 없으면 인증 실패
}
JWT를 이용한 토큰 생성
- 토큰 자체에 사용자 식별값 정보를 저장하는 방식
-
대표적인 방식이 JWT(JSON Web Token)를 이용하는 것임
1 2 3 4 5 6 7 8
// JWT 토큰 생성 String token = Jwts.builder() .subject("userId") // 사용자 식별값 설정 .signWith(key) // 비밀 키로 서명 .compact(); // 토큰 문자열 생성 // 로그인 응답 반환 return LoginResponse.of(token);
RBAC (Role-Based Access Control)
- 역할 기반 권한 제어 방식
- 사용자에게 역할을 부여하고 역할에 허용된 기능을 실행할 수 있는 권한을 부여함

암호화
데이터 암호화의 필요성
- 유출되면 가장 큰 피해로 이어질 수 있는 데이터 중 하나가 로그인 비밀번호임
- 암호화는 데이터를 특정한 규칙으로 변환하여 원본 유추를 어렵게 만드는 것

단방향 암호화
-
암호화한 데이터를 복호화할 수 없는 암호화 방식임
-
주요 알고리즘
- SHA-256
- MD5
- BCrypt

해시 함수의 특징
-
SHA-256과 같은 해시 함수 알고리즘은 원본 데이터를 유추하기 어렵게 하기 위해 원본 데이터가 조금만 달라도 완전히 다른 해시 값을 생성함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
public static String encrypt(String input) { StringBuilder hexString = new StringBuilder(); try { // SHA-256 해시 알고리즘 인스턴스 생성 MessageDigest digest = MessageDigest.getInstance("SHA-256"); // 입력값을 바이트 배열로 변환하여 해시 생성 byte[] hash = digest.digest(input.getBytes("UTF-8")); // 바이트 배열을 16진수 문자열로 변환 for (byte b : hash) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) hexString.append('0'); // 한 자리면 0 추가 hexString.append(hex); } } catch (Exception e) { throw new RuntimeException(e); } return hexString.toString(); }
충돌 저항성 (Collision Resistance)
- 해시 함수는 원본 데이터의 산출값이 일정한 길이의 해시 값을 생성함
- 길이가 제한되기 때문에 서로 다른 데이터가 같은 해시 값을 생성할 가능성이 있음
- 해시 함수의 생성 결과가 길수록 충돌 발생 가능성이 낮아짐
- SHA-256
- 256비트(32바이트)
- SHA-512
- 512비트(64바이트)
- SHA-256
비밀번호 검증
-
단방향 암호화는 해시 값을 비교하여 두 데이터가 같다고 간주함
1 2 3 4 5 6 7 8 9 10
// 사용자 입력 비밀번호를 해시 값으로 변환 String inputPwdHash = encodePassword(inputPwd); // DB에 저장된 비밀번호 해시 값 조회 String dbPwdHash = selectDbPwd(userId); // 두 해시 값 비교 if (inputPwdHash.equals(dbPwdHash)) { // 비밀번호 일치 - 인증 성공 }
Salt로 보안 강화하기
- 같은 해시 알고리즘을 사용하면 동일한 원본 데이터에 대해 항상 동일한 해시 값이 생성됨
- Salt를 추가하면 같은 원본 데이터라도 다른 해시 값을 생성함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static String encryptWithSalt(String input, String salt) {
StringBuilder hexString = new StringBuilder();
try {
// SHA-256 해시 알고리즘 인스턴스 생성
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(salt.getBytes()); // Salt 값 추가
// Salt + 입력값 해시 생성
byte[] hash = digest.digest(input.getBytes("UTF-8"));
// 바이트 배열을 16진수 문자열로 변환
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append("0");
hexString.append(hex);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return hexString.toString();
}
양방향 암호화
-
암호화와 복호화가 모두 가능한 방식임
-
주요 사용처
- SSH 프로토콜이나 API 호출 시 사용하는 HTTPS처럼 보안이 중요한 데이터 송수신 과정
- 대표적인 알고리즘으로는 AES와 RSA가 있음

AES 대칭 키 암호화
- AES 구성요소
- 키(Key)
- IV(Initialization Vector, 초기화 벡터)
1
2
3
4
5
6
7
public static byte[] generateSecretKey() throws Exception {
// AES 키 생성기 인스턴스 생성
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256); // 256비트 (32바이트) 키 크기 설정
SecretKey secretKey = keyGenerator.generateKey(); // 키 생성
return secretKey.getEncoded(); // 바이트 배열로 반환
}
AES 암호화/복호화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// AES 암호화 메서드
public static String encrypt(String plain, SecretKey key, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // AES 암호화 알고리즘 설정
IvParameterSpec parameterSpec = new IvParameterSpec(iv); // IV 설정
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec); // 암호화 모드 초기화
byte[] encrypted = cipher.doFinal(plain.getBytes("UTF-8")); // 암호화 실행
return Base64.getEncoder().encodeToString(encrypted); // Base64로 인코딩하여 반환
}
// AES 복호화 메서드
public static String decrypt(String encrypted, SecretKey key, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // AES 복호화 알고리즘 설정
IvParameterSpec parameterSpec = new IvParameterSpec(iv); // IV 설정
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec); // 복호화 모드 초기화
byte[] decoded = Base64.getDecoder().decode(encrypted); // Base64 디코딩
byte[] decrypted = cipher.doFinal(decoded); // 복호화 실행
return new String(decrypted, "UTF-8"); // 문자열로 반환
}
RSA 비대칭 키 암호화
- 공개 키/개인 키 쌍을 생성한 뒤에 공개 키를 공유함

1
2
3
4
5
6
// RSA 키 쌍 생성기 인스턴스 생성
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048); // 키 크기 2048비트 설정
KeyPair keyPair = keyGen.generateKeyPair(); // 키 쌍 생성
PublicKey publicKey = keyPair.getPublic(); // 공개 키
PrivateKey privateKey = keyPair.getPrivate(); // 개인 키
RSA 암호화/복호화
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// RSA 공개키로 암호화
public static String encrypt(String plain, PublicKey publicKey) {
Cipher cipher = Cipher.getInstance("RSA"); // RSA 알고리즘 설정
cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 공개키로 암호화 모드 초기화
byte[] encryptedBytes = cipher.doFinal(plain.getBytes("UTF-8")); // 암호화 실행
return Base64.getEncoder().encodeToString(encryptedBytes); // Base64 인코딩
}
// RSA 개인키로 복호화
public static String decrypt(String encrypted, PrivateKey privateKey) {
Cipher cipher = Cipher.getInstance("RSA"); // RSA 알고리즘 설정
cipher.init(Cipher.DECRYPT_MODE, privateKey); // 개인키로 복호화 모드 초기화
byte[] decodedBytes = Base64.getDecoder().decode(encrypted); // Base64 디코딩
byte[] decryptedBytes = cipher.doFinal(decodedBytes); // 복호화 실행
return new String(decryptedBytes, "UTF-8"); // 문자열로 반환
}
HMAC을 이용한 데이터 검증
HMAC이란?
- API 통신을 할 때 클라이언트는 데이터를 생성해서 서버에 전송함
- 공격자가 중간에서 데이터를 가로채거나 위변조할 수 있음
1
2
3
4
5
6
// 클라이언트가 전송하는 데이터 예시
{
"action": "transfer",
"amount": 1000,
"toAccount": "1234567890"
}
- 공격자가 데이터를 조작하는 예시
1
2
3
4
5
6
// 공격자에 의해 변조된 데이터
{
"action": "transfer",
"amount": 100000, // 금액 변조
"toAccount": "9999999999" // 계좌 변조
}
- HMAC은 Hash-based Message Authentication Code의 약자로, 메시지의 무결성과 인증을 보장하기 위해 사용하는 암호화 기술임

HMAC의 주요 용도
- 메시지 무결성
- 메시지가 중간에 위변조되지 않았음
- 인증
- 메시지 발신자를 인증할 수 있음(발신자만 비밀 키 보유)
HMAC 예제 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static class HMAC {
private String secretKey;
public HMAC(String secretKey) {
this.secretKey = secretKey; // 비밀 키 저장
}
// HMAC 생성 메서드
public String hmac(String message) {
try {
// HmacSHA256 알고리즘 인스턴스 생성
Mac mac = Mac.getInstance("HmacSHA256");
// 비밀 키로 SecretKeySpec 생성
SecretKeySpec secretKeySpec =
new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
mac.init(secretKeySpec); // MAC 초기화
// 메시지에 대한 HMAC 생성
byte[] hash = mac.doFinal(message.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(hash); // Base64 인코딩하여 반환
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
방화벽으로 트래픽 제한
방화벽의 필요성
- 서버가 외부에 노출되기 시작하면 포트 스캔부터 다양한 공격이 들어옴
- 가장 기본적인 네트워크 접근 제한은 기본적으로 외부에서 내부로 유입되는 것을 차단하는 것

트래픽 방향 제어
- 방화벽은 네트워크 통신을 두 방향으로 제어함
- 인바운드 트래픽
- 외부에서 내부로 유입되는 것
- 아웃바운드 트래픽
- 내부에서 외부로 유출되는 것
- 인바운드 트래픽

감사 로그(Audit Log) 남기기
감사 로그란?
- 특정 작업, 절차, 사건 또는 장치에 영향을 주는 활동의 순서를 입증하는 보안 관련 기록

대표적인 감사 로그 기록 대상
- 사용자의 로그인/로그아웃 내역
- 암호 초기화 등 설정 변경 내역
- 회사 기밀문서 조회와 같은 중요한 정보 조회 이력
- 계산서의 수정 이력
감사 로그와 일반 로그의 차이
- 일반 로그
- 개발자나 운영자가 문제를 해결하거나 문제 등 파악을 위한 목적
- 감사 로그
- 컴플라이언스나 정책을 지키기 위한 활동 내역을 기록
- 위변조 방지가 더 중요하므로 보안성이 높은 곳에 저장해야 함
- 인증을 통과한 뒤에야 접근할 수 있도록 해야 함
- 로그 메시지에 대한 변경 권한을 제한할 수 있어야 함
데이터 노출 줄이기
데이터 노출의 위험성
- 서비스 운영자는 백오피스에서 다양한 고객 정보를 조회할 수 있음
-
고객 이름, 휴대폰 번호, 배송 주소 등 고객을 특정할 수 있는 민감 정보가 노출됨
- 주의사항
- 개인 정보가 많이 표시될수록 고객 정보를 쉽게 취득할 수 있음
- 페이지당 표시되는 정보의 양이 많을수록 대량 유출 위험이 증가함
- 목록 화면에서 개인정보가 노출되면 스크린샷 하나로 다수의 정보 취득 가능

데이터 노출 최소화 전략
- 고객 데이터를 마스킹하여 표시해야 함
- API 응답은 원본 데이터가 보이지 않도록 서버에서 마스킹해야 함
- 프런트 코드에서만 마스킹하면 개발자 도구로 원본 데이터를 알아낼 수 있음
고객 정보 조회 제한
- 소수 인원에게만 고객 목록 조회 권한을 부여함
- 권한이 없는 나머지 사용자는 고객의 이메일 또는 휴대폰 번호나 이름을 사용해서 고객 정보를 조회할 수 있게 함
- 자동화를 의심할 수 있는 경우 도구를 사용해서 개인 정보를 수집하는 것을 막을 수 있음
- 고객 목록 기능을 짧은 시간 동안 빈번하게 실행한 경우
- 고객 상세 정보를 짧은 시간 간격으로 조회하는 사용자가 있는 경우
- 비정상 사용으로 인지하고 접근을 차단
비정상 접근 처리
비정상 접근이란?
-
사용자가 평소와 다른 행동 패턴을 보이면 비정상 접근으로 판단하고 사용자에게 해당 내용을 알려주는 시나리오가 있음
-
비정상 접근의 예
- 평소와 다른 장소에서 로그인함
- 평소와 다른 기기에서 로그인함
- 로그인에 여러 차례 실패함

비정상 접근 대응 방법
- 계정 관련 확인을 사용자에게만 맡기지 않고 시스템적으로 보안을 강화함
- 이를 통해 사용자는 예상치 못한 계정 탈취 상황에 대처할 수 있게 됨
- 사용자에게 알리는 것을 넘어 계정 사용 중지와 같은 정책을 적용할 수도 있음
- 일정 횟수 이상 로그인 실패 시 임시적으로 계정을 잠금
- 이를 통해 Brute Force 공격에 대응함
- 이메일이나 SMS로 계정 활동 알림 전송
Brute Force 공격
- 특정한 암호를 풀기 위해 가능한 모든 값을 대입하는 것을 의미함
- 모든 가능한 조합을 시도하는 방식으로 암호를 찾음
- 대부분의 암호화 방식은 이론적으로 Brute Force 공격에 대해 안전하지 못하며, 충분한 시간이 존재한다면 암호화된 정보를 해독할 수 있음
- 일정 패턴을 탐지하여 만든 사전을 활용해서 공격하는 방식도 있음
시큐어 코딩
SQL 인젝션 취약점
1
2
3
4
// 사용자 입력값을 직접 SQL 쿼리에 결합 - 위험
String id = request.getParameter("id");
String query = "select id, name from member where id = '" + id + "'";
ResultSet rs = stmt.executeQuery(query); // SQL 인젝션 취약점
- 이 코드는 사용자가 입력한 값을 이용해서 SQL을 실행함
- 사용자가 입력한 문자열이
abcd이면 다음 쿼리로 실행됨
1
select id, name from member where id = 'abcd'
악의적인 입력값
- 문제는 정상적이지 않은 값을 입력하는 사용자도 있다는 데에 있음
1
' or 1=1 or id = '
- 이 값을 입력하면 실제로 실행되는 쿼리는 다음과 같아짐
1
select id, name from member where id = '' or 1=1 or id = ''
- 이 코드의 where 절은 각 조건이 or로 연결되어 있고, 중간에 항상 참인
1=1조건이 있음 - 따라서 where 절은 항상 참이 되고, 결과적으로 member 테이블에 있는 모든 id와 name을 조회함

Prepared Statement 사용
- Prepared Statement를 사용하면 입력된 특수 문자를 안전하게 방어할 수 있음
1
2
3
4
5
6
// Prepared Statement를 사용한 안전한 방법
String id = request.getParameter("id");
String query = "select id, name from member where id = ? "; // 파라미터 플레이스홀더 사용
PreparedStatement pstmt = connection.prepareStatement(query);
pstmt.setString(1, id); // 파라미터 바인딩 - 자동으로 특수문자 이스케이프
ResultSet rs = pstmt.executeQuery(); // 안전한 쿼리 실행
시큐어 코딩의 다양한 취약점
- 입력 값 검증
- 클라이언트가 전송한 값이 올바른지 검증하고 만약 모든 값을 검증하지 않으면 안 됨
- 개인 정보/민감 정보 암호화
- 로그인 암호뿐만 아니라 주민 번호, 운전 면허, 이름 같은 고유 식별 정보도 암호화해야 함
- 에러 메시지 시스템 정보 노출 방지
- 에러 메시지에 내부 IP나 DB IP와 같은 시스템 정보가 노출되지 않도록 함
- 보안 통신
- HTTPS처럼 데이터를 암호화해서 데이터 유출을 방지함
- CORS(Cross Origin Resource Sharing) 설정
- 허용된 도메인만 서버 자원에 접근할 수 있도록 제한함
- CSRF(Cross-Site Request Forgery) 대응
- 주요 기능은 타 사이트에서 위조 공격이 들어오는 것을 방지하기 위해 CSRF 토큰, SameSite 쿠키, 검사 등을 사용함
개인 보안
개발자 PC 보안의 중요성
- 개발자는 다양한 서버에 연결할 수 있음
- 권한에 따라 DB에 접속해서 다양한 쿼리도 실행할 수 있음
- 개발자가 접근할 수 있는 시스템이 많은 만큼 개발자 PC가 해킹 당하면 큰 사고로 이어짐

개발자 보안 수칙
- 개발자 PC가 해킹당할 수 있는 주요 경로
- 출처가 불분명한 파일을 다운받거나 실행하는 경우
- 의심스러운 이메일의 첨부 파일을 열거나 링크를 클릭하는 경우
- 신뢰할 수 없는 소프트웨어나 브라우저 확장 프로그램 설치
- 공개 Wi-Fi에서 중요한 작업 수행
보안 사고 사례
-
악성 코드 감염을 통한 정보 유출
- 개발자 PC에 악성 코드가 설치되면 DB 접속 정보, API 키 등이 탈취될 수 있음
- 해커는 탈취한 인증 정보로 시스템에 무단 접근하여 중요 데이터를 유출함
- 방지 방법
- 백신 프로그램 최신 유지
- 의심스러운 파일 실행 금지
- 접속 정보 암호화 저장
-
랜섬웨어 공격
- 랜섬웨어는 시스템의 데이터를 암호화하여 사용 불가 상태로 만들고 복구 대가를 요구함
- 서버 및 백업 데이터까지 암호화되면 서비스 중단과 막대한 복구 비용 발생
- 방지 방법
- 정기적인 백업
- 백업 데이터 격리
- 의심스러운 메일 첨부파일 주의
물리적 보안
- 물리적 보안 수칙
- 자리를 비울 때는 반드시 화면을 잠금 (Windows:
Win + L, macOS:Cmd + Ctrl + Q) - 중요 사이트에 로그인한 상태에서는 자리를 비우지 않음
- 불가피하게 자리를 비울 경우 세션 로그아웃 처리
- 자리를 비울 때는 반드시 화면을 잠금 (Windows:
- 화면 엿보기 방지
- 개인정보나 중요 코드 작업 시 모니터 보안 필름 사용 고려
- 공용 장소에서 작업 시 주변 환경에 주의
배운 점
- 가장 기본적인 보안은 사용자 개인정보와 같은 민감한 정보에 대한 적절한 접근 제어임
- 인증과 인가는 서버 개발에서 가장 기본적인 보안 개념임
- 암호화는 단방향(해시)와 양방향(AES, RSA)으로 나뉘며 각각 사용 목적이 다름
- HMAC을 통해 메시지의 무결성과 인증을 보장할 수 있음
- 감사 로그는 위변조 방지가 중요하므로 일반 로그와 분리된 저장소에 보관해야 함
- SQL 인젝션 등 시큐어 코딩 취약점은 Prepared Statement 등으로 방어할 수 있음
- 개발자 PC 보안이 소홀하면 전체 시스템의 보안이 위협받을 수 있음
적용 방안
- 인증 시스템 강화
- JWT 기반 토큰 인증 적용
- 2단계 인증(2FA) 도입 검토
- 토큰 만료 시간 설정
- 암호화 적용
- 비밀번호는 반드시 BCrypt 등 단방향 암호화 사용
- Salt 값을 사용자별로 다르게 적용
- 민감한 개인 정보는 AES로 양방향 암호화
- API 보안 강화
- 중요한 API는 HMAC으로 무결성 검증
- HTTPS 통신 필수
- CORS, CSRF 대응 설정
- 데이터 노출 최소화
- 백오피스 화면에서 개인 정보 마스킹 처리
- 고객 목록 조회 권한 제한
- 비정상 접근 패턴 탐지 로직 구현
- 시큐어 코딩
- Prepared Statement 사용
- 입력값 검증 로직 추가
- 에러 메시지에 시스템 정보 노출 방지
- 감사 로그 구축
- 중요한 사용자 행위 로깅