JWT(JSON Web Token)을 이용한 API 인증 - #1 개념 소개

조대협 (http://bcho.tistory.com)


REST API에 대한 보안과 인증이 화두가 되면서 많이 언급되는 것이 OAuth인데, 근래에 들어서 화두가 되고 있는 것이 JWT (JSON Web Token)이라는 표준이다.


Claim기반 토큰의 개념


OAuth에 의해서 발급되는 access_token은 random string으로 토큰 자체에는 특별한 정보를 가지고 있지 않는 일반적인 스트링 형태 이다. 아래는 페이스북에서 발급된 access_token의 형태로 일반적인 문자열 형태임을 확인할 수 있다.



<그림1.  Facebook의 Oauth에서 사용하는 일반적인 스트링 기반 토큰 예제>

 

 API나 서비스를 제공하는 서버 입장에서 그 access_token을 통해서 사용자에 연관된 권한(예를 들어 scope같은 것)을 식별한 뒤 권한을 허용해주는 구조이다.

즉 서비스를 제공하는 입장에서는 토큰을 가지고 그 토큰과 연관된 정보를 서버쪽에서 찾아야 한다. (사용자 ID나 권한등).

JWT는 Claim 기반이라는 방식을 사용하는데, Claim이라는 사용자에 대한 프로퍼티나 속성을 이야기 한다. 토큰자체가 정보를 가지고 있는 방식인데, JWT는 이 Claim을 JSON을 이용해서 정의한다.

다음은 Claim을 JSON으로 서술한 예이다.이 JSON 자체를 토큰으로 사용하는 것이 Claim 기반의 토큰 방식이다.

{

  "id":"terry"

  ,"role":["admin","user"]

  ,"company":"pepsi"

}

<코드 1. JSON으로 Claim을 기술한 토큰의 형태 >

자 그렇다면, 이러한 Claim 방식의 토큰은 무엇이 좋을까? 이 토큰을 이용해서 요청을 받는 서버나 서비스 입장에서는 이 서비스를 호출한 사용자에 대한 추가 정보는 이미 토큰안에 다 들어가 있기 때문에 다른 곳에서 가져올 필요가 없다는 것이다.

“사용자 관리” 라는 API 서비스가 있다고 가정하다.

 이 API는 “관리자(admin)” 권한을 가지고 있는 사용자만이 접근이 가능하며, “관리자” 권한을 가지고 있는 사용자는 그 관리자가 속해 있는 “회사(company)”의 사용자 정보만 관리할 수 있다. 라고 정의하자

이  시나리오에 대해서 일반적인 스트링 기반의 토큰과 JWT와 같은 Claim 기반의 토큰이 어떤 차이를 가질 수 있는 지 알아보도록 하자.


OAuth 토큰의 경우



<그림 2. String 토큰에 의한 API 권한 인증 흐름>

 

1.    API 클라이언트가 Authorization Server (토큰 발급서버)로 토큰을 요청한다.

이때, 토큰 발급을 요청하는 사용자의 계정과 비밀번호를 넘기고, 이와 함께 토큰의 권한(용도)을 요청한다. 여기서는 일반 사용자 권한(enduser)과 관리자 권한(admin)을 같이 요청하였다.

2.    토큰 생성 요청을 받은 Authorization Server는 사용자 계정을 확인한 후, 이 사용자에게 요청된 권한을 부여해도 되는지 계정 시스템등에 물어본 후, 사용자에게 해당 토큰을 발급이 가능하면 토큰을 발급하고, 토큰에 대한 정보를 내부(토큰 저장소)에 저장해놓는다.

3.    이렇게 생성된 토큰은 API 클라이언트로 저장된다.

4.    API 클라이언트는 API를 호출할때 이 토큰을 이용해서 Resource Server(API 서버)에 있는 API를 호출한다.

5.    이때 호출되는 API는 관리자 권한을 가지고 있어야 사용할 수 있기 때문에, Resource Server가 토큰 저장소에서 토큰에 관련된 사용자 계정, 권한 등의 정보를 가지고 온다. 이 토큰에 (관리자)admin 권한이 부여되어 있기 때문에, API 호출을 허용한다. 위에 정의한 시나리오에서는 그 사용자가 속한 “회사”의 사용자 정보만 조회할 수 있다. 라는 전제 조건을 가지고 있기 때문에, API 서버는 추가로 사용자 데이타 베이스에서 이 사용자가 속한 “회사” 정보를 찾아와야한다.

6.    API서버는 응답을 보낸다.


JWT와 같은 Claim 기반의 토큰 흐름을 보자

 



<그림 3. Claim 기반의 토큰을 이용한 API 권한 인증 흐름 >

 

1.    토큰을 생성 요청하는 방식은 동일하다.  마찬가지로 사용자를 인증한다음에, 토큰을 생성한다.

2.    다른 점은 생성된 토큰에 관련된 정보를 별도로 저장하지 않는다는 것이다. 토큰에 연관되는 사용자 정보나 권한등을 토큰 자체에 넣어서 저장한다.

3.    API를 호출하는 방식도 동일하다.

4.    Resource Server (API 서버)는 토큰 내에 들어 있는 사용자 정보를 가지고 권한 인가 처리를 하고 결과를 리턴한다.

결과적으로 차이점은 토큰을 생성하는 단계에서는 생성된 토큰을 별도로 서버에서 유지할 필요가 없으며

토큰을 사용하는 API 서버 입장에서는 API 요청을 검증하기 위해서 토큰을 가지고 사용자 정보를 별도로 계정 시스템 등에서 조회할 필요가 없다는 것이다.


참고 : 다른 Claim 기반 토큰은?


그러면 이러한 Claim 기반의 토큰이 JSON이 처음일까? 이미 이전에, XML기반의 SAML 2.0이 이와 비슷한 개념을 가지고 있다. Assertion이라는 개념으로 XML안에 이러한 Claim 정보를 넣어서 넘길 수 있었으나, 문제점은 전체적인 사이즈가 너무 크고, 구조가 복잡하여 쉽게 접근이 어려웠다. 더군다가 크기가 크기 때문에 API와 같이 자주 호출해야 하는 경우에는 HTTP 헤더등에 실어서 보내기가 어렵고, 파싱에 대한 오버해드가 크기 때문에 적절하지 않았다. (주로 다른 사이트나 시스템간의 SSO에서 상호 사용자 인증등을 위해서 사용된다. 무겁기는 하지만 표준화가 잘되어 있기 때문에 사용자 인증 시나리오에서는 현재에도 많이 사용된다.)

JWT는 이JSON Claim을 BASE64로 인코딩하여HTTP Header에 쉽게 넣을 수 있으며, JSON 기반이기 때문에 파싱과 사용이 쉽다.

결과적으로 Claim 기반의 토큰은 토큰 자체가 정보를 담음으로써, 토큰을 가지고 서비스나 API 접근을 제어할 때 별도의 작업이 서버에서 필요하지 않으며, 토큰 자체를 서버에서 관리할 필요가 없기 때문에 구현이 상대적으로 단순해진다.


JWT에 대한 소개


Claim 기반의 토큰에 대한 개념을 대략적으로 이해했다면, 그러면 실제로 JWT가 어떻게 구성되는지에 대해서 살펴보도록 하자.


Claim (메세지) 정의

JWT는 Claim을 JSON형태로 표현하는 것인데, JSON은 “\n”등 개행문자가 있기 때문에, REST API 호출시 HTTP Header등에 넣기가 매우 불편하다. 그래서, JWT에서는 이 Claim JSON 문자열을 BASE64 인코딩을 통해서 하나의 문자열로 변환한다.

{

  "id":"terry"

  ,"role":["admin","user"]

  ,"company":"pepsi"

}

<코드 2. JSON 기반의Claim 예제>

문자열을 BASE64 인코딩 한 결과

ew0KICAiaWQiOiJ0ZXJyeSINCiAgLCJyb2xlIjpbImFkbWluIiwidXNlciJdDQogICwiY29tcGFueSI6InBlcHNpIg0KfQ0K

<코드 3. JSON 기반의 Claim 코드 2를 BASE64 인코딩 한 결과>


변조 방지

위의 Claim 기반의 토큰을 봤으면, 첫번째 들 수 있는 의문이 토큰을 받은 다음에 누군가 토큰을 변조해서 사용한다면 어떻게 막느냐? 이다. 이렇게 메세지가 변조 되지 않았음을 증명하는 것을 무결성(Integrity)라고 하는데, 무결성을 보장하는 방법중 많이 사용되는 방법이 서명(Signature)이나 HMAC 사용하는 방식이다. 즉 원본 메세지에서 해쉬값을 추출한 후, 이를 비밀 키를 이용해서 복호화 시켜서 토큰의 뒤에 붙인다. 이게 HMAC방식인데,  누군가 이 메세지를 변조를 했다면,변조된 메세지에서 생성한 해쉬값과 토큰뒤에 붙어 있는 HMAC값이 다르기 때문에 메세지가 변조되었음을 알 수 있다. 다른 누군가가 메세지를 변조한후에, 새롭게 HMAC값을 만들어내려고 하더라도, HAMC은 앞의 비밀키를 이용해서 복호화 되었기 때문에, 이 비밀키를 알 수 없는 이상 HMAC을 만들어 낼 수 없다.


※ HMAC에 대한 자세한 설명은http://bcho.tistory.com/807 를 참고하기 바란다.

그래서 앞의 JSON 메세지에 대해서 SHA-256이라는 알고리즘을 이용해서 비밀키를 “secret” 이라고 하고, HMAC을 생성하면 결과는 다음과 같다.

i22mRxfSB5gt0rLbtrogxbKj5aZmpYh7lA82HO1Di0E

<코드 4. 코드 2의 JSON기반 Claim에 대해서, SHA1-256으로 생성한 HMAC>

서명 생성 방식

그러면 무결성 보장을 위해서 사용할 수 있는 알고리즘이 SHA1-256 HMAC 뿐일까? 보안요건에 따라서 SHA1-256,384,512. 그리고 공인 인증서 (Ceritification)을 이용한 RS256 등등 다양한 서명 방식을 지원한다. 그렇다면 JWT 토큰이 어떤 방식으로 서명이 되어 있는지는 어떻게 알 수 있을까?

그래서 JWT 토큰의 맨 앞부분에는 서명에 어떤 알고리즘을 사용했는지를 JSON형태로 정의한후, 이 JSON을 다시 BASE64 방식으로 인코딩한 문자열을 붙인다

{"alg":"HS256","typ":"JWT"}

<코드 5. JSON으로 서명 생성 방식은 SHA1-256으로 정의한 예>

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

<코드 6. 위의 코드 5 JSON 문자열을 BASE64 인코딩한 결과>

 

전체 메세지 포맷


위에서 설명한, 서명 방식, JSON 기반의 Claim,그리고 서명(Signature)까지 포함된 전체적인 JWT 토큰의 구조를 보면 다음과 같다.

{서명 방식을 정의한 JSON을 BASE64 인코딩}.{JSON Claim을 BASE64 인코딩}.{JSON Claim에 대한 서명}

이를 정리해서 그림으로 서술해 보면 다음과 같다.


<그림. JWT 토큰 구조>

그리고 결과로 나온, JWT 토큰은

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ew0KICAiaWQiOiJ0ZXJyeSINCiAgLCJyb2xlIjpbImFkbWluIiwidXNlciJdDQogICwiY29tcGFueSI6InBlcHNpIg0KfQ0K.i22mRxfSB5gt0rLbtrogxbKj5aZmpYh7lA82HO1Di0E

가 된다.


2편에서 다룰 내용

  • 유출 방지(암호화)

  • 상세 스펙

  • 구현예제



출처: http://bcho.tistory.com/999 [조대협의 블로그]

출처: http://bcho.tistory.com/999 [조대협의 블로그]

출처: http://ohgyun.com/470


키워드: OAuth

문제:
보안은 늘 어려운 것 같다.

잘 정리된 문서를 찾기도 어렵고, 있다 하더라도 난 좀 이해하기 어렵더라. @_@


이번에 OAuth 인증 처리가 필요하던 차에, 한빛 소프트에서 나온 EBook을 보게 됐다.

오잉~~ 쉽게 설명되어 있어서 참 좋더라. :D


http://www.hanb.co.kr/ebook/look.html?isbn=9788979149944



책 읽으면서 정리해둔 게 있어 옮겨둔다.



해결책:


서버사이드 웹 애플리케이션


1. 권한 서버로 권한 코드 요청하기


요청 URL 예: https://accounts.google.com/o/oauth2/auth


client_id: 등록한 애플리케이션의 아이디

redirect_uri: 권한 코드 획득 후 리다이렉트할 URI

scope: 요청 데이터의 접근 범위 (API 서버에 따라 컴마나 공백으로 구분)

response_type=code

state: CSRF를 막기위한 1회성 랜덤값

approval_prompt:

          사용자가 방문할 때마다 승인받을 것인가? 매번 받으려면 force, 처음만 받으려면 auto

access_type:

          사용자가 컴퓨터를 사용하지 않는 동안에도 접근하도록 할 것인가?

          그렇다면 offline이고, 이 땐 애플리케이션에서 재발급 토큰을 가져올 수 있다.

          online을 사용하면 재발급 토큰은 발급되지 않는다.



---> 이 요청을 보내면 API 서비스의 권한 허용 페이지로 이동한다.

     "어떤 앱이 이런이런 권한을 사용하려 합니다. 허용하겠습니까?"의 메시지다.

     허용하면, 위에 정의했던 redirect_uri 의 경로로 리다이렉트되며,

     이 때 접근 요청을 승인했을 때를 나타내는 권한 코드(code)와 요청에 포함했던 (state)가 함께 전달된다.



2. 권한 코드로 액세스 토큰 발급받기


이제, 전달받은 code와 state로 API 요청을 만들기 위해 권한 코드를 OAuth 액세스 토큰으로 교환해야 한다.

액세스 토큰의 엔드포인트로 HTTP POST 요청을 보내면 된다.


요청에는 아래 파라미터가 필요하다.


code: 애플리케이션에 전달되는 권한 코드

redirect_uri: 권한 엔드포인트에 첫 요청을 보낼 때의 등록된 위치

grant_type=authorization_code: 권한 코드의 액세스 토큰으로의 교환을 의미



헤더에는 client_id와 client_secret을 포함해 보낸다.

- HTTP Basic Authorization 헤더로 (client_id 와 client_secret을 포함하는 방식)

- POST의 파라미터로 client_id와 client_secret을 포함하는 방식



--> 이 요청이 인증되고 다른 파라미터가 유효하면, 권한 서버는 JSON 인코딩 응답에 OAuth 액세스 토큰을 리턴한다.

    access_token: API 요청을 허가하는데 사용하는 토큰

    token_type: 발급된 액세스 토큰의 타입. 주로 'bearer'가 사용되지만, 확장 가능하다.

    expires_in: 토큰은 시간 제약이 있을 수 있다. 이 값은 추가 정보이고 만료되기까지 남은 시간(초)를 의미한다.

    refresh_token: 액세스 토큰이 만료된 후 새로운 액세스 토큰을 얻기 위해 사용하는 토큰이다.

                         재발급 토큰이 있으면 offline 모드에서도 계속 API에 접근할 수 있다.

                         액세스 토큰과 재발급 토큰은 항상 기밀이 유지되어야 하고,

                         자원 소유자를 포함한 임의의 사용자에게 노출되지 말아야 한다.

                         재발급 토큰은 사용자 계정과 연관된 서버사이드 데이터베이스에 저장되어야 한다.

                         액세스 토큰도 데이터베이스에 저장할 수 있지만, 주로 성능 때문에 세션에 캐시한다.



3. API 호출하기


발급받은 액세스 토큰으로 API에 요청할 수 있다.

이를 전달 토큰(bearer token)이라 하고, 주로 헤더에 넣어 보낸다.


"Authorization: Bearer 액세스 토큰"




4. 액세스 토큰 재발급 받기


액세스 토큰을 발급받을 때 토큰의 만료시간을 계산해 함께 저장해둔다.

다음 요청 시 토큰이 만료되었다면, 재발급 토큰으로 다시 액세스 토큰을 발급받아온다.



* 액세스 토큰과 재발급 토큰

- 액세스 토큰의 유효함을 확인할 때 매번 권한 서버나 데이터베이스에 요청하면 API 응답이 늦어질 수 있다.

  이 때문에 주로 서명이나 암호화한 토큰을 사용해 검증 범위를 줄인다.

  사용자가 이전에 허가했던 애플리케이션의 접근을 취소할 수 있기 때문에,

  API 서비스가 암호화를 사용해 검증하더라도 안전할 수 있게 토큰의 유효 범위를 짧게하는 것이 필요하다.


     client_id

     client_secret

     grant_type=refresh_token

     refresh_token: 재발급 토큰

  --> 이 응답으로는 access_token, refresh_token, expires_in 을 받음 






클라이언트 사이드 웹 애플리케이션



암묵적 허가 플로우를 사용하며 아래 경우에 사용한다.

- 데이터에 접근이 일시적으로 필요할 때

- 사용자가 규칙적으로 API 제공 업체에 로긍니할 때

- OAuth 클라이언트가 자바스크립트, 플래시 등을 사용해 웹 브라우저에서 실행될 때

- 웹 브라우저의 신뢰도가 높고, 신뢰할 수 없는 사용자나 애프리케이션에 노출될 염려가 적을 때



1. 권한 서버로의 요청 파라미터


client_id

redirect_uri

scope

response_type=token (액세스토큰)


--> 권한을 얻으면 redirect_uri 에 정의한 페이지로 이동하면서,

     URL에 해시(#) 형태로 access_token과 관련 정보가 추가된다.


     http://example.com/callback#access_token=xxx&token_type=Bearer&expires_in=3600



2. API 요청하기


전달받은 access_token 으로 요청을 보내면 된다.

도메인이 다른 경우엔 jsonp로 보낸다.



3. 액세스 토큰 재발급 받기


암묵적 허가 플로우에서는 권한 코드 플로우와 달리 토큰을 재발급하기 위한 특정 프로토콜을 사용하지 않는다.

처음 토큰을 가져올 때와 동일한 방식으로 가져와야 한다.


아직 표준화되지 않았지만, 일부 OAuth 2.0 제공자들은 즉시 모드(immediate mode)를 지원한다.

즉시 모드는 사용자에게 알리지 않고 새로운 액세스 토큰을 투명하게 애플리케이션으로 보낼 수 있도록,

숨겨진 iframe 내에서 토큰 재발급 과정을 허용한다.

주로 `immediate=true`라는 파라미터를 추가로 제공해 해결한다.





자원 소유자 비밀번호 플로우


사용자 이름과 비밀번호를 액세스 토큰으로 교환하고, 재발급 토큰은 선택적으로 사용한다.

다른 OAuth 플로우에 비해 의미있는 보안성을 가지며,

주로 사용자의 강한 신뢰가 바탕이 되어야 한다.

자원 소유자의 비밀번호가 애플리케이션에 노출되기 때문에, 보통 API 제공 업체가 배포한 공식 애플리케이션에만 추천한다.



1. 사용자에게 인증 요청


사용자에게 아이디와 비밀번호를 받아 권한 서버로 요청을 보낸다.

아래 파라미터가 필요하다.


grant_type=password: 이 플로우에서는 'password'라고 기술한다.

scope: 접근 요청할 수 있는 데이터

client_id: (선택) 애플리케이션의 등록 값

client_secret: (선택) 파라미터 이름만 보면 비밀성을 내포하는 듯 하지만, 네이티브 모바일 애플리케이션 같은

               공개 클라이언트를 위해서도 API 제공 업체에서 가끔 사용한다.

               이런 경우, 파라미터 값은 비밀이 아니어서 애플리케이션 사용자가 발견할 수도 있다.

username: 자원 소유자가 제공하는 사용자 이름(utf-8)

password: 자원 소유자가 제공하는 비밀번호(utf-8)



--> 요청에 성공하면 아래 값을 application/json 형태로 응답한다.

     access_token: API 접근에 사용하는 액세스 토큰

     id: 사용자의 유일한 식별값.

               (예제에서는 URL 형태인데, 사용자에 대한 더 많은 정보를 얻기 위해 OAuth에서 보호되는 자원처럼 접근될 수 있다고 한다. 잘은 모르겠다)

     signature: (선택) 서버에 식별 URL을 보낸 후, 이 URL이 변경되지 않았음을 검증하기 위해 사용하는 서명이다.



2. API 호출


마찬가지로 헤더에 "Authorization: Bearer 액세스토큰" 값을 넣어 API를 요청하면 된다.

MiPlatform_XPLATFORM_보안취약점관련.pdf


출처: http://woony5231.tistory.com/entry/%EB%A7%88%EC%9D%B4%ED%94%8C%EB%9E%AB%ED%8F%BC%EC%9D%B4%EB%9E%80



PID Developer's Guide.pdf




마이플랫폼(MiPlatform)은 클라이언트/서버 환경의 웹의 클라이언트 환경을 대체할 대표적인 대안인 X-Internet 기반의


제품으로 국내 유일의 유무선 통합 UI 개발/운용 환경이다.

포레스트 리서치가 주창한 X-인터넷 개념 (eXecutable은 물론 eXtended Internet까지 확장된 개념)을 가장 완벽하게


재현해낸 솔루션으로 4GL 수준의 강력하고 풍부한 사용자 인터페이스(UI) 및 용이한 애플리케이션 개발 환경을 제공하고,


한번의 응용프로그램 개발로 웹은 물론 PDA, Smart Phone 등 다양한 채널을 통합관리 할 수 있으며, 또한 바코드 리더기 등


다양한 외부 장비와의 손쉬운 인터페이스를 제공한다.

이 밖에 다양한 출력 포맷 및 미려한 리포트 디자인을 지원하는 리포팅 툴을 기본 내장하였으며 필요한 경우 별도의 3rd Party


제품과의 손쉬운 연계가 가능하여 개발에서부터 화면 배포, 최종 출력까지 모든 프로세스를 통합 관리하는 솔루션이다.







효과적인 모바일 서비스 지원하는 X-인터넷 솔루션

투비소프트는 자사의 X-인터넷 솔루션인 ‘마이플랫폼’이


마이크로소프트 인터넷 익스플로러 대신, 자체 개발한 비즈니스 전용 브라우저를

 

기반으로 하고 있어 윈도우 운영체제에 독립적이라는 것을 특장점으로


내세우고 있다.


또 한 번의 웹 애플리케이션 개발로 클라이언트-서버 환경은 물론 모바일 환경


에서도 사용할 수 있도록 해주는 국내 유일한 X-인터넷 솔루션이라는 것 역시


투비소프트의 자랑으로, 특히 네트워크 트래픽을 최소화해 유무선 네트워크 환경에서


쾌적한 애플리케이션 사용을 보장하고 있다.


사용자 삽입 이미지

다른 국내 X-인터넷 솔루션들이 웹 브라우저(IE)를 기반으로 하고 있는데 비해


투비소프트에서 자체 개발한 비즈니스 전용 브라우저를 기반으로 하고 있는


마이플랫폼 3.1은 특히 신속한 데이터 조회를 필요로 하는 금융권에 적합하다. 

또한 직원들의 모바일을 통한 적재적시의 업무처리와 이용자(고객)들에게


다양한 모바일 서비스를 지원하고자 하는 물류, 통신, 제조, 의료, 대학 및


공공 기관으로 다양하게 도입돼 이용되고 있다. 

자체 개발한 비즈니스 전용 브라우저를 기반으로 한 마이플랫폼은 소스 코드가 윈도우 OS에 독립적이기 때문에 다양한


OS로의 이식이 가능하다. 따라서 한 번의 개발로 웹과 클라이언트 서버는 물론 동시에 다양한 모바일 서비스를 지원하는


국내 유일한 X-인터넷 솔루션이라 게 마이플랫폼의 특장점이다.

투비소프트 부설연구소 이우철 팀장은 “웹 브라우저를 기반으로 한 다른 솔루션들의 경우 웹 브라우저에 독립적이지 못하다.


예를 들면 웹 내에 버그가 생길 경우 웹이 고쳐져야만 해결되는 것처럼 웹이 제공 못하는 기능도 있는데, 마이플랫폼은 기본


OS에 독립적이란 게 다른 솔루션과의 가장 큰 차별점”이라고 설명한다. 

마이플랫폼 3.1은 자체 개발한 X-인터넷 개발툴인 PID(Presentation Interface Developer)툴을 지원함으로써


비주얼 베이직, 델파이, 파워빌더와 유사한 인터페이스 및 기능을 제공한다.


특히 PID툴은 개발자들이 소스 코드에 대한 개발 단계별 결과 값을 확인할 수 있어 개발 과정에서의 잘못된 부분을 찾는 데


용이한 라인 디버깅(Line Debugging) 기능과 다양한 프로젝트 폼의 조합을 지원하는 MDI(Multi Documents Interface) 등을


지원함으로써 개발자들의 손쉬운 업무 실현을 돕는다. 

실제로 중앙대병원은 다양한 의료 장비와 기존 시스템 자원(컴포넌트)과의 연동을 목적으로 마이플랫폼을 도입해


종합의료정보시스템을 운영중인데, 중앙대병원의 개발자들은 MDI를 이용해 손쉬운 개발과 다양한 응용을 함으로써


개발 생산성을 극대화하고 있다. 


투비소프트 이우철 팀장은 “개발 생산성의 핵심은 버그를 찾아내는 속도인데, PID 툴에서 디버깅에 대해서도 추적할 수 있는


라인디버깅 기능을 지원하기 때문에 개발 생산성을 높이는 데 효과적”이라며, “마이플랫폼은 코드 시작과 끝을 찾아내는 기능을


지원하는 국내 유일한 X-인터넷 솔루션” 이라고 강조한다. 

 

 

네트워크 트래픽 ‘최소화’로 모바일 환경에서도 ‘탁월’

마이플랫폼 3.1은 최대 80% 데이터 압축으로 네트워크 부하를 감소하고 캐싱 기능을 통해 네트워크 트래픽을 최소화한다.


또 자체 개발한 브라우저를 기반으로 하고 있기 때문에 개발자가 구현한 화면 산출물에 대한 최적화가 가능하다는 게 이우철


팀장의 설명. 

현재 조흥은행은 유무선 통합 뱅킹 서비스인 ‘원클릭’ 서비스에 마이플랫폼을 도입해 이용중이다. 기존에 인터넷 뱅킹 시


데이터 조회 속도가 느려 업무를 보는데 고객들이 불편을 겪었지만, 현재 마이플랫폼의 캐싱 기능으로 화면에 부하가 없고,


기존에 비해 데이터 패킷 양이 7배 정도 줄어들어 인터넷 환경이 좋지 않거나 원격지에서도 쾌적하게 사용할 수 있다. 

조흥은행 관계자는 “투비소프트의 마이플랫폼을 이용해 인터넷 뱅킹 애플리케이션을 개발했는데, 속도가 빠른 편이다.


남극에 파견돼 있는 연구소에서도 인터넷 뱅킹으로 편리하고 빠르게 계좌 데이터 조회 등 인터넷으로 뱅킹 서비스를 제공받고


있다”고 전했다. 

또한 SK텔레콤은 기업전용 모바일 플랫폼인 BCP(Business Common Platform)에 마이플랫폼을 도입했다. 마이플랫폼은


네트워크로 보내지는 데이터를 압축, 전송함으로써 트래픽을 최소화하기 때문에 기업들은 마이플랫폼을 이용해 개발한


데스크톱이나 서버용 애플리케이션을 다시 PDA용으로 전환할 필요 없이 기존에 개발한 애플리케이션을 그대로 모바일


환경에서 사용할 수 있어 시간과 비용, 안정성 측면에서 효과적이다. 

대출영업 모바일 시스템에 마이플랫폼을 적용한 한미은행 역시 직원들에게 PDA를 지급, 고객들에게 직접 화면을 보여주면서


설명, 계약 업무를 수행하고 있으며 기업은행, 제일화재, 대한생명 등도 투비소프트의 마이플랫폼으로 유무선을 연동해 타


업체와 차별화된 모바일 서비스로 경쟁력을 강화하고 있다. 

마이플랫폼은 암호화해 데이터를 주고받는 강력한 보안인 SSL을 기본 지원함은 물론, 멀티 모니터와 다양한 벤더의 보안


모듈과 연동할 수 있도록 지원한다. 강력한 보안 기능은 투비소프트 마이플랫폼이 금융권 고객을 다수 확보하고 있다는


데서도 알 수 있는 것. 

 

 

 

다양한 기기와 연동 가능한 오픈 아키텍처

버추얼 엔진을 기반으로 하는 마이플랫폼은 통신과 일부 컴포넌트를 고객 환경에 맞게 커스터마이징할 수 있다. 모바일 기기를


비롯한 외부기기들과 연동이 가능하며 통신 모듈을 따로 구축할 수 있어 기업들은 통신 부문을 제외하고 애플리케이션을


개발한 후, 추후 마이플랫폼의 통신 모듈을 연동시키면 된다. 때문에 개발자들의 업무 집중 효과도 높다는 설명이다.

또한 보통 웹 애플리케이션에서는 액티브X를 기반으로 그리드, 에디트 박스 등 컨트럴 패널이나 메뉴를 교체하기 위해서는


소스를 바꿔야 하지만, 컴포넌트 방식인 마이플랫폼은 화면 소스를 바꾸지 않고도 컨트롤 메뉴 교체가 가능하다.

그 밖에도 마이플랫폼 3.1은 개발자들에게 4GL 수준의 다양한 UI 컴포넌트를 제공함으로써 개발자들이 손쉬운 애플리케이션을


개발하도록 해 개발 생산성을 향상시킬 수 있다. 또 차트와 OLAP, 리포팅 툴이 내장돼 있어서 각 나라별, 연도별 대화방식으로


데이터를 보고 분석할 수 있고 편리한 프리젠테이션과 보고서 기능 등을 제공하고 있다.

이우철 팀장은 “X-인터넷 솔루션의 중요한 경쟁력은 그리드 기능이다. 마이플랫폼은 고객들이 원하는 그리드 안에서의 기본적인


엑셀 기능은 모두 지원하며 멀티헤더 구현이 가능하다는 점에서 다른 솔루션들과 차별점을 지닌다” 고 전한다. 

차트는 별도로 구매해 연동시키면 데이터 바인딩이 가능하지만, 리포팅 툴은 다른 제품이 별도 판매를 하는데 반해 마이플랫폼은


기본으로 내장하고 있어 비용상 경제적이라고 이우철 팀장은 덧붙인다. 

또 마이플랫폼은 ‘마이푸쉬 서버’와의 통신 기능이 기본 제공되는데, 이는 비즈니스 서비스에서 생성된 데이터를 클라이언트에


메시지 형태로 신속하게 전송해준다. 예를 들어 증권사에서 주식 시세가 바뀔 때마다 사용자의 클라이언트 화면에 신속하게


알려줄 수 있는 것이 마이푸쉬 서버로, 이 제품은 별도 판매된다. 

이우철 팀장은 “마이플랫폼은 ‘클라이언트 툴’ 이므로 서버가 무엇이든 다 접목시킬 수 있다. 마이푸쉬 서버처럼 패키지 형태로


구축하면 개발 기간이 줄고, 비용도 감소해 훨씬 효과적”이라고 강조한다.

또 RFID가 화두인 제조나 물류, 유통업체에서는 마이플랫폼이 내장돼 있는 ‘마이 RFID’를 도입함으로써 마이플랫폼과 RFID를


동시에 구현할 수 있다. 

투비소프트는 올해 마이플랫폼이 어떤 환경에서도 신속하게 구축될 수 있도록 하기 위해 마이크로소프트 환경에 독립적인


개발 방향을 강화한다는 방침이다. 

투비소프트 마이플랫폼 3.1
모바일을 비롯한 외부기기들과의 연동이 가능하고, 데이터 압축으로 네트워크 트래픽을 최소화함으로써 한 번 개발된 애플리케이션을 변환 없이 모바일에서도 사용할 수 있다. PID툴, 라인디버깅 기능, 다양한 UI 컴포넌트를 제공한다.

+ 유무선 연동 및 모바일 서비스 지원; 자체 개발한 PID툴 지원; 라인 디버깅 기능 및 MDI 제공; 네트워크 트래픽 최소화; 4GL 수준의 다양한 UI 컴포넌트 제공; 리포팅툴 기본 탑재 

- 윈도우 OS에 대한 종속성을 벗어난다는 취지에서 사용하고 있는 전용 브라우저가 기업 고객들에게는 또다른 구속이 될 수도 있다

비교 대상 쉬프트정보통신 가우스 닷넷 4.0

문의 투비소프트
02-2140-7700 www.tobesoft.com




사용자 삽입 이미지




사용자 삽입 이미지


'프로젝트 관련 조사 > ' 카테고리의 다른 글

OAuth 정리  (0) 2017.09.18
Miplatform 취약점 관련 파일  (0) 2016.11.21
마이플랫폼이란?  (0) 2016.11.21
ASP request.ServerVariable("QUERY_STRING")  (0) 2016.11.16
Tomcat SSL 적용 (테스트 인증서-Trial Version)  (0) 2016.08.29
아파치 톰캣 보안 가이드  (0) 2016.08.26

출처 : http://fendee.egloos.com/7097191


삭제 방지용으로 내용 붙여 넣는다.


<form method="post" enctype="text/html" action="<%=Request.ServerVariables("SCRIPT_NAME")%>?t2=t2&t3=t3">
  <input type="text" name="t1" value="t1">
  <input type="text" name="t4" value="t4">
  <input type="submit" value="확인">
</form>

<br>전송방식 : <%=Request.ServerVariables ("REQUEST_METHOD")%>
<br>쿼리 스트링: <%=Request.ServerVariables ("QUERY_STRING")%>
<br>IP주소 : <%=Request.ServerVariables ("REMOTE_ADDR")%>
<br>브라우저 : <%=Request.ServerVariables ("HTTP_USER_AGENT")%>
<br>로그온ID : <%=Request.ServerVariables ("LOGON_USER")%>
<hr>
<br>서버 포트 : <%=Request.ServerVariables ("SERVER_PORT")%>
<br>웹 서버 : <%=Request.ServerVariables ("SERVER_SOFTWARE")%>

<hr>

출력결과:

전송방식 : POST 
쿼리 스트링: t2=t2&t3=t3 
IP주소 : 127.0.0.1 
브라우저 : Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727) 
로그온ID : 
--------------------------------------------------------------------------------
서버 포트 : 8080 
웹 서버 : Microsoft-IIS/5.1

--------------------------------------------------------------------------------

설명:
Request.ServerVariables("QUERY_STRING") 은 쿼리스트링으로(get) 넘어온 모든 값을 보여준다.
Request.ServerVariables("LOGON_USER") 는 윈도우 인증으로 로그인이 된 경우의 사용자 아이디를 보여준다.


출처: http://visu4l.tistory.com/419


Tomcat 혹은 그외에 웹서버에 SSL을 적용시키기 전에 개발자들이 테스트용으로 사용하는 Trial SSL에 대해 설명한다.



1. 테스트 인증서 생성


명령어 : keytool -genkey -keyalg rsa -alias [Alias_Name] -keystore [KeyStore_File_Name]


keytool 명령은 java jdk 디렉토리 하위 bin 디렉토리에 있다. path가 지정되어있지 않으면 

해당 디렉토리로 이동후 실행하거나 절대경로로 실행해주어야 한다.

저는 path가 지정되어 있어 keytool 명령어로 실행한다.





인증서 생성시 물어보는 질문들은 다음과 같다.


> 키저장소 비밀번호 입력

> 새 비밀번호 다시 입력:

> 이름과 성을 입력하십시오. (인증서가 사용될 도메인을 입력하면된다.)

> 조직 단위 이름을 입력하십시오

> 조직 이름을 입력하십시오

> 구/군시 이름을 입력하십시오

> 시/도 이름을 입력하십시오

> 이 조직의 두 자리 국가 코드를 입력하십시오




2. csr 파일 생성


명령어 : keytool -certreq -alias [Alias_Name] -keyalg rsa -file [CSR_File_name] -keystore [KeyStore_File]



csr 파일을 확인해보면 아래와 같은 형태의 파일이 생성이 된다.






3. Trial SSL 인증서 발급받기


Trial SSL 인증서를 발급 받는 기관은 여러곳이 있지만 여기에선 시만텍을 사이트를 이용한다.

아래 사이트 접속

Trial 인증서 발급 받기


사이트를 그대로 따라가면 됩니다. Continue~




두번째에서 사용자 등록을 하는데 Email은 정확히 적으셔야합니다.

아래는 그다음은 알아서 등록 Continue~



다음으로 CSR을 등록하라는 페이지가 나왔는데 위해서 만들어두었던 csr.txt파일에 내용을 그대로 복사 후 Continue~



그러면 끝났습니다. 동의하고 Submit하면 

끝났습니다.

이제 메일을 확인해보시면 됩니다.



이제 인증서가 3가지 종류가 필요합니다.

1). 인증기관에서 발급받은 인증서

