1. 프론트에서 막기 (일반인 상대)
required : 값을 입력하지 않으면 submit이 안됨
required를 추가해줘서 공격을 막았다고 생각하겠지만 아니다.
브라우저에서의 공격은 막혀있지만
/join이라는 주소만 알고 있다면 Postman으로 공격이 가능하다.
2. 백엔드에서 막기 (악의적인 공격자 상대)
body로 넘어온 값을 확인한다.
null 체크, 공백 체크, @없는 잘못된 형식의 이메일, 비밀번호 길이 12자(한글로는 4자 / 한글 막기),
모든 값을 잘 적고 비밀번호 길이를 막아놓지 않은 채 길게 적으면
DB에서 터져버린다. -> 문법 오류
프론트에서 maxlength 설정이 가능하지만 이것 또한 postman으로 공격이 가능하다.
일반적으로 브라우저에서만 요청을 하는 게 아니기 때문에 백엔드에서 반드시 막아줘야 한다.
이런 귀찮은 일을 줄이기 위해 라이브러리가 나왔다.
우선 null과 공백을 막는 유효성 검사를 직접 만들어보자.
null인지 확인하지 않고 참조하려고 한다면 null을 가리키게 되어
null에 접근해서 nullPointerException이 뜬다.
이 오류는 런타임 시에 잡히기 때문에 컴파일 시점에 확인이 불가능하다.
-> validation 체크 / 트라이 캐치
2가지 방법이 있다.
트라이 캐치의 경우에는 트라이에서 오류가 발생하면 캐치를 타고 캐치에서 잡아주는데
애초에 if를 사용해 오류를 발생시키지 않는 게 더 좋다.
하지만 나중에 이 오류 잡는 것을 자동화해주어야 하는데
자동화를 하기에는 이미 발생한 오류를 캐치해내는 게 더 편하다.
나중에는 다 트라이 캐치로 잡을 거지만
지금은 if로 미리 오류를 잡을 것이다.
이게 기본기이기 때문!
유효성 검사를 하기 위해 로그인해서 만들어진 세션에 principal이라는 키값을 부여한다.
User principal = (User) session.getAttribute("principal");
로그인이 인증의 과정인데
로그인을 하지 않고 주소창에 직접
http://localhost:8080/user/1이라고 요청을 할 위험이 있기 때문에
이를 막기 위한 인증 체크 과정이 필요하다.
// 1. 인증 체크 (로그인하지 않고 주소로 접근 막기)
if (principal == null) {
return "error/page1";
}
또 하나의 문제가 있다.
내가 id가 1인 사용자로 로그인에 성공만 한다면
id가 2인 사용자의 정보를 주소창에 검색할 수 있다.
인증은 되어있는 상태이기 때문이다.
이를 막아줘야 한다.
// 2. 권한 체크
if (principal.getId() != id) {
return "error/page1";
}
두 가지 검사가 끝나면 유저 정보 상세 보기 페이지를 위해
DB에서 데이터를 찾아와야 한다.
id로 SELECT 하여 하나의 row를 뽑아올 것이다.
이는 JpaRepository에 이미 만들어져 있는 findById( ) 메서드를 사용하자.
여기서 가져온 데이터는 Optional<User> 타입인 userOp에 담아줄 것이다.
Optional이라는 박스에 User 타입을 담는다고 생각하면 된다.
만약 Entity가 null이라고 해도 Optional 박스 자체는 존재하기 때문에
null 예외가 발생하지 않는다.
Optional<User> userOp = userRepository.findById(id); // 유저정보
이제 if문으로 핵심 로직이 시작된다.
만약 박스에 데이터가 존재한다면 userEntity에 박스 내용물을 담고
모델에 키와 값을 넣어준다.
그리고 유저 정보 상세 보기 페이지를 리턴해주면 된다.
// 3. 핵심 로직
if (userOp.isPresent()) { // 박스안에 뭐가 있으면
User userEntity = userOp.get();
model.addAttribute("user", userEntity);
return "user/detail";
} else { // 없으면 == isEmpty
// 누군가 고의로 DELETE 하지 않는 이상 거의 타지 않는 오류
return "error/page1";
}
// http://localhost:8080/user/1
// 유저 상세 페이지 (동적 -> DB연동 필요) - 인증(로그인) O
@GetMapping("/user/{id}")
public String detail(@PathVariable int id, Model model) {
// 유효성 검사 하기(수십개... 엄청 많겠지?)
User principal = (User) session.getAttribute("principal");
// 1. 인증 체크 (로그인하지 않고 주소로 접근 막기)
if (principal == null) {
return "error/page1";
}
// 2. 권한 체크
if (principal.getId() != id) {
return "error/page1";
}
Optional<User> userOp = userRepository.findById(id); // 유저정보
// 3. 핵심 로직
if (userOp.isPresent()) { // 박스안에 뭐가 있으면
User userEntity = userOp.get();
model.addAttribute("user", userEntity);
return "user/detail";
} else { // 없으면 == isEmpty
// 누군가 고의로 DELETE 하지 않는 이상 거의 타지 않는 오류
return "error/page1";
}
// DB에 로그 남기기 (로그인 한 아이디도 남기기)
// Heidi SQL에서도 남겨야해 근데 디비는 알아서 로그가 남음
}
사용자의 실수로 생긴 에러가 아닌
나쁜 의도로 공격하는 사람으로 인해 생긴 에러에 대한 리턴 페이지는
UX를 좋게 만들어줄 필요가 없다.
대신 에러의 stackTrace는 절대 보이게 해서는 안된다!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>에러페이지</title>
<style>
.red_color {
color: red;
}
</style>
</head>
<body>
<h1 class="red_color">잘못된 페이지를 요청하였습니다.</h1>
<hr/>
<a href="/">메인으로 가기</a>
</body>
</html>
[출처]
https://cafe.naver.com/metacoding
메타 코딩 유튜브
https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9