본 글은 Spring Boot 3 & Spring Framework 6 마스터하기! (https://www.udemy.com/course/spring-boot-and-spring-framework-korean/) 를 보고 정리한 글입니다.
글또 x Udemy 이벤트로 수강하게 된 강의인데, 좋은 기회를 얻게 되어 감사드리는 마음입니다!
컴퓨터에 강한 나라 인도 출신 강사님이 설명해주시는 spring framework 강의로, Hibernate, React, Docker AWS 등 spring framework 뿐 아니라 이를 활용해서 하나의 어플리케이션을 만들기 위한 전반적인 과정을 담고 있습니다.
가장 좋은 점은 정말 핵심 이론만 쏙쏙 골라서 설명한 후 , 코드를 작성하는 방식이라는 점입니다.
군더더기 없이 빠르게 웹 전반과 개발 전략에 대해 학습할 수 있어서 좋았습니다.
Spring Security 가 요청에 대해 인증, 인가하는 방식 : 필터 체인을 활용
순서
- CORS , CSRF
- Authentication
- Authorization
Spring Security
- Everything is Authenticated. (including non-exisiting resources)
- Spring Security enables form based authentication by default(login form, logout form, logout url)
-> 폼 기반 인증시 세션을 사용함. 로그인 시, session이 생성되어 session cookie 가 저장됨. 그리고 사이트에 요청을 보낼 때마다 session cookie 를 함께 보내게 된다. - Spring Security also use Basic Authentication
- REST API 보호에 사용되나, 단점이 많아 프로덕션 환경에서 사용되지는 않음.
- request header 에 사용자 id, pw 를 암호화해서 저장해서 전송 (단점: 디코딩이 쉬움, 만료기한이 없음, 사용자 권한 정보는 담을 수 없음)
CSRF
Cross Site Request Forgery
[CSRF 공격 막기 - Spring Security] (https://docs.spring.io/spring-security/reference/features/exploits/csrf.html#csrf-protection%5D)
악성 사이트에서 브라우저에 저장된 로그인 쿠키 정보를 이용해서 사이트에 위험한 요청을 보내는 것.
세션을 사용하는 경우 문제가 되는 위조이다.
- Synchronizer Token Pattern
- 요청마다 새 토큰을 만들어서 전송 (브라우저가 자동으로 세션 쿠키를 웹사이트로 요청 시 포함하는 것과 다르게, 올바른 요청에서만 해당 토큰을 넣어서 사용할 수 있게끔 하는 것. 즉 쿠키 외의 다른 정보를 요청에 포함하는 방식. 악성 사이트는 respnose body를 읽지는 못하기 때문에[same origin policy] html hidden field에 넣은 채로 사용자에게 응답을 제공하면 이 패턴을 안전하게 사용할 수 있다. )
- 어차피 애플리케이션의 상태를 바꾸지 않는 GET 요청 같은 read-only http method 에는 csrf 토큰을 사용하지 않는다. 괜히 유출만 되기 때문.
- 이전에 들었던 토큰을 이용해서 PUT, POST 요청을 해야만 요청이 수행되게끔.
- 요청을 보내면 해당 세션에 대해 csrf token을 만들어서 서버가 hidden field에 보내준다. 이 값을 이용해서 브라우저가 요청을 해야만 POST, PUT 요청을 하면 요청이 수행된다.
- form 에서 사용할 수 있지만, 매 페이지에 hidden field로 csrf 토큰을 만들어두어야 한다.
Spring Security 는 기본적으로 thymeleaf 등을 이용해서 html을 반환할 때 자동적으로 내부에 CSRF 토큰을 넣는다. - SameSite Attribute
- CSRF 위조를 막는 새로운 방법. 외부 사이트에서 해당 사이트와 관련된 쿠키를 보내지 못하도록 하는 방식이다.
- Spring Security 는 session cookie 의 생성을 직접적으로 조절하지는 않는다. spring Session 이 servlet-based-application에서 sameSite 속성을 제공한다.
- Cookie 의 SameSite 속성에 들어갈 수 있는 값
- Strict : 같은 사이트에서 오는 요청에만 쿠키를 포함시킨다.
- Lax : 같은 사이트 혹은 top-level navation 이면서 read-only 메소드 요청일 때만 쿠키 포함을 허용한다.
- samesite 속성이 strict 일 경우, 타 사이트에서 해당 사이트로의 접근 자체가 이미 로그인되어 있어도 인증이 안되는 문제가 발생할 수 있다. 또한 브라우저가 samesite 속성을 지원해야 한다는 특징이 있다.
- RESt API에서 CSRF 토큰 방식 해제하기
- SpringBootWebSecurityConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
static class SecurityFilterChainConfiguration {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
http.formLogin(withDefaults());
http.httpBasic(withDefaults());
return http.build();
}
}
요청에 대해서 인증하는 기본 필터 체인을 가지고 있다.
별도로 필터 체인을 생성해서 사용하기 위해서는 보안설정을 따로 만들면 된다.
HttpSecurity 를 입력받아서 SercurityFilterChain을 반환하는 securityFilterChain Bean을 만들자.
HttpSecurity 는 필터체인 설정에 유용한 클래스이다.
http.sessionManagement(
session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
http.csrf(AbstractHttpConfigurer::disable);
ALWAYS 는 항상 세션을 설정하고, STATELESS 는 stateless하게 즉 세션을 생성하지 않고 사용도 하지 않는다.
csrf 해제하기
CORS
- 브라우저는 기본적으로 다른 url 에 AJAX 요청을 보내는 것을 막는다.
- CORS 설정을 통해 도메인간 요청을 설정할 수 있는 규격이 바로 CORS 이다.
- CORS 설정의 두가지 방식 ( 글로벌 설정, 매 컨트롤러마다 로컬 설정)
- 글로벌 설정 : WebMvcController 에 addCoresMapping 콜백 함수를 설정해둔다.
- 로컬 설정 : Controller 클래스에 @CrossOrigin (모든 url에 대해 허용) 혹은 @CrossOrigin(origins= "url")
- 사용자 자격 증명 : memory(프로덕션 환경에서는 비추), Database, LDAP(Lightweight Driectory AccessProtocol) 등에 저장
- 메모리 : 여러 사용자 설정해보기
- UserDetailsService : 사용자별 데이터를 로드하는 코어 인터페이스
- User 객체에서 여러가지 설정을 한 build 결과물을 만든다. (role에 들어가는 String 은 ENUM으로 관리하는 것이 좋겠다.)
@Bean public UserDetailsService userDetailService() { UserDetails user = User.withUsername("ebang") .password("{noop}dummy") .roles("USER") .build(); UserDetails admin = User.withUsername("whhat") .password("{noop}dummy") .roles("ADMIN") .build(); return new InMemoryUserDetailsManager(user, admin); }
- 메모리 : 여러 사용자 설정해보기
- JDBC 에서 자격증명 사용해보기
- Datasource : JDBC를 통해서 데이터베이스와 연결하는 인터페이스
- 애플리케이션이 데이터 베이스에 대한 연결을 가져오고 반환하는데 사용. (연결정보를 담고 있다.)
- InMemoryUserDetailsManager 대신 JDBCUserDetailsManager 를 사용해서 사용자 자격 증명을 사용한다.
- UserDetails 를 만들어두고, JDBCUserDetailsManager 에 넣어서 createUser 한다.
- DataSource를 통해서 User 관련한 table을 만들고 미리 유저를 만들어 설정할 수 있다.
- SpringSecurity 가 role 앞에 "ROLE_"을 붙여서 authority를 생성한다.
- password encryption
Encoding, Hashing, Encryption
- Encoding : 데이터를 원래 형식에서 다른 형식으로 변환하는 것. (key 나 password 사용없이 가역적으로 사용)
- 가역적으로, 다시 decoding 이 가능하기 때문에 보안을 목적으로는 사용하지 않는다.
- 압축, 스트리밍을 목적으로 사용하며 예시로는 Base 64, MP4 등이 있다.
- Hashing : 데이터를 해시 문자열로 변환하는 것. (비가역적. 원래 데이터를 구할 수 없다. )
- 데이터 무결성을 검증하는데 사용.
- 데이터, 데이터의 해쉬를 함께 전송하면 데이터의 무결성을 검증할 수 있다.
- bcrypt, scrypt
- Encryption : data 를 key, password 로 인코딩하는 것.
- key나 패스워드가 있어야만 decoding이 가능하다.
- RSA
Spring Security 를 이용해서 비밀번호 저장하기
- SHA-256 해싱을 이용해서 비밀번호를 저장하는 것은 요즘에는 적합하지 않다.
- 해싱 값을 구하는 것이 매우 빠르게 가능하기 때문에 브루트포스로 비밀번호를 찾을 수 있기 때문이다.
- 추천하는 방식은 1초를 워크 팩터로 적용해 적응형 단방향 함수를 사용하는 것이다.
Jwt
Json Web Token
- contains User Detail, Authorization ㅇ
- header : type, hashing Algorithm
- payload :
iss : The issure,
sub : the Subject(사)
aud : the audience,
exp : expire date
iat : when was issued - signature :
- Base 64로 header, payload 인코딩, signature 값까지 포함해서 해싱!
- 암호화, 비암호화 가능
- 대칭 암호화 : 같은 키로 암호화, 비암호화 (키 저장, 키 공유를 잘해야 함.)
- 비 암호화 : == 공개키 암호화 기법 공개 키와 비밀 키. 공개키는 아무에게나 공개하되 개인키는 자기만 갖고 있음. 공개키가 있어도 비밀키를 만들기는 매우 어렵다.
- jwt 는 비암호화 방법을 이용한다.
- jwt 만들기 (user credetntial, user data, RSA key pair)
- send jwt as part of request header (Authorization header, Bearer Token)
- JWT is verified
jwt 보안 설정
- 스프링 부트 OAuth2 리소스 서버 사용
- create key pair
- java.security.KeyPairGenerator.generateKeyPair()
- create RSA Key Object using Key Pair
- numbusds -> RSA Key Object 생성
- create JWKsource (JSON Web Key source)
JWKSet -> JWKSource - User RSA Public Key for Decoding
- user JWKSource for Encoding
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
class Oauth2ResourceServerConfiguration 서버가 지원해준다 .
- jwt Configuration 설정 시 jwtDecoder 생성하기
Spring Security에서 인증을 하는 동작원리
graph TD;
start(Authentication Manager) --> mid(Authentication Provider) --> last(UserDetails Service);
(티스토리는 mermaid 가 안되네요..)
Spring Security 에서 Authentication이 의미하는 것 3가지
- Credential (username, password)
- Principal : 사용자에 관한 세부 정보
- Authorites : 주체가 가지고 있는 권한 정보
- AuthenticationManager 에서 authenticate 함수가 호출될 때, 인자인 Authentication 에는 1번, Credential 만 포함되어 있을 것이다.
- 함수가 성공적으로 끝난다면, 3번 권한도 처리된다.
- Authentication Manager는 어떻게 사용자 이름과 패스워드가 일치하는 지 알 수 있을까? Authentication Provider 들의 상호작용을 통해서 가능하다.
- jwt 인증의 경우, JwtAuthenticationProvider가 처리한다.
- AuthenticationProvider 들 역시 사용자 정보를 알고 있어야 패스워드, 아이디를 대조할 수 있지 않겠나. 그런 Provider 들끼리 대화하는 인터페이스가 바로 UserDetailsService 이다.
loadUserByUserName 함수를 오버라이딩 함으로써 가능한데, userName을 입력받고 이와 일치하는 UserDetail 을 Authentication Provider에게 리턴한다. Provider는 Credential을 비교한 후, 일치한다면 Principal, Authorities 를 채워넣게 된다.
하나의 Authentication Manager가 다수의 Authentication Provider와 소통하면서 사용자를 인증, 인가 처리한다. 그리고 UserDetails 구현체도 다수 존재할 수 있다. 유저 정보는 데이터베이스, LDAP 등에 존재할 수 있기 때문에.
- 인증 결과가 저장되는 곳은 SecurityContextHolder.
graph LR;
start(SecurityContextHolder) --> mid(Security Context 여기에 저장) --> mid2(Authentication : User Detail 갖고 있음) --> last(GrantedAuthority)
spring Security 의 접근법
- Global Security
- FilterChain, requestMatchers
- Method Security
- @Pre, @Post Annotation
- JSR-250 Annotation @EnableMethodSecurity(jsr250Enabled = true), @RolesAllowed()
'개발 > java' 카테고리의 다른 글
[Spring - JPA] 4. 엔티티 매핑 (0) | 2024.03.16 |
---|---|
Spring 에서 configuration 을 다루는 방법 - @Value vs @ConfigurationProperties (2) | 2024.02.26 |
[Spring - JPA] 3. 영속성관리 (1) | 2024.02.18 |
[Spring - JPA] 2. JPA 개요 (1) | 2024.02.18 |
[Spring - JPA] 1. JPA 소개 (1) | 2024.02.03 |