2). 루트인증서 (최상위 인증기관 인증서)

3). 체인인증서 (중간기관 인증서)


1). 인증기관에서 발급받은 인증서 


메일을 확인 해보면 시만텍에서 메일이 와있을겁니다. 본문 혹은 첨부파일에 


-----BEGIN CERTIFICATE-----

....

-----END CERTIFICATE-----


요런 형태의 시만텍 인증서가 있을겁니다.



그내용을 그대로 복사후 cert.pem 파일에 저장합니다.



2). 루트인증서


다시 메일을 확인해서 차근히 읽어 봅시다


Step 1. Download and install the Test Root CA Certificate.

Open the link below and follow the steps to install the Root certificate in your internet browser:
https://knowledge.verisign.com/support/ssl-certificates-support/index?page=content&id=SO10670


Root CA 파일 다운로드 링크가 걸린 부분이 있을겁니다. 저 링크를 타고 이동합니다.

혹시나 링크가 계속 변경될수 있으니 메일에 있는 링크로 이동하시기 바랍니다.


링크로 이동하면 다음과 같은 페이지가 보입니다.

페이지에서 중간에 

"How to install the trial root CA Certificate into ~~" 부분 뒤에 "SO10~~"링크를 클릭합니다.

아무 거나 들어가셔도 됩니다.


