최근 연구에 따르면 비밀번호의 복잡성보다는 길이가 보안성을 높이는데 더 중요하다고 밝혀짐
CSRF(Cross-Site Request Forgery)는 사용자의 의지와는 상관없이 공격자가 사용자의 권한을 도용하여 특정 요청을 서버에 보낼 수 있도록 하는 공격, 이를 방지하기 위해
재인증 프로세스 : 이체 요청과 같은 민감한 작업을 수행하기 전에 사용에게 재인증을 요구(확인 알림, 비밀번호 또는 OTP 입력, CSRT 토큰 발행 등)
사용자 경험과 성능에 영향을 줄 수 있지만 보안을 강화
설계 단계에서 요구사항을 수집하고 분석한 후 구현
CSRF 토큰 : 서버가 CSRF 토큰을 발행하고 이를 사용자의 세션에 저장한 후, 폼 데이터와 함께 전송, 서버는 요청을 받을 때 토큰의 유효성을 검사
XSS(Cross-Site Scripting)는 공격자가 웹 페이지에 악성 스크립트를 삽입하여 사용자의 브라우저에서 실행되게 하는 공격, 이를 방지하기 위해
태그를 텍스트로 인식 : 사용자 입력을 HTML 태그로 인식하지 않고 텍스트로 처리
innerHTML 대신 textContent를 사용하여 HTML을 텍스트로 처리
서버 측에서 입력을 필터링하거나 인코딩하여 출력 시 HTML 태그가 실행되지 않도록 함
한 사용자의 권한이 클수록(특히 관리자인 admin) 해킹 시 피해가 커지므로, 철저한 인증 및 권한 관리를 통해 보안을 강화
기본 인증(Basic Authentication) : 기본 인증은 ID와 비밀번호를 평문으로 전송하므로 해킹 위험이 높음, 이를 대신하여 더 안전한 인증 방식을 사용
토큰 기반 인증 : JWT(JSON Web Token)와 같은 토큰 기반 인증을 사용하여 세션을 관리
이중 인증 : 추가적인 보안층을 제공하기 위해 두 가지 이상의 인증 요소를 요구

<form id="hongForm" style="display:none" method="post" action="/post/save" >
<input type="hidden" name="title" value="회비 계좌 확인 요합니다">
<input type="hidden" name="id" value="admin">
<input type="hidden" name="content" value="이번 모임의 회비 납부 안내입니다<br>국민은행 01-0123-1234 홍길동">
<input type="submit">
</form>
<script>
document.getElementById('hongForm').submit();
</script>
ce로 글 작성

작성자인 ce에서 작성 글 클릭 시 alert

admin으로 로그인 후 글 클릭 시

script가 실행되어 글이 자동으로 생성
이를 막기 위해
서버 측에서 CSRF 토큰을 생성하고 이를 세션에 저장, 그런 다음 글 작성은 글 작성 화면이 동반되어야 하기 때문에 작성 페이지를 렌더링할 때 CSRF 토큰을 포함시킴
토큰 생성 후 비교하는 로직 적용 시 alert창 뜨면서 전처럼 글이 자동으로 생성되지 않아 CSRF 공격이 방지

1. 파일 사이즈 제한
예시)파일 크기를 10MB로 제한
const multer = require('multer');
const upload = multer({
limits: { fileSize: 10 * 1024 * 1024 } // 10MB
});
2. 파일 갯수 제한
예시)파일 갯수를 최대 5개로 제한
const uploadMultiple = upload.array('files', 5); // 최대 5개 파일
3. 업로드된 파일에 실행 권한을 부여하지 않음
업로드 디렉토리의 권한 설정
chmod -R 644 /path/to/upload/directory
4. 파일 원본 이름 숨기고, 서버에 별칭으로 저장
예시)원본 파일명을 무작위로 생성된 문자열로 대체
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, uniqueSuffix + path.extname(file.originalname));
}
});
const upload = multer({ storage: storage });
검색 기능 추가