관리 메뉴

bright jazz music

스프링 시큐리티 인 액션 2-1 ~ 2-2 본문

Framework/Spring Security

스프링 시큐리티 인 액션 2-1 ~ 2-2

bright jazz music 2022. 11. 18. 22:41
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

 

 

package com.example.ssiach2ex1;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        return "hello!!!!!!";
    }
}

 

#실패(인증값을 넣지 않았음)
$ curl -v http://localhost:8080/hello


*   Trying 127.0.0.1:8080...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.80.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< Set-Cookie: JSESSIONID=FC942B8AE439939645C5C350A7CA1EAF; Path=/; HttpOnly
< WWW-Authenticate: Basic realm="Realm"
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Fri, 18 Nov 2022 13:40:43 GMT
<
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
* Connection #0 to host localhost left intact

 

 

 

 

##성공시

$ curl -v -u user:72f811a9-7a1a-435c-ab75-61997458d5c1 http://localhost:8080/hello

*   Trying 127.0.0.1:8080...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (127.0.0.1) port 8080 (#0)
* Server auth using Basic with user 'user'
> GET /hello HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dXNlcjo3MmY4MTFhOS03YTFhLTQzNWMtYWI3NS02MTk5NzQ1OGQ1YzE=
> User-Agent: curl/7.80.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=9112626746A2421626CF3B9BF007EE52; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 11
< Date: Fri, 18 Nov 2022 13:32:39 GMT
<
{ [11 bytes data]
100    11  100    11    0     0    144      0 --:--:-- --:--:-- --:--:--   146hello!!!!!!
* Connection #0 to host localhost left intact

 

 

#base64처리

$ echo -n user:72f811a9-7a1a-435c-ab75-61997458d5c1 | base64
dXNlcjo3MmY4MTFhOS03YTFhLTQzNWMtYWI3NS02MTk5NzQ1OGQ1YzE=

 

 

#base64처리한 값을 헤더에 넣어서 요청
$ curl -v -H "Authorization: Basic dXNlcjo3MmY4MTFhOS03YTFhLTQzNWMtYWI3NS02MTk5NzQ1OGQ1YzE=" http://localhost:8080/hello


*   Trying 127.0.0.1:8080...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.80.0
> Accept: */*
> Authorization: Basic dXNlcjo3MmY4MTFhOS03YTFhLTQzNWMtYWI3NS02MTk5NzQ1OGQ1YzE=
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=463D9F2C36211B860D0D113348D58D7C; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 11
< Date: Fri, 18 Nov 2022 13:39:00 GMT
<
{ [11 bytes data]
100    11  100    11    0     0    144      0 --:--:-- --:--:-- --:--:--   146hello!!!!!!
* Connection #0 to host localhost left intact

 

 

 

 

자동으로 구성되는 Bean

  • UserDetailsService
  • PasswordEncoder

 

인증 공급자는 위의 빈을 이용해 사용자를 찾고 암호를 확인한다.

 

  • UserDetailsService

사용자에 대한 세부 정보는 스프링 시키리티로 UserDetailsService계약을 구현하는 객체가 관리한다.

(지금까지는 스프링 부트가 제공하는 기본 구현을 사용했다. 이 구현은 애플리케이션의 내부 메모리에 기본 자격을 등록하는 일만 한다. 이러한 기본 자격증명에서 사용자 이름은 user이고 기본 암호는 UUID 형식이며 암호는 스프링 컨텍스트가 로드될 때 자동으로 생성된다. 이를 콘솔에 출력한다. 이 구현은 자격증명을 메모리에 보관한다. 즉 애플리케이션은 자격증명을 보존하지 않는다. 이 접근법은 예제나 개념증명에 적합하지만 운영 시 애플리케이션에서는 피해야 한다.)

 

 

  • PasswordEncoder

- 암호를 인코딩한다.

- 암호가 기존 인코딩과 일치하는지 확인한다.

(이녀석도 Basic 인증 흐름에 꼭 필요하다. 가장 단순한 구현에서는 암호를 일반 텍스트로 관리하고 인코딩 하지 않는다. userDetailsService의 기본 구현을 대체할 때는 PasswordEncoder도 지정해야 한다.

 

http basic 접근 인증을 시도하는 경우 클라이언트가 사용자 이름과 암호를 HTTP Authorization 헤더를 통해 보내기만 하며 면 된다. 클라이언트는 헤더 값에 접두사 Basic을 붙이고 그 뒤에 콜론으로 구분된 사용자 이름과 암호가 포함된 문자열을 Base64로 인코딩하고 붙인다.

 

AuthenticationProvider는 인증 논리를 정의하고 사용자와 암호의 관리를 위임한다.

AuthenticationProvider의 기본 구현은 UserDetailsService및 PasswordEncoder에 제공된 기본 구현을 이용한다.

 

 

참고1

 

HTTP Basic인증은 자격증명 시 기밀성을 보장하지 않는다. Base64는 전송의 편의를 위한 인코딩 방법이고 암호와나 해시 방법이 이니다. 따라서 전송중에 자격 증명을 누군가 가로챔녀 누구든지 볼 수 있다.

 

 

참고2

 

실제로는 Http가 아닌 HTTPS를 통해서만 통신해야 한다.

시스템에서 HTTS를 구성하는 데에는 여러 패턴이 있다. 개발자는 때에 따라 애플리케이션 수준에 HTTPS를 구성할 수도 있고 인프라 수준에 HTTPS를 설정할 수도 있다. 스프링 부트를 사용하면 손쉽게 HTTPS를 사용할 수 있다.

 

이러한 모든 구성 시나리오엔느 인증기관(CA, Certification Autority)이 서명한 인증서가 필요하다.

인증서를 이용하면 엔드포인트를 호출하는 클라이언트에서 응답이 인증 서버에서 보낸 것이며 통신을 가로채지 않았다는 것을 알 수 있다. 인증서는 구매해야 하며 갱신이 필요하다. 애플리케이션 테스트 용도라면 OpenSSL과 같은 툴을 사용해서 자체 서명 인증서를 생성하면 된다.

 

터미널

$ openssl req -newkey rsa:2048 -x509 -keyout key.pem -out cert.pem -days 365

Generating a RSA private key
.............+++++
..................................+++++


--멈춤



$ openssl pkcs12 -export -in cert.pem -inkey key.pem -out certificate.p12 -name "certificate"
unable to load private key
29460:error:0909006C:PEM routines:get_name:no start line:../openssl-1.1.1l/crypto/pem/pem_lib.c:745:Expecting: ANY PRIVATE KEY

이 명령은 개인 키를 담은 key.pem과 공개 인증서를 담은 cert.pem  두 파일을 생성한다. 이 두 파일은 https활성화를 위한 자체 서명 인증서를 생성하는 데 필요하다.

 

 

 

pkcs12(public key cryptography standards #12) 생성

 

 

 

윈도우 환경에서는 아래와 같이 winpty를 붙여 줘야함.

$ winpty openssl req -newkey rsa:2048 -x509 -keyout key.pem -out cert.pem -days 365
Generating a RSA private key
.....+++++
......+++++
writing new private key to 'key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:KR
State or Province Name (full name) [Some-State]:Seoul
Locality Name (eg, city) []:Seoul
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Gwacheon
Organizational Unit Name (eg, section) []:jika
Common Name (e.g. server FQDN or YOUR name) []:mygging
Email Address []:mygging@gmail.com




$ winpty openssl pkcs12 -export -in cert.pem -inkey key.pem -out certificate.p12 -name "certificate"
Enter pass phrase for key.pem:
Enter Export Password:
Verifying - Enter Export Password:

 

 

 

 

 

 

 

아래와 같이 나오며 실패... 뭐야

$ curl -v -k https://localhost:8080/hello
*   Trying 127.0.0.1:8080...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (127.0.0.1) port 8080 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [193 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [155 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [28 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [892 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [264 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=KR; ST=Some-State; O=Internet Widgits Pty Ltd
*  start date: Nov 21 01:17:29 2022 GMT
*  expire date: Nov 21 01:17:29 2023 GMT
*  issuer: C=KR; ST=Some-State; O=Internet Widgits Pty Ltd
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
} [5 bytes data]
> GET /hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.80.0
> Accept: */*
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [50 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< Set-Cookie: JSESSIONID=AE6BBE698F08ACD07A8703BB3D40591C; Path=/; Secure; HttpOnly
< WWW-Authenticate: Basic realm="Realm"
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Mon, 21 Nov 2022 01:25:55 GMT
<
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
* Connection #0 to host localhost left intact

 

껐다 다시 켜고 아래와 같이 쳐줬음. -k를 안 붙였을 땐 접근 불가

$ curl -v -u user:2e08560c-afe7-46fe-87dc-ca0ef782bff9 https://localhost:8080/hello
*   Trying 127.0.0.1:8080...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (127.0.0.1) port 8080 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
*  CApath: none
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [193 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [155 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [28 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [892 bytes data]
* TLSv1.3 (OUT), TLS alert, unknown CA (560):
} [2 bytes data]
* SSL certificate problem: self signed certificate
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

 

-k 옵션을 붙여주면 접근 가능 

참고: https://sarc.io/index.php/miscellaneous/1494-curl-60-ssl-certificate-problem-self-signed-certificate

$ curl -v -k -u user:2e08560c-afe7-46fe-87dc-ca0ef782bff9 https://localhost:8080/hello
*   Trying 127.0.0.1:8080...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (127.0.0.1) port 8080 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [193 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [155 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [28 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [892 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [264 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=KR; ST=Some-State; O=Internet Widgits Pty Ltd
*  start date: Nov 21 01:17:29 2022 GMT
*  expire date: Nov 21 01:17:29 2023 GMT
*  issuer: C=KR; ST=Some-State; O=Internet Widgits Pty Ltd
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Server auth using Basic with user 'user'
} [5 bytes data]
> GET /hello HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dXNlcjoyZTA4NTYwYy1hZmU3LTQ2ZmUtODdkYy1jYTBlZjc4MmJmZjk=
> User-Agent: curl/7.80.0
> Accept: */*
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [50 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=D5E2EE7FBF6073C4B028103598AE305F; Path=/; Secure; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 7
< Date: Mon, 21 Nov 2022 01:44:09 GMT
<
{ [7 bytes data]
100     7  100     7    0     0     29      0 --:--:-- --:--:-- --:--:--    29Hello!!
* Connection #0 to host localhost left intact

 

--insecure를 붙여도 -k와 동일한 결과 출력

$ curl -v -u user:2e08560c-afe7-46fe-87dc-ca0ef782bff9 https://localhost:8080/hello --insecure
*   Trying 127.0.0.1:8080...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to localhost (127.0.0.1) port 8080 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [193 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [155 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [28 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [892 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [264 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=KR; ST=Some-State; O=Internet Widgits Pty Ltd
*  start date: Nov 21 01:17:29 2022 GMT
*  expire date: Nov 21 01:17:29 2023 GMT
*  issuer: C=KR; ST=Some-State; O=Internet Widgits Pty Ltd
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* Server auth using Basic with user 'user'
} [5 bytes data]
> GET /hello HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dXNlcjoyZTA4NTYwYy1hZmU3LTQ2ZmUtODdkYy1jYTBlZjc4MmJmZjk=
> User-Agent: curl/7.80.0
> Accept: */*
>
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
{ [50 bytes data]
* Mark bundle as not supporting multiuse
< HTTP/1.1 200
< Set-Cookie: JSESSIONID=AAAD79D8DFA7B752A21E604B55306C87; Path=/; Secure; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 7
< Date: Mon, 21 Nov 2022 01:47:14 GMT
<
{ [7 bytes data]
100     7  100     7    0     0     56      0 --:--:-- --:--:-- --:--:--    57Hello!!
* Connection #0 to host localhost left intact

 

 

 

Comments