그러면 다음과 같은 화면이 나오고 중간에 "Download Secure Site Trial Root Certificate" 링크를 클릭합니다.


그러면 아래와 같이 또다른 인증서가 보입니다.

해당 인증거를 복사후 TrialRoot.pem 파일로 저장합니다.

파일이름은 알아서 정하면됩니다. 구분만 잘 할수 있으면 됩니다.



3). 체인인증서


다시 메일로 가서


Step 2. Download the Trial SSL Intermediate CA Certificate.

To download the Trial Intermediate CA on each Web server you are testing with, go to:
https://knowledge.verisign.com/support/ssl-certificates-support/index?page=content&id=AR1737


위부분을 찾습니다. 똑같이 링크로 이동

이번에는 바로 인증서가 나올겁니다.


똑같이 복사 후 저장 

저는 TrialIntermediate.pem 라는 이름으로 저장했습니다.






4. Trial SSL 인증서 합치기


keytool -import -alias Root -trustcscerts -file TrialRoot.pem -keystore testserver


keytool -import -alias Intermediate -trustcscerts -file TrialIntermediate.pem -keystore testserver


keytool -import -alias testserver -trustcscerts -file cert.pem -keystore testserver


3개에 인증서를 합치면 아래 와 같이 메시지가 나옵니다.





5. Tomcat Server 설정

Tomcat 폴더에 conf/server.xml 파일을 수정합니다.


<Connector 

sslProtocol="TLS" clientAuth="false" 

keystorePass="암호"

keystoreFile="C:/Users/Administrator/testserver" 

secure="true" scheme="https" maxThreads="150" 

SSLEnabled="true"  port="443"/>


keystorePass에 처음에 생성했던 keystore에 암호를 입력

keystoreFile에 keystore 파일 위치(절대경로)



이렇게 설정까지 하면 준비가 끝났다.


https://localhost 로 접속하면 이후부터는 ssl통신이 이루어진다.


만약에 접속후 아래와 같이 오류나 나더라도 무시하고 계속하면 됩니다.

이유는 해당 인증서가 신뢰된 인증서는 아니기때문입니다.


그렇다고 ssl통신이 안되는것은 아닙니다.




출처:https://geekflare.com/apache-tomcat-hardening-and-security-guide/


Apache Tomcat Hardening and Security Guide



Tomcat is one of the most popular Servlet and JSP Container servers. It’s used by some of following high traffic websites:

  • LinkedIn.com
  • Dailymail.co.uk
  • Comcast.net
  • Wallmart.com
  • Reuters.com
  • Meetup.com
  • Webs.com

Below diagram shows the market position of Tomcat in terms of popularity and traffic compared.

Having default configuration may have much sensitive information, which helps hacker to prepare for an attack the Tomcat server. This practical guide provides you the necessary skill set to secure Apache Tomcat server.

It was great to see the overwhelming response on my article about Apache Web Server Hardening and Security Guide. In this article, I will talk about how to harden and secure Apache Tomcat server. Following are tested on Tomcat 6.x and I don’t see any reason it won’t work with Tomcat 5.x, 7.x or 8.x

Audience

This is designed for Middleware Administrator, Application Support, System Analyst or anyone working or eager to learn Tomcat Hardening and Security. Fair knowledge of Tomcat & UNIX command is mandatory.

BONUS (Download in PDF Format): Tomcat Security & Hardening Guide

Pre-requisite

We require some tool to examine HTTP Headers for verification. Let’s do this by install firebug add-on in Firefox.

  • Click on Install Now
  • Restart Firefox
  • You can see firebug icon at right top bar

We will use this icon to open firebug console to view HTTP Headers information.

There are many online tools also available which helps to check in HTTP header information. Below are some of them you can try out.

Note: as a best practice, you must take backup of any file you are about to modify.

We will call Tomcat Installation folder as $tomcat throughout this guidelines.

1. Remove Server Banner

Removing Server Banner from HTTP Header is one of the first things to do as hardening. Having server banner expose the product you are using and leads to information leakage vulnerability.

Implementation:

  • Go to $tomcat/conf folder
  • Modify server.xml by using vi
  • Add following under Connector port and save the file
Server =” “

Ex: –

<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" 
Server =" " 
redirectPort="8443" />

Verification:

  • Open Firefox with firebug
  • Access Tomcat application
  • You will notice Server value is blank now.

2. Starting Tomcat with a Security Manager

Security Manager protects you from an untrusted applet running in your browser. Running Tomcat with a security manager is definitely better than running without one. Tomcat has very good documentation on Tomcat Security Manager

Implementation:

All you got to do is to start tomcat with –security argument.

Chandans:bin root# ./startup.sh -security
Using CATALINA_BASE:   /opt/tomcat
Using CATALINA_HOME:   /opt/tomcat
Using CATALINA_TMPDIR: /opt/tomcat/temp
Using JRE_HOME:        /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Using CLASSPATH:       /opt/tomcat/bin/bootstrap.jar:/opt/tomcat/bin/tomcat-juli.jar
Using Security Manager Chandans:bin root#

3. Enable access log logging

The default configuration doesn’t capture access logs. The access log is very useful in troubleshooting to check request type, requester IP address, status code, etc.

Implementation:

  • Go to $tomcat/conf
  • Modify server.xml by using vi
  • Go to the end of the file and uncomment Valve entry for valves.AccessLogValue
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs" 
prefix="localhost_access_log."
suffix=".txt"
pattern="common" resolveHosts="false"/>
  • Restart Tomcat server and you should see localhost_access_log is created under $tomcat/logs folder

4. Enforced HTTPS

It’s good to force redirect all HTTP requests to HTTPS to ensure web application data transmission are over SSL Certification.

Implementation:

  • Go to $tomcat/conf folder
  • Modify web.xml by using vi
  • Add following before </web-app> syntax
<security-constraint> 
<web-resource-collection> 
<web-resource-name>Protected Context</web-resource-name> 
<url-pattern>/*</url-pattern>
</web-resource-collection> 
<user-data-constraint> 
<transport-guarantee>CONFIDENTIAL</transport-guarantee> 
</user-data-constraint> 
</security-constraint>
  • Restart Tomcat and access web application to verify.

Note: ensure Tomcat is configured to run on SSL else it will break the application accessibility.

5. Add Secure flag in cookie

It is possible to steal or manipulate web application session and cookies without having a Secure flag in HTTP Header as Set-Cookie.

Implementation:

  • Go to $tomcat/conf folder
  • Modify server.xml by using vi
  • Add following in Connector port
Secure=”true

Ex:

<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" 
Server=" " 
Secure="true" 
redirectPort="8443" />

Verification:

  • Open Firefox with firebug
  • Access your application and check HTTP response header, you should see Secure flag

6. Add HttpOnly in cookie

Best practice to have this enabled at application code level. However, due to bad programming or developer’s unawareness, it comes to Web Infrastructure.

Implementation:

  • Go to $tomcat/conf folder
  • Modify context.xml by using vi
  • Add following  in Context directive
usehttponly=”true”

Ex:-

<context usehttponly="true">
...
</context>

7. Enable Secure Socket Layer (SSL)

To enable Tomcat to listen over HTTPS protocol, you must configure tomcat with SSL. If you are new to SSL, you can refer to Beginner’s Guide to SSL. This assumes you have SSL Certificate imported under keystore.

Implementation:

  • Go to $tomcat/conf folder
  • Modify server.xml by using vi
  • Add following under Connector port
SSLEnabled=”true” scheme=”https” keystoreFile="conf/keystore" keystorePass="password"

Ex:

<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" 
Server=" " 
Secure="true" 
SSLEnabled="true" scheme="https" keystoreFile="conf/keystore" keystorePass="password" clientAuth=”false” sslProtocol=”SSLv3” 
redirectPort="8443" />

8. Run Tomcat from non-privileged account

It’s good to use a separate non-privileged user for Tomcat. The idea here is to protect other services running in case of any security hole.

Implementation:

  • Create a UNIX user
  • Change $tomcat ownership to newly created UNIX user

9. Remove default/unwanted applications

By default, Tomcat comes with following web applications, which may or not be required in a production environment. You can delete them to keep it clean and avoid any known security risk with Tomcat default application.

  • ROOT – Default welcome page
  • Docs – Tomcat documentation
  • Examples – JSP and servlets for demonstration
  • Manager, host-manager – Tomcat administration

10. Change SHUTDOWN port and Command

By default, tomcat is configured to be shutdown on 8005 port. Do you know you can shutdown tomcat instance by doing a telnet to IP:port and issuing SHUTDOWN command?

Chandans # telnet localhost 8005
Trying ::1... telnet:
connect to address ::1:
Connection refused Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
SHUTDOWN Connection closed by foreign host.
Chandans #

You see having default configuration leads to high-security risk. It’s recommended to change tomcat shutdown port and default command to something unpredictable.

Implementation:

  • Go to $tomcat/conf folder
  • Modify server.xml by using vi
<Server port="8005" shutdown="SHUTDOWN">

8005 – Change to some other unused port

SHUTDOWN – Change to something complicated

Ex-

<Server port="8867" shutdown="NOTGONNAGUESS">

11. Replace default 404, 403, 500 page

Having default page for not found, forbidden, server error exposes Tomcat version and that leads to security risk if you are running with vulnerable version. Let’s look at default 404 page.

To mitigate, you can first create a general error page and configure web.xml to redirect to general error page.

Implementation:

  • Go to $tomcat/webapps/$application
  • Create an error.jsp file
#vi error.jsp 
<html>
<head> 
<title>404-Page Not Found</title>
</head>
<body> That's an error! </body>
</html>
  • Go to $tomcat/conf folder
  • Add following in web.xml by using vi. Ensure you add before </web-app> syntax
<error-page> 
<error-code>404</error-code> 
<location>/error.jsp</location>
</error-page>
<error-page> 
<error-code>403</error-code> 
<location>/error.jsp</location>
</error-page>
<error-page> 
<error-code>500</error-code> 
<location>/error.jsp</location>
</error-page>
  • Restart tomcat server. Now, let’s test it.

As you can see tomcat information is no more exposed.

You can do this for java.lang.Exception as well. This will help in not exposing tomcat version information if any java lang exception.

Just add following in web.xml and restart tomcat server.

<error-page> 
<exception-type>java.lang.Exception</exception-type> 
<location>/error.jsp</location>
</error-page>

I hope above guide gives you an idea no securing Tomcat. If you like this, please share with your friends.

출처: http://syaku.tistory.com/278


개발환경

Mac OS X 10.9.4
JAVA 1.6
Apache Tomcat 7.x
Spring 3.1.1
Spring Tool Suite 3.5.1
Maven 2.5.1

스프링 시큐리티(Spring Security)는 스프링 서브 프로젝트 중 하나로 스프링 기반의 어플리케이션을 보호하기 위한 필수적인 프레임워크이다. 스프링을 사용하면서 자체적으로 세션을 이용한 인증방식을 구현한다면 바보같은 짓일 것이다. 스프링 시큐리티는 보안을 체계적으로 관리하며 개발한 스프링 어플리케이션들과 유연하게 연결된다. 그리고 오랜기간 다양한 피드백으로 개발되어 신뢰도가 높을 것이다.
스프링에 최적화된 스프링 시큐리티 보다 안정적인 프레임워크는 아마 없을 것이다. 무엇보다 스프링에서는 스프링 시큐리티를 표준으로 정의하고 있다.

공식 사이트 : http://projects.spring.io/spring-security

스프링 시큐리티의 기본 예제를 먼저 알아보도록하겠다. 실무에서 사용할 수 없겠지만 스프링 시큐리트가 어떻게 구현되는 지 알 수 있는 예제이다.
프로젝트를 생성하고 스프링 시큐리티 라이브러리를 설치한다. Maven 설정에 2개의 dependency 를 추가한다.

@소스 pom.xml

<!-- Spring Security -->
<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-web</artifactId>
     <version>${org.springframework-version}</version>
</dependency>
<dependency>
     <groupId>org.springframework.security</groupId>
     <artifactId>spring-security-config</artifactId>
     <version>${org.springframework-version}</version>
</dependency>

<!— CGLib —>
<dependency>
     <groupId>cglib</groupId>
     <artifactId>cglib</artifactId>
     <version>3.1</version>
     <type>jar</type>
     <scope>compile</scope>
</dependency>

스프링 시큐리티를 사용하기 위해 아래와 같이 환경설정을 추가한다.

@소스 web.xml

<context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>
     /WEB-INF/spring/root-context.xml,
     classpath*:com/syaku/config/security-context.xml
     </param-value>
</context-param>

<!-- spring security -->
<filter>
     <filter-name>springSecurityFilterChain</filter-name>
     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
     <filter-name>springSecurityFilterChain</filter-name>
     <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-name>는 springSecurityFilterChain 이름으로 해야한다. 스프링에서 의존하는 필터이기 때문이다.

다음은 스프링에 스프링 시큐리티 설정정보를 추가한다. /src/main/resources/com/syaku/config/security-context.xml 파일을 생성한다.

@소스 security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
     xmlns="http://www.springframework.org/schema/security"
     xmlns:beans="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/security
     http://www.springframework.org/schema/security/spring-security.xsd
     ">

     <http auto-config="true">
          <intercept-url pattern="/**" access="ROLE_USER" />
     </http>

     <authentication-manager>
          <authentication-provider>
               <user-service>
                    <user name="guest" password="guest" authorities="ROLE_USER"/>
               </user-service>
          </authentication-provider>
     </authentication-manager>

</beans:beans>

테스트를 위해 웹페이지에서 아래와 같이 접속을 하면 로그인 화면이 출력된다.

http://localhost:8080/security

security-context.xml 에서 설정한 계정과 암호를 입력하여 로그인한다. authentication-manager 태그를 확인하면 된다.

http 태그는 접근 권한설정하는 부분이고, authentication-manager 태그는 접근 권한을 부여하는 부분이다.
그래서 guest 계정으로 로그인 하면 ROLE_USER 라는 권한을 부여받게 된다.

스프링 시큐리티에는 기본적인 로그인 부분을 제공하고 있기때문에 기본 설정만으로 로그인 로직을 구현할 수 있다. 하지만 실무에 사용하긴 힘들고 스프링 서큐리티가 어떻게 작동하는 지 알 수 있는 참고용으로 사용할 수 있다.

만약 다양한 접근 권한을 구현하기를 원한다면 인터셉터를 더많이 추가하면 된다. 현재는 모든 애플리케이션에 ROLE_USER 권한이 있는 사용자만 접근할 수 있게 설정된 것이다.

관리자 계정을 추가하고 관리자 계정만 접근할 수 있는 애플리케이션을 만들어 테스트해보기로 한다.

@소스 security-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
     xmlns="http://www.springframework.org/schema/security"
     xmlns:beans="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/security
     http://www.springframework.org/schema/security/spring-security.xsd
     ">

     <http auto-config="true">
          <intercept-url pattern="/" access="ROLE_USER" />
          <intercept-url pattern="/admin" access="ROLE_ADMIN" />
     </http>

     <authentication-manager>
          <authentication-provider>
               <user-service>
                    <user name="guest" password="guest" authorities="ROLE_USER"/>
                    <user name="admin" password="admin" authorities="ROLE_ADMIN"/>
               </user-service>
          </authentication-provider>
     </authentication-manager>

</beans:beans>

여기서 또 중요한 것이 access 의 첫문자 패턴이 ROLE_ 시작해야한다. 변경이 가능하나, 구지 그럴필요가 없기때문에 모든 권한은 ROLE_ 시작할 수 있게 구성하도록한다.

권한을 추가하였다면 admin 컨트롤러도 추가한다. HomeController.java 파일을 열어 아래의 메서드를 추가한다.

@소스 HomeController.java

@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String admin() {
     return "admin";
}

그리고 컨트롤러의 뷰페이지 파일을 추가한다. src/main/webapp/WEB-INF/views/admin.jsp 소스 내용은 알아서 추가한다.
http://localhost:8080/admin 으로 접근하면 권한이 없다는 오류메세지가 출력된다.

근데 문제는 admin/ 접근하면 권한 체크를 하지 않고 접속이 가능해진다. 이럴때 패턴을 /admin/** 주면된다. 하지만 하위 폴더에는 다른 권한을 줘야할 경우가 생길수가 있다. 이럴때는 최상 권한을 제일 하위에 등록하고 그 위에 추가할 경로의 권한을 추가하면 된다.


<intercept-url pattern="/" access="ROLE_USER" />
<intercept-url pattern="/admin/test" access="ROLE_USER" />
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />

이처럼 접근 권한은 위에서 아래로 순차적으로 인터셉터가 처리하게 된다. 그리고 intercept-url 의 pattern 은 말 그대로 경로의 패턴 값을 입력하는 것이다. 경로 값으로 오해하지 않도록한다.

Spring EL 표현식

http 설정에서 use-expressions=true 한 경우 SpEL(Spring EL expressions) 를 사용할 수 있다. 기본 값은 false 이다.

스프링 표현 언어(SpEL) 에 대한 자세한 설명은 아래와 같다.

ExpressionDescription
hasRole([role])Returns true if the current principal has the specified role.
hasAnyRole([role1,role2])Returns true if the current principal has any of the supplied roles (given as a comma-separated list of strings)
principalAllows direct access to the principal object representing the current user
authenticationAllows direct access to the current Authentication object obtained from the SecurityContext
permitAllAlways evaluates to truedenyAllAlways evaluates to false
isAnonymous()Returns true if the current principal is an anonymous user
isRememberMe()Returns true if the current principal is a remember-me user
isAuthenticated()Returns true if the user is not anonymous
isFullyAuthenticated()Returns true if the user is not an anonymous or a remember-me user

출처 : 스프링 프레임워크 시큐리티 도움말 3.1.x

SpEL 방식이 아닌 경우 아래와 같은 설정할 수 있다.

AUTHORITYDESCRIPTION
IS_AUTHENTICATED_ANONYMOUSLY익명 사용자
IS_AUTHENTICATED_REMEMBEREDREMEMBERED 사용자
IS_AUTHENTICATED_FULLY인증된 사용자

출처 : 전자정부프레임워크 도움말

스프링 서큐리티 어노테이션

좀 더 유연하게 접근 권한을 설정하기 위해 어노테이션을 사용할 수 있다. 한 곳에서 권한을 설정한다면 어플리케이션이 추가될때마다 수정되어야 하는 문제가 발생한다. 여러 어플리케이션을 개발하는 프로젝트라면 각각의 어플리케이션에 맞게 개별적으로 설정하는 것이 효과적이다.
[참고] 스프링 서큐리티 어노테이션을 사용하기 위해 CGLib 가 필요하다.

기존에 생성했던 security-context.xml 에 http 태그를 <http auto-config="true" /> 수정한다.
security-context.xml 파일은 공통적인 보안에 대한 설정을 담당하는 파일이라고 생각하면 될 것 같다.
그리고 servlet-context.xml 파일을 열어 아래와 같이 수정한다.

@소스 servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:beans="http://www.springframework.org/schema/beans"
     xmlns:context="http://www.springframework.org/schema/context"
     xmlns:security="http://www.springframework.org/schema/security"
     xsi:schemaLocation="
     http://www.springframework.org/schema/mvc
     http://www.springframework.org/schema/mvc/spring-mvc.xsd
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/security
     http://www.springframework.org/schema/security/spring-security.xsd
     ">

     <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

     <!-- Enables the Spring MVC @Controller programming model -->
     <annotation-driven />

     <security:global-method-security secured-annotations="enabled" />

     <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
     <resources mapping="/resources/**" location="/resources/" />

     <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
     <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
          <beans:property name="prefix" value="/WEB-INF/views/" />
          <beans:property name="suffix" value=".jsp" />
     </beans:bean>

     <context:component-scan base-package="com.syaku.security" />

</beans:beans>

기본 소스에서 security 스키마와 <security:global-method-security secured-annotations="enabled" /> 설정이 추가되었고, @Secured 어노테이션을 사용할 수 있다. 본 어노테이션에는 SpEL 표현식은 사용할 수 없다. 기본적인 룰과 생성한 룰만을 사용할 수 있고, 여러가지 룰을 적용하려면 배열을 사용하면 된다. @Secured({"ROLE_ADMIN","ROLE_USER"})

컨트롤러 파일을 열어 접근 권한 어노테이션을 추가하면 된다.

@소스 HomeController.java

package com.syaku.security;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {

     private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

     /**
      * Simply selects the home view to render by returning its name.
      */
     @Secured("ROLE_USER")
     @RequestMapping(value = "/", method = RequestMethod.GET)
     public String home(Locale locale, Model model) {
          logger.info("Welcome home! The client locale is {}.", locale);

          Date date = new Date();
          DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

          String formattedDate = dateFormat.format(date);

          model.addAttribute("serverTime", formattedDate );

          return "home";
     }

     @Secured("ROLE_ADMIN")
     @RequestMapping(value = "/admin", method = RequestMethod.GET)
     public String admin() {
          return "admin";
     }

     @Secured("ROLE_USER")
     @RequestMapping(value = "/admin/test", method = RequestMethod.GET)
     public String admin_test() {
          return "admin";
     }

}

기존 소스에서 @Secured 가 각 메서드마다 추가되었다. 접근 권한에 대해서는 이전에 설명했던것 처럼 룰을 적용하면 된다.

만약 로그인도 했고 해당 메서드에 권한도 있는 데… 접근할 수 없는 곳이 있다면?
쉽게말해… 게시판을 예로들어 글에 대한 제어 권한은 작성한 사용자만이 글을 수정 및 삭제할 수 있게 해야한다면 @Secured 만으로 구현할 수 없다. @Secured 는 SpEL 표현식을 지원하지 않기 때문이다.

그래서 스프링 시큐리티에서 지원하는 새로운 어노테이션을 사용해야 한다.
어노테이션을 사용하기 위해 다음과 같이 설정해야 한다.

<security:global-method-security pre-post-annotations="enabled" />

@Pre* : 메서드 인자값에 접근할 수 있다.
@Post* : 메서드 반환값에 접근할 수 있다.

어노테이션설명
@PreFilter메서드 인자 값을 필터한다.
@PreAuthorize메서드 인자 값을 검증한다.
@PostFilter메서드 반환 값을 필터한다.
@PostAuthorize메서드 반환 값을 검증한다.

@PerAuthorize

간단한 예제로 계정을 비교하는 프로그램을 아래와 같이 만들어 본다.

Service 와 VO 두개의 클래스 파일을 생성한다.

@소스 UserVO.java

package com.syaku.security;

public class UserVO {
     private String user_name;
     private String password;

     public void setUser_name(String user_name) {
          this.user_name = user_name;
     }

     public String getUser_name() {
          return this.user_name;
     }

     public void setPassword(String password) {
          this.password = password;
     }

     public String getPassword() {
          return this.password;
     }
}

@소스 HomeService.java

package com.syaku.security;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class HomeService {
     private static final Logger logger = LoggerFactory.getLogger(HomeService.class);

     @PreAuthorize("#userVO.user_name == authentication.name or hasRole(‘ROLE_ADMIN')")
     public void getUser(UserVO userVO) {
          // 테스트를 위한 로그 출력
          logger.info("getUser success");
     }
}

만약 디비로 구성한다면 @Service 계층에 CURD 메서드들을 구성하면 된다. 이제 컨트롤러에서 서비스 메서드를 호출하면 된다.
테스트를 위해 동적인 계정을 얻기 위해 파라메터 값을 사용해 비교하였다.

@소스 HomeController.java


@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model,@RequestParam(value="user", defaultValue="", required=true) String user) {
     logger.info("Welcome home! The client locale is {}.", locale);

     // 파라메터 값을 얻어 삽입
     UserVO userVO = new UserVO();
     userVO.setUser_name(user);

     // 서비스 호출
     homeService.getUser(userVO);

     Date date = new Date();
     DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

     String formattedDate = dateFormat.format(date);

     model.addAttribute("serverTime", formattedDate );

     return "home";
}

http://localhost:8080/security/ 를 접속하여 테스트한다. 로그인은 guest 로 한다.
위 경로로 접속하면 익셉션이 발생한다. 비교대상인 계정의 파라메터가 없기 때문이다. 하지만 admin 계정으로 접속하면 정상적인 페이지가 출력된다.

http://localhost:8080/security/?user=guest 접속하면 @Service 계층의 테스트 로그가 출력되고 정상적인 화면이 출력된다. 로그인한 계정과 파라메터로 넘긴 계정이 일치하기 때문이다.

로그인 정보 얻기

3가지 방법이 있고, 조금식 다른 방식과 결과를 출력한다. 그에 맞게 사용하면 될 것 같다.

@소스 HomeController.java

@Secured({"ROLE_USER","ROLE_ADMIN"})
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String user(Principal principal) {

     // 첫번째 방법
     Authentication auth = SecurityContextHolder.getContext().getAuthentication();
     logger.info(auth.toString());

     // 두번째 방법
     User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
     logger.info(user.toString());

     // 세번째 방법
     logger.info(principal.toString());

     return "home";
}

첫번째 결과

INFO : com.syaku.security.HomeController - org.springframework.security.authentication.UsernamePasswordAuthenticationToken@be01b6d0: Principal: org.springframework.security.core.userdetails.User@5e22dd8: Username: guest; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffe9938: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: 02543D109730FC954434CFFE9B3E4D5E; Granted Authorities: ROLE_USER

두번째 결과

INFO : com.syaku.security.HomeController - org.springframework.security.core.userdetails.User@5e22dd8: Username: guest; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER

세번째 결과

INFO : com.syaku.security.HomeController - org.springframework.security.authentication.UsernamePasswordAuthenticationToken@be01b6d0: Principal: org.springframework.security.core.userdetails.User@5e22dd8: Username: guest; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffe9938: RemoteIpAddress: 0:0:0:0:0:0:0:1%0; SessionId: 02543D109730FC954434CFFE9B3E4D5E; Granted Authorities: ROLE_USER

첫번째와 세번째 결과는 같다. 하지만 호출방식이 틀리니 참고한다.

참고 : http://www.mkyong.com/spring-security/get-current-logged-in-username-in-spring-security/

+ Recent posts