8 장. JBoss에서의 보안

J2EE 보안 환경설정과 아키텍쳐

보안은 어떠한 엔터프라이즈 어플리케이션에서도 기본적인 사양에 포함됩니다. 여러분은 어플리케이션에 누구가 액세스할 수 있는지와 사용자들이 어플리케이션의 어떤 조작까지 가능하게 할지와 같은 제어기능을 필요로 합니다. J2EE 시방서에서는 EJB와 웹 컴포넌트들을 위해 간단한 롤-기반(role-based) 보안 모델을 정의하고 있습니다. 보안을 처리하는 JBoss 컴포넌트 프레임워크는 JBossSX 익스텐션 프레임워크입니다. JBossSX 보안 익스텐션에서는 롤-기반의 선언적인(declarative) J2EE 보안 모델뿐만 아니라 보안 프록시 계층을 통해 커스텀 보안의 통합까지도 지원할 수 있습니다. 선언적인 보안 모델의 기본 구현은 자바 증명(Authentication)과 인가(Authorization) 서비스(JAAS) 로그인 모듈과 주제들(subjects)에 기반을 두고 있습니다. 보안 프록시 계층(layer)을 통해 선언적인 모델을 사용해서 기술할 수 없는 커스텀 보안을 EJB 비즈니스 객체와 독립적인 방식으로 EJB에 추가시킬 수 있게 합니다. JBoss 보안 구현을 자세히 알아보기전에 우리는 먼저 EJB와 서블릿 스펙에서의 보안 모델들뿐만 아니라 이들을 세부적으로 구성하는 기본을 이루는 JAAS까지도 리뷰해볼 것입니다.

8.1. J2EE 선언적 보안 개요

J2EE 시방서에서 권장하는 보안 모델은 선언적 모델입니다. 선언적 보안 모델에서는 여러분의 비즈니스 컴포넌트내에 보안을 내장하기보다는 표준 XML 서술자를 사용하여 보안의 역할(role)과 허가권(permission)을 기술하도록 되어 있습니다. 이렇게 비즈니스-레벨의 코드로부터 보안을 분리시키는 이유는 배치된 컴포넌트의 보안이 컴포넌트가 가진 비즈니스 로직 측면에 따라 결정되는 것보다 훨씬 다양한 기능을 요구받기 때문입니다. 가령, 은행 계좌에 접근하는데 사용되는 ATM 컴포넌트를 예로 들어보겠습니다. 역할과 허가권등과 같은 보안 요구사항들은 ATM 기계가 배치된 곳에서 어떤 은행이 계좌를 관리하느냐에 따라 어떻게 은행 계좌로 접근하느냐가 매우 독립적이어야 합니다.

J2EE 어플리케이션의 보안은 표준 J2EE 배치 서술자를 통해 어플리케이션 보안 요구사항들의 사양서에 따라 달라지게 됩니다. 여러분은 ejb-jar.xml 과 web.xml 배치 서술자를 사용하여 엔터프라이즈 어플리케이션내의 EJB들과 웹 컴포넌트들로의 액세스를 보호합니다. 그림 8.1, “보안과 관련된 요소들을 보여주고 있는 EJB 2.0 배치 서술자 컨텐츠 모델의 서브셋.” 그림 8.2, “보안과 관련된 요소들을 보여주고 있는 서블릿 2.2 배치 서술자 컨텐츠의 서브셋.” 에서는 각각 EJB 2.0과 서블릿 2.2 배치 서술자내에서 보안과 관련된 요소들을 보여주고 있습니다.

보안과 관련된 요소들을 보여주고 있는 EJB 2.0 배치 서술자 컨텐츠 모델의 서브셋.

그림 8.1. 보안과 관련된 요소들을 보여주고 있는 EJB 2.0 배치 서술자 컨텐츠 모델의 서브셋.

보안과 관련된 요소들을 보여주고 있는 서블릿 2.2 배치 서술자 컨텐츠의 서브셋.

Figure 8.2. 보안과 관련된 요소들을 보여주고 있는 서블릿 2.2 배치 서술자 컨텐츠의 서브셋.

그림 8.1, “보안과 관련된 요소들을 보여주고 있는 EJB 2.0 배치 서술자 컨텐츠 모델의 서브셋.” 그림 8.2, “보안과 관련된 요소들을 보여주고 있는 서블릿 2.2 배치 서술자 컨텐츠의 서브셋.”에 주어진 다양한 보안 요소들의 목적과 사용법은 다음에 오는 섹션에서 다루게 됩니다.

8.1.1. 보안 레퍼런스

EJB와 서블릿 모두 한개 이상의 security-role-ref 요소를 정의할 수 있습니다. 이 요소는 isCallerInRole(String) 메쏘드의 인수로써 role-name 값을 사용하는 컴포넌트를 정의하는데 사용됩니다. isCallerInRole 메쏘드를 사용하여 하나의 컴포넌트는 security-role-ref/role-name 요소와 함께 선언되어진 caller가 있는지 검증할 수 있습니다. role-name 요소의 값은 반드시 role-link 요소를 통해 security-role 요소에 링크되어야만 합니다. isCallerInRole의 전형적인 사용은 역할 기반의 method-permissions 요소들을 사용해서 정의할 수 없는 보안 점검을 수행하는 것입니다. 하지만, 이 결과가 컴포넌트 코드내에 보안 로직으로 내장되기때문에 tt class="literal">isCallerInRole의 사용을 권장하고 있지 않습니다. 예제 8.4, “security-role 요소의 사용법을 보여주는 ejb-jar.xml 서술자의 예제중 일부분.” 예제 8.5, “security-role 요소 사용법을 보여주는 web.xml 서술자의 예제중 일부분.”에서는 security-role-ref 요소의 사용법을 보여주는 서술자의 예제중 일부분입니다.

예제 8.1. security-role 요소의 사용법을 보여주는 ejb-jar.xml 서술자의 예제중 일부분.

<!-- A sample ejb-jar.xml fragment -->
<ejb-jar>
  <enterprise-beans>
    <session>
      <ejb-name>ASessionBean</ejb-name>
      ...
      <security-role-ref>
          <role-name>TheRoleICheck</role-name>
          <role-link>TheApplicationRole</role-link>
      </security-role-ref>
    </session>
  </enterprise-beans>
  ...
</ejb-jar>

예제 8.2. security-role 요소 사용법을 보여주는 web.xml 서술자의 예제중 일부분.

<web-app>
  <servlet>
    <servlet-name>AServlet</servlet-name>
    ...
    <security-role-ref>
      <role-name>TheServletRole</role-name>
      <role-link>TheApplicationRole</role-link>
    </security-role-ref>
  </servlet>
  ...
</web-app>

8.1.2. 보안 식별성(Identity)

EJB들은 선택적으로 security-identity 요소를 선언할 수 있습니다. EJB 2.0부터는 다른 컴포넌트에서 메쏘드를 호출할 때 어떤 식별자를 EJB가 사용해야만 할지를 지정하는 기능이 추가되었습니다. 호출 식별자는 현재의 caller에 대한 것이 될 수도 있고 또는 특정한 역할(role)이 될 수도 있습니다. 어플리케이션 어셈블러는 EJB에 의해 만들어진 메쏘드 호출을 위한 보안 식별자로써 전파되어져야 하는 현재의 caller 식별자를 지시하기 위해 use-caller-identity 자식 요소와 함께 security-identity 요소를 사용합니다. caller의 식별자 전파는 분명하게 security-identity 요소를 선언하지 않은 경우에 기본적으로 사용됩니다.

이와 유사하게, 어플리케이션 어셈블러는 EJB에 의해 만들어진 메쏘드 호출에 대한 보안 식별자로써 사용되어야만 하는 role-name 값에 의해 주어진 특정한 보안 역할을 지정하기위한 run-as/role-name 자식 요소를 사용할 수 있습니다. EJBContext.getCallerPrincipal()에서 보이듯이 caller의 식별자에는 아무런 변화가 없음에 주의하십시오. 이보다는 caller의 보안 역할이 run-as/role-name 요소의 값에 의해 지정된 단일 역할로 설정되게 됩니다. run-as 요소의 use case중에 하나는 내부의 EJB를 외부의 클라이언트에서 접속하지 못하게 하는 것입니다. 이러한 동작은 외부에 있는 클라이언트에게 절대로 역할을 할당해주지 않도록 제한된 액세스인 내부 EJB의 method-permission 요소를 할당함으로써 구현할 수 있습니다. 내부 EJB를 사용할 필요가 있는 EJB들은 제한된 역할과 동등한 run-as/role-name 을 갖도록 설정되어 집니다. security-identity 요소의 사용법은 예제로 제공되는 서술자의 일부를 발췌한 예제 8.3, “security-identity 요소의 사용법을 보여주는 예제 ejb-jar.xml 서술자의 일부분.”에 보여지고 있습니다.

예제 8.3. security-identity 요소의 사용법을 보여주는 예제 ejb-jar.xml 서술자의 일부분.

<!-- A sample ejb-jar.xml fragment -->
<ejb-jar>
    <enterprise-beans>
        <session>
            <ejb-name>ASessionBean</ejb-name>
            <!-- ... -->
            <security-identity>
                <use-caller-identity/>
            </security-identity>
        </session>
        <session>
            <ejb-name>RunAsBean</ejb-name>
            <!-- ... -->
            <security-identity>
                <run-as>
                    <description>A private internal role</description>
                    <role-name>InternalRole</role-name>
                </run-as>
            </security-identity>
        </session>
    </enterprise-beans>
    <!-- ... -->
</ejb-jar>

8.1.3. 보안 역할(role)

security-role-refsecurity-identity 요소중에 하나에서 참조되는 보안 역할의 이름은 어플리케이션의 선언된 역할들중에 하나로 매핑될 필요가 있습니다. 어플리케이션 어셈블러는 security-role 요소들을 선언해줌으로써 논리적인 보안 역할을 정의합니다. role-name 값은 Administrator, Architect, SalesManager와 같은 논리적인 어플리케이션 역할의 이름입니다.

역할(role)이란 무엇입니까? J2EE 사양서에 따르면 배치 서술자내에서 보안 역할은 어플리케이션의 논리적인 보안 뷰를 정의하는 데 사용하는 중요한 개념으로 기술되어 있습니다. J2EE 배치 서술자내에 정의된 역할은 대상이 되는 엔터프라이즈의 오퍼레이션 환경내에 존재하는 사용자 그룹, 사용자, 주도자(principals) 및 다른 개념들과 혼동되어서는 않됩니다. 배치 서술자내의 역할들은 어플리케이션 도메인에 특정한 이름들과 함께 어플리케이션 구성개념(constructs) 입니다. 예를 들자면, 은행 어플리케이션은 BankManager, Teller 그리고 Customer와 같은 이름의 역할들을 사용하게 됩니다.

JBoss에서 하나의 security-role은 단지 참조되는 컴포넌트 역할인 논리적인 역할에 security-role-ref/role-name 값을 매핑시키는데만 사용되어집니다. 사용자에게 할당된 역할은 JBossSX 구현을 자세하게 논의하면서 알게되는 어플리케이션의 보안 관리자의 동적 함수입니다. JBoss는 메쏘드의 승인(permission)을 선언하기위해 security-roles을 정의할 필요는 없습니다. 따라서, security-role 요소들의 지정은 어플리케이션 서버들사이에서의 이식성에 대한 보장과 배치 서술자의 유지보수를 위한 좋은 연습일뿐입니다. 예제 8.4, “security-role 요소의 사용법을 보여주는 예제 ejb-jar.xml 서술자의 일부분.” 예제 8.5, “security-role 요소의 사용법을 보여주는 예제 web.xml 서술자의 일부분.”에서 예제로 제공되는 서술자의 일부분을 통해 security-role 요소의 사용법을 보여주고 있습니다.

예제 8.4. security-role 요소의 사용법을 보여주는 예제 ejb-jar.xml 서술자의 일부분.

<!-- A sample ejb-jar.xml fragment -->
<ejb-jar>
    <!-- ... -->
    <assembly-descriptor>
        <security-role>
            <description>The single application role</description>
            <role-name>TheApplicationRole</role-name>
        </security-role>
    </assembly-descriptor>
</ejb-jar>

예제 8.5. security-role 요소의 사용법을 보여주는 예제 web.xml 서술자의 일부분.

<!-- A sample web.xml fragment -->
<web-app>
    <!-- ... -->
    <security-role>
        <description>The single application role</description>
        <role-name>TheApplicationRole</role-name>
    </security-role>
</web-app>

8.1.4. EJB 메쏘드의 승인(permissions)

어플리케이션 어셈블러는 method-permission 요소 선언을 통해 EJB의 home과 remote 인터페이스 메쏘드를 호출할 수 있도록 해주는 역할을 설정할 수 있습니다. 각각의 method-permission 요소에는 method 자식 요소들에 의해 식별되어지는 EJB 메쏘드들에 액세스할 수 있는 논리적인 역할을 정의해주는 하나 이상의 role-name 자식 요소들을 포함합니다. EJB 2.0에서 여러분은 method 자식 요소들에 의해 식별되는 메쏘드들을 액세스할 수 있는 허가된 어떠한 사용자라도 정의해주는 role-name 요소 대신 unchecked 요소를 지정해줄 수 있게 되었습니다. 또한 여러분은 exclude-list 요소를 사용하여 메쏘드에 누구도 액세스할 수 없도록 정의할 수도 있습니다. EJB가 method-permission 요소를 사용하여 역할에 의해 액세스가 가능하도록 선언되어 있지 않은 메쏘드를 갖고 있다면, 이 EJB 메쏘드는 기본적으로 사용에서 배제되게 됩니다. 이것은 기본적으로 메쏘드들을 exclude-list쪽에 추가해주는 것과 동일한 효과를 갖습니다.

method 요소의 선언에는 3개의 지원되는 스타일이 있습니다.

  • Style 1 은 이름이 붙은 엔터프라이즈 빈의 모든 home과 컴포넌트 인터페이스 메쏘드들을 참조할때 사용됩니다.
<method>
    <ejb-name>EJBNAME</ejb-name>
    <method-name>*</method-name>
</method>
  • Style 2 는 이름이 붙은 엔터프라이즈 빈의 home 또는 컴포넌트 인터페이스중에 지정된 메쏘드를 참조할때 사용됩니다. 똑같은 이름으로 중첩된 여러 메쏘드가 존재한다면, 이 스타일에서는 모든 중첩된 메쏘드들을 참조합니다.
<method>
    <ejb-name>EJBNAME</ejb-name>
    <method-name>METHOD</method-name>
</method>
  • Style 3 은 중첩된 이름을 갖는 일련의 메쏘드 셋내에서 지정된 메쏘드를 참조할때 사용됩니다. 이 메쏘드는 반드시 특정한 엔터프라이즈 빈의 home 또는 remote 인터페이스내에 정의되어야만 합니다. method-param 요소의 값은 대응되는 method 파라메터의 타입의 완전한 형태를 갖는 이름입니다. 만약 동일한 중첩된 서명(signature)를 갖는 여러 메쏘드들이 존재한다면, 일치하는 중첩된(overloaded) 메쏘드들 모두에 대해 승인이 적용됩니다.
<method>
    <ejb-name>EJBNAME</ejb-name>
    <method-name>METHOD</method-name>
    <method-params>
        <method-param>PARAMETER_1</method-param>
        <!-- ... -->
        <method-param>PARAMETER_N</method-param>
    </method-params>
</method>

선택적으로 사용되는 method-intf 요소에서는 엔터프라이즈 빈의 home과 remote 인터페이스 모두에 정의되어 있는 동일한 이름과 서명을 갖는 메쏘드들을 차별화(differentiate)하는데 사용할 수 있습니다. 예제 8.6, “method-permission 요소의 사용법을 보여주는 예제 ejb-jar.xml 서술자의 일부분.”에서는 method-permission 요소의 사용방법 예제를 보여주고 있습니다.

Example 8.6. method-permission 요소의 사용법을 보여주는 예제 ejb-jar.xml 서술자의 일부분.

<ejb-jar>
    <assembly-descriptor>
        <method-permission>
            <description>employee 와 temp-employee 역할에서는 EmployeeService 빈의 
                어떠한 메쏘드에도 액세스할 수 있습니다 </description>
            <role-name>employee</role-name>
            <role-name>temp-employee</role-name>
            <method>
                <ejb-name>EmployeeService</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>
        <method-permission>
            <description>employee 역할은 AardvarkPayroll 빈의 
                findByPrimaryKey, getEmployeeInfo 그리고 updateEmployeeInfo(String)
                메쏘드에 액세스할 수 있습니다 </description>
            <role-name>employee</role-name>
            <method>
                <ejb-name>AardvarkPayroll</ejb-name>
                <method-name>findByPrimaryKey</method-name>
            </method>
            <method>
                <ejb-name>AardvarkPayroll</ejb-name>
                <method-name>getEmployeeInfo</method-name>
            </method>
            <method>
                <ejb-name>AardvarkPayroll</ejb-name>
                <method-name>updateEmployeeInfo</method-name>
                <method-params>
                    <method-param>java.lang.String</method-param>
                </method-params>
            </method>
        </method-permission>
        <method-permission>
            <description> admin 역할은 EmployeeServiceAdmin 빈의 
                어떠한 메쏘드에도 액세스할 수 있습니다 </description>
            <role-name>admin</role-name>
            <method>
                <ejb-name>EmployeeServiceAdmin</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>
        <method-permission>
            <description> 허가된 사용자라면 누구나 EmployeeServiceHelp 빈의 
            어떠한 메쏘드에도 액세스할 수 있습니다.  </description>
            <unchecked/>
            <method>
                <ejb-name>EmployeeServiceHelp</ejb-name>
                <method-name>*</method-name>
            </method>
        </method-permission>
        <exclude-list>
            <description> 이 배치내에서는 EmployeeFiring 빈의 
                fireTheCTO 메쏘드를 사용할 수 없습니다 </description>
            <method>
                <ejb-name>EmployeeFiring</ejb-name>
                <method-name>fireTheCTO</method-name>
            </method>
        </exclude-list>
    </assembly-descriptor>
</ejb-jar>

8.1.5. 웹 컨텐츠 보안의 제약

웹 어플리케이션에서 보안은 보호되는 컨텐츠를 식별하는 URL 패턴을 사용하여 컨텐츠에 액세스를 허용하는 역할 정의로 구현됩니다. 이러한 정보 셋은 web.xml security-constraint 요소를 사용하여 정의합니다. 보호되어야할 컨텐츠는 하나 이상의 web-resource-collection 요소들을 사용하여 정의해줍니다. 각각의 web-resource-collection 요소에는 선택적인 일련의 url-pattern 요소들과 http-method 요소들을 포함합니다. url-pattern 요소의 값에는 보호되는 컨텐츠에 액세스하기위한 요청에 해당되는 URL이 어떤것인지를 알려주는 URL 패턴을 지정합니다. http-method 요소의 값에는 허용되는 HTTP 요청의 타입을 지정합니다.

옵션인 user-data-constraint 요소에는 클라이언트가 서버 연결에 사용하는 전송 계층의 요구사항을 지정합니다. 요구사항에는 컨텐츠 무결성(integrity- 커뮤니케이션 처리과정에서 데이터 오염 방지) 혹은 기밀성(전송중에 볼수없게 방지)이 있습니다. transport-guarantee 요소의 값에는 클라이언트와 서버사이에서 보호되어야하는 커뮤니케이션의 정도를 지정하게 됩니다. 이 값에는 NONE, INTEGRAL, 혹은 CONFIDENTIAL이 올 수 있습니다. NONE 은 어플리케이션에서 어떠한 전송에 대한 보장도 요구되지 않는다는 것을 의미합니다. INTEGRAL 은 어플리케이션에서 클라이언트와 서버사이에 전송되는 데이터가 변경되지 않도록 요구되는 것을 의미합니다. CONFIDENTIAL 은 어플리케이션에서 데이터가 전송되어질때 다른 곳에서 전송되는 컨텐츠를 볼 수 없도록 방지할 필요가 있다는 것을 의미합니다. 대부분의 경우 INTEGRAL 이나 CONFIDENTIAL 플래그는 SSL의 사용이 필요하다는 것을 가르키게 됩니다.

옵션으로 사용되는 login-config 요소는 사용되어야만 하는 인증 메쏘드, 이 어플리케이션에서 사용되어질 realm 이름 그리고 폼 로그인 메커니즘에서 필요로 하는 속성들을 설정하는데 사용됩니다. auth-method 자식 요소에는 웹 어플리케이션을 위한 인증 메커니즘을 지정합니다. 인증 제약에 의해 보호되는 웹 리소스들에 액세스할 수 있는 허가를 취득하기위한 사전 조건(prerequisite)으로써 사용자는 설정된 메커니즘을 사용하여 인증되어야만 합니다. auth-method에 올수있는 값으로는 BASIC, DIGEST, FORM, 또는 CLIENT-CERT가 있습니다. realm-name 자식 요소에는 HTTP basic 및 digest 인증에서 사용되는 영역(realm)의 이름을 지정합니다. form-login-config 자식 요소에는 로그인뿐만 아니라 에러 페이지처럼 form-based 로그인에서 사용될 페이지를 지정합니다. auth-method의 값이 FORM이 아닌경우에는 form-login-config과 그 자식 요소들은 무시됩니다.

일례로써, 예제 8.7, “ security-constraint와 이에 관련된 요소들의 사용법을 보여주기위한 web.xml 서술자의 일부분.” 에서 보여주고 있는 web.xml 서술자의 일부분에서는 웹 어플리케이션의 /restricted 경로밑에 위치된 어떠한 URL들도 액세스하기 위해서는 AuthorizedUser 역할을 필요로 하도록 지시하고 있습니다. BASIC HTTP 인증에서는 사용자의 식별을 위해 사용되는 인증 메쏘드와 전송에 따른 보증을 필요로 하지 않습니다.

예제 8.7.  security-constraint와 이에 관련된 요소들의 사용법을 보여주기위한 web.xml 서술자의 일부분.

<web-app>
    <!-- ... -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Secure Content</web-resource-name>
            <url-pattern>/restricted/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>AuthorizedUser</role-name>
        </auth-constraint>
        <user-data-constraint>
            <transport-guarantee>NONE</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <!-- ... -->
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>The Restricted Zone</realm-name>
    </login-config>
    <!-- ... -->
    <security-role>
        <description>The role required to access restricted content </description>
        <role-name>AuthorizedUser</role-name>
    </security-role>
</web-app>

8.1.6. JBoss에서 선언적인 보안 활성화시키기

J2EE의 보안 요소들에서 다루고 있는 것들은 단지 어플리케이션 측면에서의 보안 요구사항에 국한되어 있습니다. J2EE 보안 요소들에서 논리적인 역할들을 선언하기때문에, 어플리케이션 배치자는 배치 환경쪽에서 어플리케이션의 도메인을 역할에 매핑시킵니다. J2EE 사양에서는 이러한 어플리케이션-서버에 특정한 세부사항들은 생략되어 있습니다. JBoss에서는 어플리케이션 역할을 배치 환경에 매핑시키는데 JBoss 서버에 특정한 배치 서술자를 사용하여 J2EE 보안 모델을 구현하고 있는 보안 관리자를 지정해주는 과정이 수반됩니다. 여기서는 이러한 과정의 세부적인 사항들을 논의하지는 않겠습니다. 보안 설정에 숨어있는 상세한 내용들은 8.3 절, “JBoss 보안 모델”에서 일반적인 JBoss 서버의 보안 인터페이스를 설명할때 함께 다루도록 하겠습니다.

8.2. JAAS의 소개

JBossSX 프레임워크의 기본적인 구현은 JAAS API에 기반을 두고 있습니다. JBossSX의 세부적인 구현에 대해 이해를 하기위해서는 JAAS API의 기본 요소들에 대한 이해가 중요합니다. 이번 섹션에서는 여러분과 함께 JBossSX 아키텍쳐를 논의하기위해 준비해야할 사항들을 소개하도록 하겠습니다. JAAS 패지키의 추가적인 세부사항들은 JAAS 홈페이지에서 찾을 수 있습니다:
http://java.sun.com/products/jaas/.

8.2.1. JAAS란 무엇인가?

JAAS 1.0 API는 사용자의 인증과 인가를 위해 설계된 자바 패키지의 세트로 구성되어 있습니다. 여기에서는 표준 플러그인이 가능한 인증 모듈(PAM-Pluggable Authenticatio Module) 프레임워크와 호환서있는 확장된 자바 2 플랫폼의 액세스 컨트롤 아키텍쳐의 자바 버전을 구현하여 사용자-기반의 인증이 지원되도록 하였습니다. JAAS는 처음에는 JDK1.3용 확장 패키지로 출발하여 JDK1.4 이상에서는 번들링되어있습니다. JBossSX 프레임워크에서는 선언적인 역할기반의 J2EE 보안 모델을 구현하기위해 JAAS의 인증 기능만을 사용하기 때문에, 이 글에서도 이러한 토픽에 한정하여 논의하도록 하겠습니다.

이번 섹션에서 다루어지고 있는 내용의 대부분은 JAAS 1.0 개발자 가이드로부터 나온것이기 때문에 이 부분에 대한 내용에 익숙하신 분들은 8.4 절, “JBoss 보안 확장 아키텍쳐”에서 논의되는 JBossSX 아키텍쳐로 넘어가셔도 됩니다.

JAAS 인증은 플러그인 방식으로 수행됩니다. 이를 통해 자바 어플리케이션이 적용되어 있는 인증 기술로부터 독립적이될 수 있으며 JBossSX 보안 관리자가 상이한 방식의 보안 인프라구조내에서 동작할 수 있도록 합니다. 보안 인프라구조의 통합은 JBossSX 보안 관리자 구현의 변경없이 가능합니다. 변경해줘야 하는 것은 JAAS가 사용하는 인증 스택의 설정뿐입니다.

8.2.1.1. JAAS 핵심 클래스

JAAS의 핵심 클래스는 3개의 카테고리로 나눌 수 있습니다: common, 인증(authentication), 그리고 위임(authorization). 다음에 주어진 항목은 이번 장에서 다루어지고 있는 JBossSX의 기능 구현에 사용된 특정한 클래스들인 common과 인증 클래스입니다.

Common classes:

  • Subject (javax.security.auth.Subject)
  • Principal (java.security.Principal)

Authentication classes:

  • Callback (javax.security.auth.callback.Callback)
  • CallbackHandler (javax.security.auth.callback.CallbackHandler)
  • Configuration (javax.security.auth.login.Configuration)
  • LoginContext (javax.security.auth.login.LoginContext)
  • LoginModule (javax.security.auth.spi.LoginModule)
8.2.1.1.1. Subject 및 Principal

리소스에 접근할 수 있는 권한을 취득하려면, 어플리케이션에서는 먼저 요청되는 소스에 대한 인증을 필요로 합니다. JAAS 프레임워크에서는 요청 소스를 표현하기 위해 subject라는 용어를 정의하고 있습니다. Subject 클래스는 JAAS에서 중심적인 클래스입니다. Subject는 개인 똔느 서비스와 같은 단일 엔티티를 위한 정보를 표현합니다. 이것은 엔티티의 principal, public credential(자격 증명서) 및 private credentilal을 감싸고 있습니다. JAAS API들은 본질적으로는 그저 typed name인 principal을 표현하기 위한 기존의 java.security.Principal 인터페이스를 사용합니다.

인증과정내에서 하나의 subject는 관련된 identity들과 함께 생성됩니다. 하나의 subject는 다수의 principal들을 갖을 수 있습니다. 가령, 한 사람이 [홍길동]이라는 이름 principal 과 [840315-123456]이라는 주민번호 principal 및 [hong]이라는 사용자이름 principal을 갖는다면, 이 모든것은 다른 subject들과 구분지을때 유용하게 됩니다. subject와 관련된 principal들을 가져오기 위해서는 두 개의 메쏘드를 사용할 수 있습니다:

public Set getPrincipals() {...}
public Set getPrincipals(Class c) {...} 

첫 번째 메소드는 subject내의 모든 principal들을 리턴시킵니다. 두 번째 메쏘드는 Class c의 인스턴스 혹은 자신의 서브클래스중의 하나에 관련된 principal들만을 리턴시킵니다. subject가 일치하는 principal이 없는 경우에는 빈 셋(empty set)이 리턴됩니다. java.security.acl.Group 인터페이스는 java.security.Principal의 서브인터페이스이고 따라서 principal 셋 내에서 한 개의 인스턴스가 다른 principal들의 논리적인 그룹이나 principal의 그룹으로 표현될 수 있다는 것에 주의하십시오.

8.2.1.1.2. Subject의 인증(Authentication)

Subject의 인증에서는 JAAS 로그인을 필요로 합니다. 로그인 절차는 다음 과정들로 구성됩니다:

  • 어플리케이션이 로그인 환경설정의 이름내에 넘겨지는 LoginContextLoginModules 설정에서 필요로하는 Callback 객체를 생성시키기위한 CallbackHandler를 초기화합니다.
  • LoginContext는 이름을 갖는 로그인 설정내에 포함된 모든 LoginModules들을 로딩하기 위해 Configuration을 참고합니다. 해당되는 이름을 갖는 Configuration이 없으면, 다른 Configuration을 기본값으로 사용하게 됩니다.
  • 어플리케이션은 LoginContext.login 메쏘드를 호출합니다.
  • login 메쏘드는 로드된 모든 LoginModule들을 호출합니다. 각각의 LoginModule들이 subject를 인증하려하기 때문에, 인증 프로세스를 위해 필요한 정보를 얻기위한 관련 CallbackHandler 에 대한 handle 메쏘드를 호출합니다. 필요한 정보들은 Callback 객체들의 배열 형태로 handle 메쏘드에 넘겨집니다. 위의 과정이 성공하면, LoginModule은 subject와 함께 관련된 principal들과 credential들을 결합합니다.
  • LoginContext는 어플리케이션에 인증 상태를 반환합니다. Success는 login 메쏘드로부터 반환되는 것을 의미합니다. Failure는 login 메쏘드에서 발생되어진 LoginException을 의미합니다.
  • 인증이 성공하게되면, 어플리케이션은 LoginContext.getSubject 메쏘드를 사용하여 인증된 subject를 가져옵니다.
  • subjcet 인증과정이 완료된 후, login 메쏘드에의해 subject와 함께 모든 principal들과 관련된 정보들은 LoginContext.logout 메쏘드 호출에 의해 제거됩니다.

LoginContext 클래스는 subject 인증에 대한 기본 메쏘드를 제공하며 적용된 인증 기술에 어플리케이션 개발을 비종속적이게 하는 방법을 제공합니다. LoginContext는 특정한 어플리케이션에 대해 설정된 인증 서비스를 결정하기 위해 Configuration을 참고합니다. LoginModule 클래스들은 인증 서비스를 의미합니다. 따라서, 여러분은 어플리케이션 자체를 변경하지 않고도 어플리케이션에 서로 다른 로그인 모듈들을 플러그인시킬 수 있습니다. 예제 8.8, “어플리케이션 측면에서 바라본 인증 프로세스 과정의 도해(illustration).”에서는 어플리케이션에서 subject를 인증하기위해 필요한 과정의 실례를 보여주는 코드의 부분을 제공하고 있습니다.

예제 8.8. 어플리케이션 측면에서 바라본 인증 프로세스 과정의 도해(illustration).

CallbackHandler handler = new MyHandler();
LoginContext lc = new LoginContext("some-config", handler);

try {
    lc.login();
    Subject subject = lc.getSubject();
} catch(LoginException e) {
    System.out.println("authentication failed");
    e.printStackTrace();
}
                        
// Perform work as authenticated Subject
// ...

// Scope of work complete, logout to remove authentication info
try {
    lc.logout();
} catch(LoginException e) {
    System.out.println("logout failed");
    e.printStackTrace();
}
                        
// A sample MyHandler class
class MyHandler 
    implements CallbackHandler
{
    public void handle(Callback[] callbacks) throws
        IOException, UnsupportedCallbackException
    {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
                NameCallback nc = (NameCallback)callbacks[i];
                nc.setName(username);
            } else if (callbacks[i] instanceof PasswordCallback) {
                PasswordCallback pc = (PasswordCallback)callbacks[i];
                pc.setPassword(password);
            } else {
                throw new UnsupportedCallbackException(callbacks[i],
                                                       "Unrecognized Callback");
            }
        }
    }
}

개발자들은 LoginModule 인터페이스의 구현을 만들어줌으로써 인증 기술을 통합시킵니다. 이러한 방법을 통해 관리자는 어플리케이션에 다른 인증 기술을 플러그인방식으로 적용시키는 것이 가능하게 됩니다. 여러 LoginModule들은 인증 과정의 일부로써 하나 이상의 인증 기술에 대해 함께 변경시킬 수도 있습니다. 가령, 한 개의 LoginModule에서는 username/password 기반의 인증을 수행하는 반면, 다른 LoginModule에서는 스마트카드 리더나 생체 인증과 같은 하드웨어 장치를 통한 인터페이스를 사용할 수도 있습니다. LoginModule의 생명주기는 클라이언트가 생성하고 만들게 되는 login 메쏘드에 대한 LoginContext 객체에 의해 주도됩니다. 이러한 과정은 2개의 상태로 구성됩니다. 프로세스 단계는 다음과 같습니다:

  • LoginContext가 자신의 public no-arg 컨스트럭터(constructor)를 사용하여 설정된 LoginModule을 생성합니다.
  • 각각의 LoginModule들은 자신의 initialize 메쏘드를 호출하여 초기화되어집니다. Subject 인수는 널값이 아니도록 보증됩니다. initialize 메쏘드의 사용법은 다음과 같습니다: public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options).
  • 그런 다음 login 메쏘드가 호출되어 인증 프로세스가 시작됩니다. example 메쏘드 구현으로 사용자는 username과 password를 요청받게되고, 입력한 정보는 NIS나 LDAP와 같은 네이밍 서비스내에 저장된 데이터와 검증되어집니다. 이와 대체되는 구현으로는 스마트카드와 생체 장치 또는 사용하는 운영체제로부터 사용자 정보를 간단히 추출하는 인터페이스를 들 수 있습니다. 각각의 LoginModule에 의한 사용자의 식별 검증은 JAAS 인증의 1 단계로 보실 수 있습니다. login 메쏘드의 사용법은 boolean login() throws LoginException 입니다. Failure는 LoginException이 발생되는 것을 알려줍니다. true가 반환되면 메쏘드가 성공했다는 것을 알려주는 반면 false가 리턴되면 login 모듈이 무시되어야만 한다는 것을 알려줍니다.
  • LoginContext의 인증이 성공했다면, 각각의 LoginModule에 대한 commit이 호출됩니다. LoginModule에 대한 1 단계가 성공되면, 제 2 단계에서 commit 메쏘드가 계속됩니다: 관련 principals, public credentials 및/또는 private credentials을 subject와 결합시킵니다. LoginModule에 대한 1 단계가 실패하면, username 또는 password와 같은 이전에 저장된 인증 상태들은 commit 메쏘드를 통해 제거됩니다. commit 메쏘드의 사용법은 boolean commit() throws LoginException 입니다. commit 단계까지 종료되면서 Failure가 리턴되면, LoginException이 발생되었다는 것을 알려주는 의미를 갖습니다. true가 리턴되면 메쏘드는 성공한 것이고, 이와는 반대로 false가 리턴되면 login 모듈은 무시되어야만 한다는 것을 의미합니다.
  • LoginContext의 인증 작업이 실패하게되면, 각각의 LoginModule에 대해 abort 메쏘드가 호출됩니다. abort 메쏘드는 login 이나 initialize 메쏘드에 의해 생성된 인증 상태들을 제거/파기시킵니다. abort 메쏘드의 사용법은 boolean abort() throws LoginException 입니다. abort 단계에서의 Failure는 LoginException이 발생되었음을 나타내는 것입니다. true가 반환되면 메쏘드는 성공한 것이고, 반면 false가 리턴되면 login 모듈은 무시되어져야만 한다는 것을 가르킵니다.
  • 로그인이 성공한 이후 인증 상태를 제거하는 것은 어플리케이션에서 LoginContext에 대해 logout 메쏘드를 호출함으로써 구현됩니다. 이것은 각각의 LoginModule에 대해 순차적으로 logout 메쏘드를 호출하게 됩니다. logout 메쏘드는 commit 오퍼레이션동안 subject와 관계된 원래의 principal들과 credentials를 제거합니다. Credential은 removal 메쏘드상에서 파기되어야만 합니다. logout 메쏘드의 사용법은 boolean logout() throws LoginException 입니다. logout 프로세스의 결과가 Failure라면 LoginException이 발생되었다는 것을 의미합니다. true가 리턴되면 메쏘드의 성공을, false가 반환되면 login 모듈이 무시되어야만 한다는 것을 의미합니다.

LoginModule에서 사용자로부터 인증 정보를 가져오도록 커뮤니케이션을 해야만 할 경우에는 CallbackHandler 객체를 사용합니다. 어플리케이션은 CallbackHandler 인터페이스를 구현하여 적용된 login 모듈에 직접 전달하는 LoginContext에 인증정보를 넘겨줍니다. Login 모듈은 password나 스마트카드의 PIN 번호와 같은 사용자 정보와 상태 정보와 같은 사용자에게 제공되는 정보를 취합하는데 CallbackHandler를 사용합니다. 어플리케이션에서 CallbackHandler를 지정할 수 있도록 허용해줌으로써, 적용시킨 LoginModule에는 어플리케이션이 사용자들과 상호 작용하는 서로 다른 방법들에 비종속이 될 수 있는 여지가 존재합니다. 예를 들면, GUI 어플리케이션에 대한 CallbackHandler의 구현은 사용자 입력을 권유하는 윈도우를 보여주게 될 것입니다. 이와는 다르게 어플리케이션 서버와 같은 비-GUI 환경으로 작동되는 어플리케이션에서의 callbackhandler 구현은 어플리케이션 서버의 API를 사용하여 credential 정보를 간단하게 얻게 될 것입니다. callbackhandler 인터페이스는 구현을 위한 한 개의 메쏘드를 갖습니다:

void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException;

마지막으로 다루게 될 인증 클래스는 Callback 인터페이스입니다. 이것은 예제 8.8, “어플리케이션 측면에서 바라본 인증 프로세스 과정의 도해”에서 사용되었던 NameCallbackPasswordCallback 을 포함하는 몇몇 기본 구현을 위해 제공되는 tagging 인터페이스입니다. LoginModule은 인증 메커니즘에서 필요로하는 정보를 요청하기 위해 Callback을 사용합니다. LoginModule은 캡슐로 감싸여집니다(encapsulates). LoginModule은 인증 과정의 login 단계(phase)과정중에 CallbackHandler.handle 메쏘드쪽으로 직접 Callback의 배열을 넘겨줍니다. callbackhandler가 handle 메쏘드쪽으로 넘겨진 Callback 객체를 사용하는 방법을 모를경우, login 메쏘드 호출을 취소하기 위해 UnsupportedCallbackException을 발생시킵니다.

8.3. JBoss의 보안 모델

다른 JBoss 아키텍쳐들과 유사하게, 최저 수준(lowest level)에서의 보안은 대체 구현을 제공하기위한 일련의 인터페이스들로써 정의됩니다. JBoss 서버의 보안 레이어를 정의하는데는 다음 3개의 기본 인터페이스가 있습니다:
org.jboss.security.AuthenticationManager, org.jboss.security.RealmMapping, 그리고 org.jboss.security.SecurityProxy.
그림 8.3, “보안 모델의 핵심 인터페이스들과 JBoss 서버 EJB 컨테이너 요소들과의 관계.”에서는 보안 인터페이스의 클래스 다이어그램과 이들과 관련된 EJB 컨테이너의 아키텍쳐를 보여주고 있습니다.

보안 모델의 핵심 인터페이스들과 JBoss 서버 EJB 컨테이너 요소들과의 관계

그림 8.3. 보안 모델의 핵심 인터페이스들과 JBoss 서버 EJB 컨테이너 요소들과의 관계.

밝은 청색으로 표시된 클래스들은 보안 인터페이스를 나타내며, 노란색으로 표시된 클래스들은 EJB 컨테이너 계층을 나타냅니다. J2EE 보안 모델의 구현을 위해 필요한 2개의 인터페이스들은 바로 org.jboss.security.AuthenticationManagerorg.jboss.security.RealmMapping 입니다. 그림 8.3, “보안 모델의 핵심 인터페이스들과 JBoss 서버 EJB 컨테이너 요소들과의 관계”에서의 보안 인터페이스들의 역할을 아래쪽에 정리했습니다.

  • AuthenticationManager는 principal과 관련된 credential을 검증해야하는 인터페이스입니다. Principal에 해당되는 항목의 예제로는 사용자이름, 직원번호, 주민등록번호등이 있습니다. Credential에 해당되는 항목의 예제로는 암호, 세션 키, 디지털 서명등이 있습니다. 운영체제에서 알고있는 사용자의 신분(identity)과 이에 관련된 자격증명(credential)이 사용자의 신분을 증명하는데 올바른가를 알아보기위해 isValid 메쏘드가 호출됩니다.
  • RealmMapping 은 principal 매핑과 role 매핑을 담당하는 인터페이스입니다. getPrincipal 메쏘드는 운영체제에서 알고있는 사용자 신분을 가져와 어플리케이션 도메인 신분(identity)을 리턴합니다. doesUserHaveRole 메쏘드는 운영체제내의 사용자 신분이 어플리케이션 도메인으로부터 지정된 역할(role)로 할당되어졌는지를 검증합니다.
  • SecurityProxy는 커스텀 SecurityProxyInterceptor 플러그인의 요구사항들을 기술해주는 인터페이스입니다. SecurityProxy는 EJB home과 remote 인터페이스 메쏘드 모두에 대해 메쏘드별(per-method basis)로 커스텀 보안의 외부화(externalization)를 점검할 수 있도록 합니다.
  • SubjectSecurityManager는 보안 관리자의 보안 도메인 이름과 현재 쓰레드의 인증된 Subject를 얻기위한 accessor 메쏘드를 간단히 추가해주는 AuthenticationManager 의 서브인터페이스입니다.
  • SecurityDomainAuthenticationManager, RealmMapping, 및 SubjectSecurityManager 인터페이스의 익스텐션입니다. java.security.KeyStore, and the JSSE com.sun.net.ssl.KeyManagerFactory 그리고 com.sun.net.ssl.TrustManagerFactory 인터페이스와 같은 JAAS Subject에 기반한 포괄적인 보안 인터페이스로의 이동입니다. 이 인터페이스는 어플리케이션과 리소스의 ASP 스타일을 지원하기에 보다 적합하도록 고안된 멀티-도메인 보안 아키텍쳐의 기반이 되는 작업으로써 아직까지도 진행되고 있습니다.

AuthenticationManager, RealmMapping 그리고 SecurityProxy 인터페이스들이 JAAS와 관련된 클래스들과는 어떠한 관련성도 없다는 것을 기억해주십시오. 비록 JBossSX 프레임워크가 JAAS에 밀접한 의존성을 갖기는 하지만, J2EE 보안 모델의 구현에 필요한 기본 보안 인터페이스들은 그렇치 않습니다. JBossSX 프레임워크는 JAAS에 기반한 기본적인 보안 플러그인 인터페이스들을 간단하게 구현한 것입니다. 그림 8.4, “JBossSX 프레임워크 구현 클래스와 JBoss 서버 EJB 컨테이너 레이어간의 관계” 컴포넌트 다이어그램에서는 이러한 사실을 보여주고 있습니다. 플러그인 아키텍쳐가 암시하고 있는것은 바로 여러분이 필요에 따라서는 JAAS를 사용하지 않는 직접 만든 커스텀 보안 관리자 구현으로 JAAS 기반의 JBossSX 구현 클래스를 대체하는 것이 자유롭다는 것입니다. 그림 8.4, “JBossSX 프레임워크 구현 클래스와 JBoss 서버 EJB 컨테이너 레이어간의 관계”에서 JBossSX 설정을 위해 사용가능한 JBossSX MBean을 살펴본다면 어떻게 대체시킬 수 있는지 알 수 있을 것입니다.

JBossSX 프레임워크 구현 클래스와 JBoss 서버 EJB 컨테이너 레이어간의 관계.

그림 8.4. JBossSX 프레임워크 구현 클래스와 JBoss 서버 EJB 컨테이너 레이어간의 관계.

8.3.1. JBoss에서 선언적 보안을 가능하게 하기의 복습(Revisited)

앞에서 J2EE 표준 보안 모델에 대해 논의하면서 JBoss 서버에 특정한 배치 서술자를 사용하여 보안을 활성화시키는데 필요한 요구사항들로 이야기가 끝난것을 기억하실 겁니다. 이러한 환경설정의 세부적인 내용을 일반적인 JBoss 보안 모델의 일부분으로써 이곳에서 제시하도록 하겠습니다. 그림 8.5, “JBoss 서버의 jboss.xml 과 jboss-web.xml 배치 서술자의 보안 요소 서브셋” 에서 JBoss에 특정한 EJB와 웹 어플리케이션 배치 서술자의 보안에 관련된 요소들을 볼 수 있습니다.

JBoss 서버의  jboss.xml 과 jboss-web.xml 배치 서술자의 보안 요소 서브셋

그림 8.5. JBoss 서버의 jboss.xml 과 jboss-web.xml 배치 서술자의 보안 요소 서브셋.

security-domain 요소의 값에는 EJB와 웹 컨테이너를 위해 JBoss에서 사용되는 보안 관리자 인터페이스 구현의 JNDI 이름을 지정합니다. 이것은 AuthenticationManagerRealmMapping 인터페이스 모두 적용시킨 객체입니다. 상위-레벨 요소로써 지정되어지면, 배치 유닛내의 모든 EJB들에 대해 적용되는 보안 도메인이 무엇인지를 정의하게 됩니다. 이것이 전형적인 사용방법이며, 그 이유는 배치 유닛내의 혼합된 보안 관리자들이 컴포넌트간(inter-component) 오퍼레이션과 관리의 복잡성때문입니다.

EJB들에 대해 개별적으로 보안 도메인을 지정하기위해서는 컨테이너 설정 수준에서 security-domain을 지정하는 것입니다. 이렇게 해주면 어떤 상위-레벨 보안 도메인 요소도 덮어쓰여지게 됩니다.

unauthenticated-principal 요소에는 인증되지 않은 사용자가 EJB를 호출할때 EJBContext.getUserPrincpal 메쏘드에 의해 반환되는 Principal 객체를 위해 사용하는 이름을 지정합니다. 여기서 인증되지 않은 호출자(caller)를 인가해주는 특별한 것이 따라오지 않는다는 것에 주의하십시오. 이것의 주요 목적은 보호되지 않는 서블릿들과 JSP 페이지들이 보호되지 않는 EJB들을 호출할 수 있도록 허용하는 것과 대상 EJB가 getUserPrincipal 메쏘드를 사용하여 호출자에 대한 널값이 아닌 Principal을 획득하도록 하는 것입니다. 이것은 J2EE 사양서에서 요구되는 것입니다.

security-proxy 요소는 EJB 구현내에 내장시킨 보안 로직없이도 EJB의 선언적 보안 모델 범위 밖에서도 요청당(per-request) 보안 점검을 할 수 있도록 하는 커스텀 보안 프록시 구현을 확인합니다. 이것은 org.jboss.security.SecurityProxy 인터페이스의 구현 혹은 단지 어떠한 범용 인터페이스도 없이 보안을 구현하기위한 EJB의 home, remote, local home 혹은 local interface내의 구현 메쏘드인 객체일 수 있습니다. 주어진 클래스에서 SecurityProxy 인터페이스를 구현하지 않았다면, 인스턴스는 객체쪽으로 메쏘드 호출을 위임하는 SecurityProxy 구현내에 감싸져야만 합니다. org.jboss.security.SubjectSecurityProxy는 기본 JBossSX 설치에서 사용되는 SecurityProxy 구현의 예입니다.

간단한 비상태 세션 빈의 컨텍스트내에서 커스텀 SecurityProxy의 간단한 예제를 살펴보도록 하겠습니다. 커스텀 SecurityProxy에서는 그 인수로써 4자로 구성된 단어를 갖는 빈즈의 echo 메쏘드를 어디서도 호출하지 않았음을 검증합니다. 이것은 역할기반(role-based) 보안으로 점검할 수 없습니다; 보안 컨텍스트가 호출자의 속성이 아니라 메쏘드 인수이기 때문에 여러분은 FourLetterEchoInvoker 역할을 정의할 수 없습니다. 커스텀 SecurityProxy의 코드는 예제 8.9, “echo 인수기반(argument-based) 보안 제약을 강제하는 커스텀 EchoSecurityProxy 구현 예제 1.”이며, 소스 코드의 완전한 버전은 이 책의 예제 디렉터리 src/main/org/jboss/chap8/ex1 에서 찾으실 수 있습니다. EchoBean을 위한 커스텀 프록시인 EchoSecurityProxy를 설치해주는 관련된 jboss.xml 서술자는 예제 8.10, “EchoBean에 대한 커스텀 보안 프록시인 EchoSecurityProxy를 설정해주는 jboss.xml 서술자.” 입니다.

예제 8.9. echo 인수기반(argument-based) 보안 제약을 강제하는 커스텀 EchoSecurityProxy 구현 예제 1.

package org.jboss.chap8.ex1;
                
import java.lang.reflect.Method;
import javax.ejb.EJBContext;
                
import org.apache.log4j.Category;
                
import org.jboss.security.SecurityProxy;
                
/** 메쏘드 인수 기반의 보안 점검을 보여주는 
 * 커스텀 SecurityProxy 구현의 간단한 예제.
 * @author Scott.Stark@jboss.org
 * @version $Revision: 1.15 $
 */
public class EchoSecurityProxy implements SecurityProxy
{
    Category log = Category.getInstance(EchoSecurityProxy.class);
    Method echo;
    
    public void init(Class beanHome, Class beanRemote,
                     Object securityMgr)
        throws InstantiationException
    {
        log.debug("init, beanHome="+beanHome
                  + ", beanRemote="+beanRemote
                  + ", securityMgr="+securityMgr);
        // Get the echo method for equality testing in invoke
        try {
            Class[] params = {String.class};
            echo = beanRemote.getDeclaredMethod("echo", params);
        } catch(Exception e) {
            String msg = "Failed to finde an echo(String) method";
            log.error(msg, e);
            throw new InstantiationException(msg);
        }
    }
    
    public void setEJBContext(EJBContext ctx)
    {
        log.debug("setEJBContext, ctx="+ctx);
    }
    
    public void invokeHome(Method m, Object[] args)
        throws SecurityException
    {
        // We don't validate access to home methods
    }

    public void invoke(Method m, Object[] args, Object bean)
        throws SecurityException
    {
        log.debug("invoke, m="+m);
        // Check for the echo method
        if (m.equals(echo)) {
            // Validate that the msg arg is not 4 letter word
            String arg = (String) args[0];
            if (arg == null || arg.length() == 4)
                throw new SecurityException("No 4 letter words");
        }
        // We are not responsible for doing the invoke
    }
}

예제 8.10. EchoBean에 대한 커스텀 보안 프록시로써 EchoSecurityProxy를 설정해주는 jboss.xml 서술자.

<jboss>
    <security-domain>java:/jaas/other</security-domain>
                
    <enterprise-beans>
        <session>
            <ejb-name>EchoBean</ejb-name>
            <security-proxy>org.jboss.chap8.ex1.EchoSecurityProxy</security-proxy>
        </session>
    </enterprise-beans>
</jboss> 

EchoSecurityProxy는 init 메쏘드를 로딩한 echo(String) 메쏘드에 대응되는 빈 인스턴스상에서 호출되어지는 메쏘드를 점검합니다. 일치하는 것이 있다면, 메쏘드 인수가 얻어지며 그 길이가 4인지 널인지 비교합니다. 일치하는 것이 없다면, SecurityException이 발생됩니다. 여기서 다룬 예제는 조금은 억지스러운 면도 있지만 이 어플리케이션에서는 동작됩니다. 메쏘드의 인수 값에 기반하여 보안 점검을 수행해야만 하는 어플리케이션이 일반적인 요구사항입니다. 예제의 핵심은 표준 선언적 보안 모델의 영역을 넘어서는 커스텀 보안이 빈 구현과는 비종속적으로 구현되는 방법을 보여주는 것입니다. 이것은 요구되는 보안 요구사항들에 대한 스펙과 코딩들을 보안 전문가에게 위임할 수 있게 합니다. 보안 프록시 레이어가 빈 구현과 종속적이지 않기 때문에, 보안은 배치되는 환경에서 요구되는 사항들을 만족하는 것으로 변경시킬 수 있습니다.

이제 다음과 같은 코드(일부분)를 통해 HelloFour 인수를 갖는 EchoBean.echo 메쏘드를 호출하는 클라이언트를 실행시켜 커스텀 프록시를 테스트하도록 하겠습니다:

public class ExClient
{
    public static void main(String args[])
        throws Exception
    {
        Logger log = Logger.getLogger("ExClient");
        log.info("Looking up EchoBean");

        InitialContext iniCtx = new InitialContext();
        Object ref = iniCtx.lookup("EchoBean");
        EchoHome home = (EchoHome) ref;
        Echo echo = home.create();

        log.info("Created Echo");
        log.info("Echo.echo('Hello') = "+echo.echo("Hello"));
        log.info("Echo.echo('Four') = "+echo.echo("Four"));
    }
}  

첫 번째 호출은 성공하겠지만 두 번째는 Four가 4자로 구성된 단어이기 때문에 실패할 것입니다. 예제 디렉터리에서 Ant를 사용하여 다음과 같이 클라이언트를 실행시킵니다:

[nr@toki examples]$ ant -Dchap=chap8 -Dex=1 run-example
run-example1:
     [copy] Copying 1 file to /tmp/jboss-3.2.6/server/default/deploy
     [echo] Waiting for 5 seconds for deploy...
     [java] [INFO,ExClient] Looking up EchoBean
     [java] [INFO,ExClient] Created Echo
     [java] [INFO,ExClient] Echo.echo('Hello') = Hello
     [java] Exception in thread "main" java.rmi.ServerException: RemoteException occurred
in server thread; nested exception is: 
     [java]     java.rmi.AccessException: SecurityException; nested exception is: 
     [java]     java.lang.SecurityException: No 4 letter words
...
     [java]     at org.jboss.chap8.ex1.ExClient.main(ExClient.java:28)
     [java] Caused by: java.rmi.AccessException: SecurityException; nested exception is: 
     [java]     java.lang.SecurityException: No 4 letter words
...

실행 결과를 살펴보면, 우리가 예상했던 대로 echo('Hello') 메쏘드 호출은 성공하고 echo('Four') 메쏘드 호출은 예상했던 대로 조금 복잡한 예외를 발생시킨다는 것을 알 수 있습니다. 위에 제공된 출력형태는 이 책의 편집상 잘라서 보여지는 것입니다. 예외 메시지에서 중요한 부분은 의도된 대로 메쏘드 호출시도를 취소시키도록 EchoSecurityProxy에 의해 발생된 SecurityException("No 4 letter words") 입니다.

8.4. JBoss 보안 확장 아키텍쳐

앞에서 살펴보았던 일반적인 JBoss 보안 레이어에서는 JBossSX 보안 확장 프레임워크가 보안 레이어 인터페이스의 구현이라고 언급했었습니다. 이것은 JBossSX 프레임워크의 주요 목적입니다. 구현의 세부사항들은 기존 보안 인프라구조내에 통합을 목적으로 하는 커스터마이징을 제공하기 때문에 주목을 끌게 됩니다. 보안 인프라구조는 데이터베이스나 LDAP 서버로부터 고급 보안 소프트웨어 패키지에 이르기 까지 어떠한 종류라도 될 수 있습니다. 통합의 유연성은 JAAS 프레임워크내의 플러그인이 가능한 인증 모델을 사용함으로써 가능하게 됩니다.

JBossSX 프레임워크의 핵심은 org.jboss.security.plugins.JaasSecurityManager 입니다. 이것은 AuthenticationManagerRealmMapping 인터페이스의 기본 구현입니다. 그림 8.6, “security-domain 컴포넌트 배치 서술자의 값인 컴포넌트 컨테이너와 JaasSecurityManager 사이의 관계”에서는 대응되는 컴포넌트 배치 서술자의 security-domain 요소에 기반을 둔 EJB와 웹 컨테이너 레이어와 JaasSecurityManager를 어떻게 통합시키는지 보여주고 있습니다.

security-domain 컴포넌트 배치 서술자의 값인 컴포넌트 컨테이너와 JaasSecurityManager 사이의 관계.

그림 8.6. security-domain 컴포넌트 배치 서술자의 값인 컴포넌트 컨테이너와 JaasSecurityManager 사이의 관계.

그림 8.6, “security-domain 컴포넌트 배치 서술자의 값인 컴포넌트 컨테이너와 JaasSecurityManager 사이의 관계”에서는 보안 도메인 jwdomain 쪽의 보호되는 EJB들과 웹 컨텐츠 모두를 포함하는 엔터프라이즈 어플리케이션을 묘사하고 있습니다. EJB와 웹 컨테이너는 컨테이너의 보안 모델을 강요하는 하나의 보안 인터셉터를 포함하는 요청 인터셉터 아키텍쳐를 갖습니다. 배치할때는 jboss.xmljboss-web.xml 서술자내의 security-domain 요소의 값이 컨테이너와 연결된 보안 관리자 인스턴스를 획득하는데 사용됩니다. 그런다음, 보안 인터셉터는 자신의 역할을 수행하기위해 보안 관리자를 사용합니다. 보호되는 컴포넌트가 요청되면, 보안 인터셉터는 보안 점검을 컨테이너와 연결된 보안 관리자 인스턴스쪽으로 위임합니다.

JaasSecurityMgr 컴포넌트로써의 그림 8.6, “security-domain 컴포넌트 배치 서술자의 값인 컴포넌트 컨테이너와 JaasSecurityManager 사이의 관계”에서 보여지는 JBossSX JaasSecurityManager 구현은 security-domain 요소의 값과 일치하는 이름밑에 설정된 JAAS 로그인 모듈을 실행시켜 얻은 결과인 Subject 인스턴스와 관련된 정보를 토대로 보안 점검을 수행합니다. 우리는 JaasSecurityManager 구현과 JAAS에서 이를 사용하는 것을 다음 섹션에서 차례대로 살펴보겠습니다.

8.4.1. JaasSecurityManager가 JAAS를 어떻게 사용하는가

JaasSecurityManager는 JAAS 패지키를 사용하여 AuthenticationManagerRealmMapping 인터페이스 동작을 구현하게 됩니다. 특히, 자신의 동작은 JaasSecurityManager가 할당된 security-domain 과 일치하는 이름아래에 설정된 로그인 모듈 인스턴스의 실행으로부터 파생됩니다. 로긴 모듈은 security-domain의 principal 인증과 역할-매핑 동작을 구현합니다. 따라서 여러분은 도메인들에 대해 서로 다른 로그인 모듈 설정에 대해 간단하게 플러그인을 해줌으로써 서로 다른 보안 도메인에 걸쳐 JaasSecurityManager를 사용할 수 있습니다.

JAAS 인증 프로세스에서 JaasSecurityManager의 사용법에 대한 세부적인 것들을 표현하기 위해, EJB home 메쏘드 호출의 클라이언트 호출을 살펴보도록 하겠습니다. 선행되어 설정될 것은 EJB가 JBoss 서버쪽에 배치되고 자신의 home 인터페이스 메쏘드가 ejb-jar.xml 서술자내에서 method-permission 요소를 사용하여 보호되어져야 하고, jboss.xml 서술자내의 security-domain 요소를 사용하여 jwdomain라고 명명된 보안 도메인을 할당해주는 것입니다.

보호되는 EJB home 메쏘드 호출의 인증과 인가에 포함된 단계들에 대한 그림.

그림 8.7. 보호되는 EJB home 메쏘드 호출의 인증과 인가에 포함된 단계들에 대한 그림.

그림 8.7, “보호되는 EJB home 메쏘드 호출의 인증과 인가에 포함된 단계들에 대한 그림”에서는 우리가 논의하게 될 클라이언트가 서버로 커뮤니케이션하는 뷰가 제공됩니다. 진행과정은 아랫쪽에 번호순서로 나열하였습니다:

  1. 제일 먼저 클라이언트는 인증을 위해 principal과 credential을 입증하기위해 JAAS 로그인을 수행하며, 이것은 그림에서 Client Side Login이라 명명된 부분입니다. 이것이 바로 클라이언트가 JBoss내에 자신들의 로그인 신분을 입증하는 방법이 됩니다. JNDI InitialContext 속성들을 통해 로그인 정보를 표현하기위한 지원은 환경 교체를 통해 제공됩니다. JAAS 로그인에는 LoginContext 인스턴스의 생성과 사용할 설정에 대한 이름을 넘겨주는 과정을 수반합니다. 여기서 설정의 이름은 other 입니다. 이러한 한번의 로그인을 통해 로그인 principal과 credential을 모든 일련의 EJB 메쏘드 호출에 연관시킵니다. 주의해야할 것은 이 프로세스가 사용자를 인가하지 않을 수도 있다는 것입니다. 클라이언트측의 로그인 특성은 클라이언트가 사용하는 로그인 모듈 설정에 의존적입니다. 여기에서는 other라는 클라이언트쪽 로그인 설정 항목이 ClientLoginModule 모듈(an org.jboss.security.ClientLoginModule)을 사용하도록 설정되어 있습니다. 이것은서버쪽에서 이후의 인증을 위해 JBoss EJB 호출 레이어쪽에 단순하게 username과 password를 연결해주는 기본 클라이언트측 모듈입니다. 클라이언트의 신분은 클라이언트쪽에서 인증되지 않았습니다.
  2. 나중에 클라이언트가 EJB home 인터페이스를 획득하고 빈을 생성하려는 시도를 합니다. 이러한 이벤트는 그림에서 Home Method Invocation 이라고 명명되어 있습니다. 결국 JBoss 서버쪽으로 전송되는 home 인터페이스 메쏘드 호출이 발생합니다. 호출에는 과정 1에서 수행한 클라이언트측 JAAS 로그인으로부터 얻은 사용자의 신분및 자격증명(credentials)과 함께 클라이언트에 의해 넘겨진 메쏘드의 인자들을 포함합니다.
  3. 서버쪽에서는 제일 먼저 보안 인터셉터가 JAAS 로그인에 포함된 클라이언트쪽의 사용자 호출에 대한 인증을 필요로 합니다.
  4. EJB가 보호되고 있는 보안 도메인은 로그인 모듈 선택을 결정합니다. 보안 도메인 이름은 LoginContext 컨스트럭쳐에 넘겨지는 로그인 설정 항목의 이름으로 사용됩니다. EJB 보안 도메인은 jwdomain 입니다. JAAS 로그인에서 사용자를 인증하였다면, JAAS Subject가 생성되며, 생성된 JAAS SubjectPrincipalsSet내에 다음과 같은 것을 포함합니다:
    • 배치 보안 환경내에 알려진 클라이언트의 신분에 대응되는 java.security.Principal
    • 사용자에게 할당된 어플리케이션 도메인의 역할 이름을 포함하고 있는 Roles라는 이름의 java.security.acl.Group. org.jboss.security.SimplePrincipal 객체는 역할 이름을 표시하는데 사용됩니다; SimplePrincipal은 간단한 문자열 기반으로 Principal을 구현한 것입니다. 이러한 역할(roles)은 ejb-jar.xmlEJBContext.isCallerInRole(String) 메쏘드 구현내에서 메쏘드에 할당된 역할을 검증하는데 사용됩니다.
    • 어플리케이션 도메인의 호출자의 신분(identity)과 일치하는 단일 org.jboss.security.SimplePrincipal을 포함하고 있는 CallerPrincipal이라는 이름의 선택사용되는 java.security.acl.Group. CallerPrincipal sole 그룹 구성원은 EJBContext.getCallerPrincipal() 메쏘드에 의해 리턴되는 값이 됩니다. 이 맵핑의 목적은 활동중인 보안 환경내에 알려진 Principal을 어플리케이션에서 알고있는 이름을 갖고 있는 Principal에 매핑시킬 수 있도록 합니다. CallerPrincipal 매핑이 없는 경우, 배치 보안 환경의 principal은 getCallerPrincipal 메쏘드의 값을 사용하게 됩니다. 이것은 활동중인(operational) principal 이 어플리케이션 도메인의 principal과 똑같다는 것입니다.
  5. 보안 인터셉터 점검의 마지막 단계는 인증된 사용자가 요청된 메쏘드를 호출할 수 있는 허가권을 갖는지 검증하는 것입니다. 이것은 그림 8.7, “보호되는 EJB home 메쏘드 호출의 인증과 인가에 포함된 단계들에 대한 그림”에서 Server Side Authorization으로 표시되어 있습니다. 인증을 수행하기 위해 이것은 다음과 같은 과정을 수반합니다:
    • EJB 컨테이너로부터 EJB 메쏘드를 액세스할 수 있도록 허용된 역할의 이름을 획득합니다. 역할의 이름은 호출된 메쏘드를 포함하고 있는 모든 method-permission 요소들의 ejb-jar.xml 서술자내에 있는 role-name 요소에 의해 결정되어집니다.
    • 어떠한 역할도 할당되어있지 않았다거나 exclude-list 요소내에 메쏘드가 지정되어 있다면, 메쏘드로의 액세스가 거부됩니다. 그렇지않다면 호출자가 할당된 역할 이름들중에 하나를 갖는지 확인하기위해 보안 인터셉터에 의해 보안 관리자에서 doesUserHaveRole 메쏘드가 호출됩니다. 이 메쏘드는 역할 이름에 대해 반복적으로 수행되고 인증된 사용자의 Subject Roles 그룹이 할당된 역할의 이름과 함께 SimplePrincipal을 포함하는지를 점검합니다. 역할 이름(role name)이 Roles 그룹의 멤버라면 액세스가 허용됩니다. 역할의 이름이 멤버가 아닌 경우에는 액세스는 거부됩니다.
    • EJB가 커스텀 보안 프록시를 이용해서 설정되어진 경우, 메쏘드 호출은 이쪽으로 위임됩니다. 만일 보안 프록시에서 호출자의 액세스를 거부하도록 하고자 한다면, java.lang.SecurityException을 발생시킵니다. SecurityException이 발생되지 않는다면, EJB 메쏘드의 액세스가 허용되며 메쏘드 호출은 다음 컨테이너의 인터셉터쪽으로 넘겨집니다. 여기서 주의해야 할 사항은 SecurityProxyInterceptor가 이러한 점검을 처리하고 인터셉터 자신은 보여지지 않는다는 것입니다.

모든 보호되는 EJB 메쏘드 호출 또는 보호되는 웹 컨텐츠 액세스는 보안 정보가 각각의 요청에 대해 표시되어지고 검증되어져야만 하는 요청의 비상태 속성으로 처리되기 때문에 호출자의 인증과 인가를 필요로 합니다. 이것은 JAAS 로그인이 클라이언트와 서버사이의 통신일 경우 많은 오퍼레이션 비용을 발생시킬 수도 있습니다. 이러한 이유때문에, JaasSecurityManager는 이전에 성공한 로그인으로부터 principal과 credential 정보를 저정할 수 있는 인증 캐쉬의 사용을 지원합니다. 여러분은 다음 절에서 논의되는 관련된 MBean 서버스의 JaasSecurityManager 환경설정의 일부에서 인증 캐쉬 인스턴스를 사용하도록 지정할 수 있습니다. 사용자가 정의한 캐쉬가 없을 경우, 설정된 기간동안 credential 정보를 유지하는 기본 캐쉬가 사용됩니다.

8.4.2. JaasSecurityManagerService MBean

JaasSecurityManagerService MBean 서비스는 보안 관리자들을 관리합니다. 이것의 이름이 Jaas로 시작하지만, 보안 관리자들은 그들의 구현내에서 JAAS를 사용해 처리할 필요는 없습니다. 이름에서 알수있는 것은 기본 보안 관리자 구현이 JaasSecurityManager라는 것입니다. JaasSecurityManagerService의 주요 역할은 보안 관리자 구현을 노출시켜주는 것입니다. 여러분은 AuthenticationManagerRealmMapping 인터페이스 대체 구현을 제공함으로써 보안 관리자 구현을 변경시킬 수 있습니다. 물론 이것은 기본적으로 JaasSecurityManager 구현이 사용되기 때문에 옵션입니다.

JaasSecurityManagerService의 두 번째 중요한 역할은 보안 관리자 구현 매핑을 위한 JNDI 이름에 대해 간단히 자유로운 코드 관리가 가능하도록 하는 JNDI javax.naming.spi.ObjectFactory 구현을 제공하는것입니다. 이것은 이미 security-domain 배치 서술자의 요소를 통해 보안 관리자 구현에 대한 JNDI 이름을 지정해줌으로써 활성화시키는 보안에서 언급했던 내용입니다. 여러분이 JNDI 이름을 지정하게 되면, 사용하기 위해 object-binding을 갖게 되는 것입니다. 보안 관리자 바인등을 위한 JNDI 이름 설정을 간단히 하기위해, JaasSecurityManagerServicejava:/jaas 이름 아래쪽에 JNDI ObjectFactory로써 자신과 함께 다음번 네이밍 시스템 레퍼런스에 바인딩함으로써 보안 관리자 인스턴스들에 대한 관련성을 이름으로 관리합니다. 이렇게 함으로써 security-domain 요소 값과 XYZ 보안 도메인에 대한 보안 관리자 인스턴스로서 java:/jaas/XYZ 형태의 네이밍 표기법을 사용하여 여러분이 필요로하는 것을 만들어내게 됩니다. XYZ 도메인에 대한 보안 관리자는 보안 도메인의 이름을 인수로 갖는 컨스트럭쳐를 사용하여 SecurityManagerClassName 속성에 의해 지정되는 클래스의 인스턴스를 생성시킴으로써 바인딩되는 java:/jaas/XYZ에 대한 첫 번째 룩업을 통해 생성됩니다. 즉, 다음과 같은 컨테이너 보안 설정의 일부분을 예로 들수 있습니다:

<jboss>
    <!-- 보안 도메인 "hades" 밑의 모든 컨테이너들이 보호되도록 설정 -->
    <security-domain>java:/jaas/hades</security-domain>
    <!-- ... -->
</jboss> 

java:/jaas/hades 이름을 룩업하는 것은 hades 라는 이름을 갖는 보안 도메인과 관계된 보안 관리자 인스턴스를 반환하게 됩니다. 이 보안 관리자는 AuthenticationManagerRealmMapping 보안 인터페이스를 구현하며, JaasSecurityManagerService SecurityManagerClassName 속성에 의해 지정된 타입이됩니다.

JaasSecurityManagerService MBean은 표준 JBoss 배포판에서 사용하기 위해 default에 설정되어 있으므로, 여러분은 default 설정만으로도 사용이 가능합니다. JaasSecurityManagerService의 설정가능한 속성들은 다음과 같습니다:

  • SecurityManagerClassName: 보안 관리자 구현을 제공하는 클래스의 이름입니다. 구현에서는 org.jboss.security.AuthenticationManagerorg.jboss.security.RealmMapping 두 인터페이스 모두를 지원해야만 합니다. 지정하지 않으면, JAAS 기반의 org.jboss.security.plugins.JaasSecurityManager를 기본값으로 설정합니다.
  • CallbackHandlerClassName: JaasSecurityManager에서 사용하는 javax.security.auth.callback.CallbackHandler 구현을 제공하는 클래스의 이름입니다. 기본 구현(org.jboss.security.auth.callback.SecurityAssociationHandler)이 요구에 부합되지 않을 경우, JaasSecurityManager 를 사용하여 핸들러를 바꿀 수 있습니다. 이것은 일반적으로는 설정하지 않으며, 여기에 대해 깊은 지식을 갖는 경우에만 사용하게 됩니다.
  • SecurityProxyFactoryClassName: org.jboss.security.SecurityProxyFactory 구현을 제공하는 클래스의 이름입니다. 지정되지 않으면, org.jboss.security.SubjectSecurityProxyFactory가 기본으로 설정됩니다.
  • AuthenticationCacheJndiName: 보안 credential 캐쉬 정책의 위치를 지정합니다. 보안 도메인 각각에 대해 반환되는 CachePolicy 인스턴스들의 저장이 가능한 ObjectFactory 위치로 처음에는 다루어집니다. 도메인에 대한 CachePolicy를 찾아볼 때 보안 도메인을 이 이름에 덧 붙입니다. 이러한 작업이 실패하게되면, 위치(location)는 모든 보안 도메인들에 대해 단일 CachePolicy로 다루어지게 됩니다. 기본적으로는 제한된 시간동안만의(timed) 캐쉬 정책이 사용됩니다.
  • DefaultCacheTimeout: 제한된 시간동안만의 캐쉬 정책에서의 타임아웃을 초단위로 지정합니다. 기본값은 1800 초(30분)입니다. 타임아웃에 사용할 값은 얼마나 자주 인증 오퍼레이션이 이루어지는가와 credential 정보가 저장된 보안 정보에 대해 동기화되기 까지 지속되는 시간 사이에서 절충합니다. 만일 보안 credentials의 캐쉬를 사용하지 않도록 하려면, 이 값을 0 으로 설정하여 매번 강제적으로 인증이 발생되도록 합니다. 기본값으로부터 이것은 AuthenticationCacheJndiName이 변경되어졌다면 어떠한 효력도 미치지 않게됩니다.
  • DefaultCacheResolution: 기본 캐쉬 정책 시간제한의 최소 식별시간을 초단위로 지정합니다. 이를 통해 현재 갱신된 캐쉬 기록시간(timestamp)에 대한 간격을 제어하며, 타임아웃의 존재 의미를 위해 DefaultCacheTimeout 보다 적은 값으로 설정해야 합니다. 기본 식별 간격(resolution)은 60초(1분)입니다. 기본값에서 AuthenticationCacheJndiName이 변경된 경우에는 효력을 미치지 않습니다.

또한 JaasSecurityManagerService에서는 여러 유용한 오퍼레이션들을 지원하고 있습니다. 런타임에서 보안 도메인 인증 캐쉬를 정리하고, 보안 도메인 인증 캐쉬내의 활성화된 사용자의 목록을 얻기와 보안 관리자 인터페이스 메쏘드등이 포함되어 있습니다.

저장에 사용된 저장소가 갱신되었고 사용된 상태를 즉시 저장하기 원할경우 보안 도메인 인증 캐쉬를 정리함으로써 모든 캐쉬된 credential들을 없애는데 사용될 수 있습니다. MBean 오퍼레이션의 사용법은 : public void flushAuthenticationCache(String securityDomain)입니다.

이것은 아래에 제공된 코드 일부분을 사용하여 프로그램적으로 호출될 수 있습니다:

MBeanServer server = ...;
String jaasMgrName = "jboss.security:service=JaasSecurityManager";
ObjectName jaasMgr = new ObjectName(jaasMgrName);
Object[] params = {domainName};
String[] signature = {"java.lang.String"};
server.invoke(jaasMgr, "flushAuthenticationCache", params, signature);

활성화된(active) 사용자들의 목록을 얻음으로써 유효한 보안 도메인 인증 캐쉬내의 Principals 키의 스냅샷을 제공합니다. MBean 오퍼레이션 사용법은 : public List getAuthenticationCachePrincipals(String securityDomain) 입니다.

이것은 아래에 제공된 코드 일부분을 사용하여 프로그램적으로 호출될 수 있습니다:

MBeanServer server = ...;
String jaasMgrName = "jboss.security:service=JaasSecurityManager";
ObjectName jaasMgr = new ObjectName(jaasMgrName);
Object[] params = {domainName};
String[] signature = {"java.lang.String"};
List users = (List) server.invoke(jaasMgr, "getAuthenticationCachePrincipals",  params, signature);

보안 관리자는 약간의 추가적인 액세스 메쏘드를 갖습니다.

public boolean isValid(String securityDomain, Principal principal, Object credential);
public Principal getPrincipal(String securityDomain, Principal principal);
public boolean doesUserHaveRole(String securityDomain, Principal principal, 
                                Object credential, Set roles);
public Set getUserRoles(String securityDomain, Principal principal, Object credential);

이 메쏘드들은 securityDomain 인수를 통해 이 이름에 일치하는 관련된 보안 도메인의 AuthenticationManagerRealmMapping 인터페이스 메쏘드에 액세스할 수 있도록 합니다.

8.4.3. JaasSecurityDomain MBean

org.jboss.security.plugins.JaasSecurityDomain은 SSL과 다른 암호 기술에 대한 KeyStore, JSSE KeyManagerFactoryTrustManagerFactory 표기를 추가하는 JaasSecurityManager의 확장입니다. JaasSecurityDomain에 포함된 추가적인 설정가능한 속성들은 다음과 같습니다:

JKS
  • KeyStoreType: KeyStore 구현의 타입입니다. 이것은 java.security.KeyStore.getInstance(String type) 팩토리 메쏘드에 넘겨지는 타입 인수입니다.
  • KeyStoreURL: KeyStore 데이터베이스의 위치에 대한 URL 입니다. 이것은 KeyStore를 초기화하기 위해 InputStream을 얻는데 사용됩니다. 문자열이 올바른 URL이 아닌 경우, 파일로써 취급됩니다.
  • KeyStorePass: KeyStore 데이터베이스 컨텐츠와 관련된 암호. KeyStorePass는 encode/decode 오퍼레이션에서 사용되는 PBE secret key를 생성하기 위한 SaltIterationCount 속성의 조합으로도 사용됩니다. KeyStorePass 속성 값의 포맷은 다음 중에 하나를 만족해야 합니다:
    • KeyStore에 대한 평문(plaintext) 암호. 문자열의 toCharArray() 값이 어떠한 조작없이 사용됩니다.
    • 평문 암호를 획득하기위해 실행하는 명령문. 명령문의 포맷은 : {EXT}... 이며, 여기서 ...는 플랫폼에 특정한 명령문을 실행시키기 위해 Runtime.exec(String) 메쏘드로 넘겨지게 되는 정확한 명령어 라인입니다. 명령문으로 부터 출력되는 첫 번째 줄은 암호로써 사용됩니다.
    • 평문 암호를 얻기위해 생성시키는 클래스입니다. 포맷은 {CLASS}classname[:ctorarg] 이며, 여기서 [:ctorarg]classname을 초기화할 때 컨스트럭쳐쪽에 넘겨지게 되는 옵션 문자열입니다. 암호는 toCharArray() 메쏘드를 발견하게 되면 이를 호출하여 클래스이름으로부터 얻고, 발견하지 못할 경우에는 toString() 메쏘드가 사용됩니다.
  • Salt: PBEParameterSpec salt 값.
  • IterationCount: PBEParameterSpec 반복 카운트 값.
  • ManagerServiceName: 보안 관리자 서비스 MBean의 JMX 객체 이름 문자열을 지정합니다. 이것은 java:/jaas/<domain> 아래쪽에 보안 관리자로써 JaasSecurityDomain을 등록시키기 위한 디폴트를 등록하는데 사용되며, 여기서 <domain>은 MBean 컨스트럭쳐쪽에 넘겨지는 이름입니다. 이 이름은 jboss.security:service=JaasSecurityManager의 디폴트입니다.
  • LoadSunJSSEProvider: Sun com.sun.net.ssl.internal.ssl.Provider 보안 프로바이더가 구동시에 로딩되어져야만 하는지를 가르키는 플래그. 이것은 JDK 1.3의 확장으로 설치되지 않았을 경우 Sun JSSE jars를 사용할 때 필요합니다. JDK 1.4를 사용하거나 이와 대치되어 사용될 수 있는 JSSE 프로바이더를 사용할 경우 이 값은 false로 설정해주어야만 합니다. 이 플래그는 현재 디폴트로 true를 갖습니다.

8.4.4. XML JAAS 로그인 설정 MBean

JBoss는 org.jboss.security.auth.login.XMLLoginConfig MBean을 통해 제공되는 javax.security.auth.login.Configuration 클래스의 커스텀 구현을 사용합니다. 이 설정 구현에서는 그림 8.8, “XMLLoginConfig DTD”에 보여지고 있는 DTD에 따르는 XML 포맷을 사용합니다.

XMLLoginConfig DTD

그림 8.8. XMLLoginConfig DTD

application-policy의 name 속성은 로그인 설정의 이름입니다. 이것은 java:/jaas/ 접두사 다음에 따라오는 jboss.xmljboss-web.xml security-domain 요소의 값의 부분과 일치합니다. login-module 요소의 code 속성에서는 로그인 모듈 구현의 class 이름을 지정합니다. flag 속성은 인증 스택의 동작 형태를 제어합니다. 사용이 가능한 값들과 그 의미는 다음과 같습니다:

  • required: 성공을 위해서는 LoginModule이 필요합니다. 성공 또는 실패하는 경우, 인증은 여전히 LoginModule 목록의 다음번을 처리합니다.
  • requisite: 성공을 위해서는 LoginModule이 필요합니다. 성공하면, 인증은 LoginModule 목록의 다음 항목으로 넘어갑니다. 실해하면, 그 즉시 제어가 어플리케이션쪽으로 반환됩니다(인증은 LoginModule 목록의 다음으로 넘거가지 않습니다).
  • sufficient: 성공을 위해 LoginModule이 필요하지 않습니다. 성공하면, 제어가 그 즉시 어플리케이션으로 반환됩니다(인증은 LoginModule 목록의 다음으로 넘거가지 않습니다). 실패하면, 인증은 LoginModule 목록의 다음 항목으로 넘어갑니다.
  • optional: 성공을 위해 LoginModule이 필요하지 않습니다. 성공 또는 실패하면, 인증은 여전히 LoginModule 목록의 다음 항목으로 넘어가 수행합니다.

0개 이상의 module-option 요소가 login-module의 자식 요소로써 지정될 수 있습니다. 이렇게 정의된 이름/값 문자열 쌍은 로그인 모듈이 초기화되는 동안 사용이 가능해지게 됩니다. name 속성에는 옵션의 이름을 지정하는 반면, module-option의 안에는 값을 제공합니다. 로그인 설정의 예제를 예제 8.11, “XMLLoginConfig에 적합한 사용을 한 로그인 모듈 설정의 샘플”에서 보여주고 있습니다.

예제 8.11. XMLLoginConfig에 적합한 사용을 한 로그인 모듈 설정의 샘플

<policy>
    <application-policy name="srp-test">
        <authentication>
            <login-module code="org.jboss.security.srp.jaas.SRPCacheLoginModule"
                          flag="required">
                <module-option name="cacheJndiName">srp-test/AuthenticationCache</module-option>
            </login-module>
                
            <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
                             flag="required">
                <module-option name="password-stacking">useFirstPass</module-option>
            </login-module>
        </authentication>
    </application-policy>
</policy>

XMLLoginConfig MBean 은 다음과 같은 속성들을 지원합니다:

  • ConfigURL: 구동시 이 MBean을 통해 로딩되어야만 하는 XML 로그인 설정 파일의 URL을 지정합니다. 이 값은 반드시 올바른 URL 문자열이어야 합니다.
  • ConfigResource: 구동시 이 MBean을 통해 로딩되어야만 하는 XML 로그인 설정 파일의 리소스 이름을 지정합니다. 이름은 쓰레드 컨텍스트 클래스 로더를 사용하여 위치된 URL에 대한 classpath 리소스로 처리됩니다.
  • ValidateDTD: XML 설정이 자신의 DTD에 대해 검증되어져야만 하는지를 가르키는 플래그. 기본 값은 true 입니다.

또한 MBean은 런타임에서 로그인 설정을 동적으로 확장시킬 수 있도록 하는 다음과 같은 오퍼레이션들도 지원합니다. 로그인 설정을 변경하려는 시도를 하는 어떠한 오퍼레이션이라도 보안 관리자가 함께 동작하고 있다면, javax.security.auth.AuthPermission("refreshLoginConfiguration")이 필요합니다. org.jboss.chap8.service.SecurityConfig 서비스는 어떻게 특정한 보안 설정을 동적으로 추가/삭제하는지를 보여주고 있습니다.

  • void addAppConfig(String appName, AppConfigurationEntry[] entries): 이것은 주어진 로그인 모듈 설정 스택에 현재 설정밑에 주어진 appName을 추가합니다. 이 오퍼레이션은 주어진 이름밑에 존재하고 있는 어떠한 엔트리라도 대체시킵니다.
  • void removeAppConfig(String appName): 이 오퍼레이션은 주어진 appName 아래에 등록된 로그인 모듈 설정을 제거합니다.
  • String[] loadConfig(URL configURL) throws Exception: XML 혹은 기존의 Sun 로그인 환경 파일중에 하나가 될 수 있는 URL로부터 하나 이상의 로그인 설정을 로딩합니다. 모든 로그인 설정은 반드시 추가되거나 그 어떤 것도 추가되지 않는다는 것에 주의해야 합니다. 이 오퍼레이션은 추가된 로그인 설정의 이름을 반환합니다.
  • void removeConfigs(String[] appNames): 지정된 appNames 배열의 로그인 설정을 제거합니다.
  • String displayAppConfig(String appName): 이 오퍼레이션은 존재한다면, 간단한 문자열 포맷의 이름을 갖는 설정을 보여줍니다.

8.4.5. JAAS 로그인 설정 관리 MBean

커스텀 javax.security.auth.login.Configuration의 설치는 org.jboss.security.plugins.SecurityConfig MBean으로 관리됩니다. 여기에는 하나의 설정가능한 속성이 있습니다:

  • LoginConfig: 디폴트 JAAS 로그인 설정을 제공하는 JMX ObjectName 문자열을 지정합니다. SecurityConfig이 시작된 경우라면, 자신의 getConfiguration(Configuration currentConfig) 오퍼레이션을 호출함으로써 자신의 javax.security.auth.login.Configuration에 대한 질의가 된다는 것을 의미합니다. LoginConfig 속성을 지정하지 않으면, Configuration class JavaDocs 에서 기술된 것 처럼 기본적으로 Sun Configuration 구현이 사용되어집니다.

커스텀 JAAS 로그인 설정 구현을 허용하는 것에 덧붙여, 이 서비스는 런타임시 스택내에 설정이 함께 변경되어질 수 있도록 합니다. 이를 통해 로그인 설정을 스택내에 집어넣고 나중에 꺼낼 수 있도록 합니다. 이것은 보안 유닛 테스트를 사용하여 디폴트 JBoss 설치내에 커스텀 로그인 설정을 설치하는데 사용하는 기능입니다. 새로운 설정을 넣어주는 것은 다음과 같이 하여 수행됩니다:

public void pushLoginConfig(String objectName) throws
                JMException, MalformedObjectNameException;

objectName 매개변수에는 LoginConfig 속성과 유사한 MBean을 지정합니다. 현재의 로그인 설정 제거는 다음과 같이 합니다:

public void popLoginConfig() throws JMException;

8.4.6. JBossSX 로그인 모듈의 사용과 작성

JaasSecurityManager 구현은 JAAS 로그인 모듈 설정을 사용하는 인증 메커니즘의 완전한 커스터마이징을 허용합니다. 보안 도메인의 이름과 일치하는 로그인 모듈 설정 엔트리를 정의해줌으로써 여러분은 여러분의 J2EE 컴포넌트에 안전하게 액세스할 수 있고, 인증 메커니즘을 정의하고 이러한 구현들을 통합할 수 있습니다.

JBossSX 프레임워크에는 표준 보안 인프라구조의 저장 프로토콜들인 LDAP과 JDBC에 통합시키는데 적합한 로그인 모듈의 다양한 번들이 포함되어 있습니다. 또한 표준 base 클래스 구현을 포함하여 예상되는 LoginModule8.4.7 절, “커스텀 로그인 모듈 작성하기”에서 기술되어 있는 Subject 사용 패턴으로 만드는데 도움을 주게 됩니다. 이러한 구현들을 통해 번들링으로 제공되는 로그인 모듈에서 적절한 것을 찾을 수 없다면, 여러분이 만든 인증 프로토콜을 쉽게 통합할 수 있습니다. 이번 섹션에서 우리는 먼저 유용하게 사용되는 번들된 로그인 모듈과 이들의 설정을 살펴 본 후, 끝에가서 여러분이 만든 커스텀 LoginModule 구현을 JBoss에서 어떻게 사용하는지 살펴보도록 하겠습니다.

8.4.6.1. org.jboss.security.auth.spi.IdentityLoginModule

IdentityLoginModule은 모듈에 대한 어떠한 subject를 인증하는 옵션을 갖는 모듈내에 지정한 principal과 관련된 간단한 로그인 모듈입니다. principal 옵션으로 지정한 이름을 사용하는 SimplePrincipal 인스턴스를 생성합니다. 비록 이러한 방식이 실제 상황에서는 강력한 인증을 위한 적절한 로그인 모듈이 아니더라도, 개발환경에서 주어진 princiapl과 이에 관련된 roles에 대한 보안 테스트에는 사용될 수 있습니다.

지원되는 로그인 모듈 설정 옵션은 다음과 같습니다:

  • principal=string: 모든 인증된 사용자를 SimplePrincipal로써 사용하는 이름. principal의 이름은 principal option을 지정하지 않는 경우 guest가 디폴트로 설정됩니다.
  • roles=string-list: 사용자의 principal에 할당되어질 roles의 이름. 값은 콤마로 분리된 역할의 이름 항목입니다.
  • password-stacking=useFirstPass: password-stacking 옵션이 설정되면, 이 모듈은 먼저 로그인 모듈의 공유된 상태 맵내에서 javax.security.auth.login.name 속성 이름밑의 공유된 username에 대해 락을 겁니다. 로그인 모듈의 공유된 상태 맵내에서 찾았다면, 이것이 principal의 이름으로 사용됩니다. 발견되지 않은 경우에는 principal의 이름은 로그인 모듈에 의해 설정되며, javax.security.auth.login.name 속성 이름밑에 저장됩니다.

인증하는 모든 사용자들을 jduke 라는 이름을 갖는 principal과 TheDuke 그리고 AnimatedCharacter라는 이름을 갖는 role을 할당해주는 간단한 기존의 Sun 포맷 로그인 설정 엔트리는 다음과 같습니다:

testIdentity {
    org.jboss.security.auth.spi.IdentityLoginModule required
    principal=jduke
    roles=TheDuke,AnimatedCharater;
};

대응되는 XMLLoginConfig 포맷은 다음과 같습니다:

<policy>
    <application-policy name="testIdentity">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.IdentityLoginModule"
                         flag="required">
                <module-option name="principal">jduke</module-option>
                <module-option name="roles">TheDuke,AnimatedCharater</module-option>
            </login-module>
        </authentication>
    </application-policy>
</policy> 

이 엔트리를 디폴트 설정 파일셋트내에서 찾을 수 있는 JBoss 서버의 로그인 설정으로 추가하려면, JBoss 배포판의 conf/default/auth.conf 파일을 수정해주시면 됩니다.

8.4.6.2. org.jboss.security.auth.spi.UsersRolesLoginModule

UsersRolesLoginModule은 여러 사용자와 roles을 지원하는 또 다른 간단한 로그인 모듈로써 두 개의 Java Properties로 포맷팅된 텍스트 파일에 기반을 두고 있습니다. username-to-password 매핑 파일은 users.properties로 불려지며, username-to-roles 매핑 파일은 roles.properties라는 이름을 갖습니다. 속성 파일들은 쓰레드 컨텍스트 클래스 로더의 initialize 메쏘드를 사용하여 초기화하는 동안 로딩됩니다. 즉, 이 파일들은 J2EE 배치 JAR, JBoss 설정 디렉토리 혹은 JBoss 서버상의 임의의 디렉토리나 시스템 classpath 내로 위치될 수 있음을 의미합니다. 이 로그인 모듈의 주된 목적은 어플리케이션과 함께 배치되는 속성 파일들을 사용하여 여러 사용자와 역할에 대한 보안 설정을 쉽게 테스트 하는 것입니다.

users.properties파일은 아래에 보여지는 것 처럼 각각의 라인에서 별도의 사용자 엔트리에 대한 username=password 포맷을 사용합니다 :

username1=password1
username2=password2
...

roles.properties 파일은 선택적인 그룹의 이름 값과 함께 username=role1,role2,... 포맷이 사용됩니다. 즉 :

username1=role1,role2,...
username1.RoleGroup1=role3,role4,...
username2=role1,role3,...

속성의 이름중 username.XXX 폼은 특정한 이름의 그룹에 대한 역할에 username의 역할을 할당하는데 사용됩니다. 여기서 XXX 부분은 그룹 이름의 속성 이름입니다. username=... 폼은 username.Roles=...의 축약형인데, 여기서 Roles 그룹 이름은 사용자의 인가(permission)를 정의하는 역할을 포함하도록 기대되는 JaasSecurityManager의 표준 이름입니다.

다음은 username으로 jduke를 정의하는 것과 동일한 효력을 발휘합니다:

jduke=TheDuke,AnimatedCharacter
jduke.Roles=TheDuke,AnimatedCharacter

로그인 모듈 설정 옵션에 지원되는 것들은 다음과 같습니다:

  • unauthenticatedIdentity=name: 인증 정보를 포함하고 있지 않은 요청에 할당되어져야하는 자격(principal)의 이름을 정의합니다. 보호되지 않는 서블릿이 특정한 역할을 필요로 하지 않는 EJB에 대한 메쏘드를 호출하도록 할때 사용됩니다. 이러한 자격(principal)은 역할들과 어떠한 관계도 없으며 따라서 오직 보호되지 않는 EJB나 인가에 대한 제약이 없는 EJB 메쏘드에 대해서만 액세스가 가능합니다.
  • password-stacking=useFirstPass: password-stacking 옵션이 설정되었다면, 이 모듈은 먼저 공유된 username과 password를 로그인 모듈의 공유 상태 맵내에서 각각 javax.security.auth.login.namejavax.security.auth.login.password 속성 이름아래에서 찾습니다. 찾는 경우에는 이것들을 자격의 이름과 암호로써 사용합니다. 못찾으면 자격의 이름과 암호는 로그인 모듈에 의해 설정되고 각각 javax.security.auth.login.namejavax.security.auth.login.password 속성 이름 아래쪽에 저장됩니다.
  • hashAlgorithm=string: 암호를 해쉬하는데 사용하는 java.security.MessageDigest 알고리즘의 이름. 이것은 디폴트값을 갖지 않으므로 해쉬를 활성화하기 위해서는 이 옵션을 반드시 지정해야 합니다. hashAlgorithm이 지정되었다면, callbackhandler에 의해 얻어진 어떤 가공도 되지 않은 텍스트의 암호는 inputPassword 인수로써 UsernamePasswordLoginModule.validatePassword쪽에 넘겨지기전에 해쉬됩니다. users.properties 파일내에 저장된 expectedPassword도 반드시 이에 필적하는 해쉬처리가 되어야만 합니다.
  • hashEncoding=base64|hex: 해쉬된 암호에 대한 문자열 포맷으로 base64 혹은 hex 중 하나를 갖아야 합니다. 기본값은 Base64 입니다.
  • hashCharset=string: 단순한 텍스틀된 암호를 바이트 어레이로 변환하는데 사용하는 인코딩. 플랫폼의 기본 인코딩이 기본 값이 됩니다.
  • usersProperties=string: username-to-password 매핑을 포함하고 있는 속성 리소스의 이름. 기본값은 users.properties 입니다.
  • rolesProperties=string: username-to-roles 매핑을 포함하고 있는 속성 리소스의 이름. 기본값은 roles.properties 입니다.

자격 이름은 nobody 이고 base64로 인코딩되며 usersb64.properties 파일내에서 암호는 MD5로 해쉬되는 인증되지 않은 사용자에게 할당된 기존 Sun 포맷의 로그인 설정 샘플은 다음과 같습니다:

testUsersRoles {
    org.jboss.security.auth.spi.UsersRolesLoginModule required
    usersProperties=usersb64.properties
    hashAlgorithm=MD5
    hashEncoding=base64
    unauthenticatedIdentity=nobody
    ;
};

이에 대응되는 XMLLoginConfig 포맷은 다음과 같습니다:

<policy>
    <application-policy name="testUsersRoles">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
                          flag="required">
                <module-option name="usersProperties">usersb64.properties</module-option>
                <module-option name="hashAlgorithm">MD5</module-option>
                <module-option name="hashEncoding">base64</module-option>
                <module-option name="unauthenticatedIdentity">nobody</module-option>
            </login-module>
        </authentication>
    </application-policy>
</policy>

8.4.6.3. org.jboss.security.auth.spi.LdapLoginModule

LdapLoginModule은 로그인 모듈 설정 옵션으로 JNDI 로그인을 사용하는 LDAP 서버에 대해 인증하는 LoginModule 구현입니다. 여러분의 username과 credential 정보가 JNDI LDAP 프로바이더를 사용하여 액세스할 수 있는 LDAP 서버에 저장된 경우 LdapLoginModule을 사용할 수 있습니다.

LDAP 연결 정보는 JNDI 초기 컨텍스트를 생성하기 위해 사용하는 환경 객체에 넘겨지는 설정 옵션들로써 제공됩니다. 표준 LDAP JNDI 속성들에는 다음과 같은 것들이 있습니다.

  • java.naming.factory.initial: InitialContextFactory 구현 클래스 이름. 이것은 Sun LDAP 프로바이더 구현인 com.sun.jndi.ldap.LdapCtxFactory의 기본입니다.
  • java.naming.provider.url: LDAP 서버에 대한 LDAP URL 입니다.
  • java.naming.security.authentication: 사용하려는 보안 레벨. simple의 기본입니다.
  • java.naming.security.protocol: ssl과 같은 안전한 액세스를 위해 사용하려는 전송 프로토콜.
  • java.naming.security.principal: 서비스의 호출자를 인증하기위한 자격증명(principal). 이것은 다음 아랫쪽에서 기술하는 것 처럼 다른 속성들로부터 만들어집니다.
  • java.naming.security.credentials: 인증 스키마에 의존하는 속성의 값. 예를 들면, 해쉬된 암호, 아무런 조작없는 암호, 키, 인증서 등등이 될 수 있습니다.

지원되는 로그인 모듈 설정의 옵션은 다음과 같습니다:

  • principalDNPrefix=string: 사용자를 구별하는 이름을 구성하기 위한 사용자이름(username)에 접두어로 붙이는 문자열. 보다 상세한 정보는 principalDNSuffix를 참조하십시오.
  • principalDNSuffix=string: 사용자를 구별하는 이름을 구성하기 위한 사용자이름에 접미사로 붙이는 문자열. 사용자이름을 묻는 프롬프트에서 완전히 구별되는 이름을 입력하길 원하지 않는 경우에 유용합니다. 이 속성과 principalDNSuffix를 사용하여 userDN 은 다음과 같은 형태로 구성되어집니다:
String userDN = principalDNPrefix + username + principalDNSuffix;  
  • useObjectCredential=true|false: 자격증명(credential)을 JAAS PasswordCallback을 사용한 char[] password 보다는 Callbackorg.jboss.security.auth.callback.ObjectCallback 타입을 사용하여 불투명한(opaque) Object 로써 획득하는 것을 가르킵니다. 이것을 통해 non-char[] credential 정보를 LDAP 서버쪽으로 넘겨줄 수 있습니다.
  • rolesCtxDN=string: 사용자의 역할(role) 검색을 위한 컨텍스트를 구별하기 위한 고정된 이름.
  • userRolesCtxDNAttributeName=string: 사용자 역할에 대한 검색을 위한 컨텍스트의 구별되는 이름을 포함하는 사용자 객체내의 속성 이름. 이것은 각각의 사용자에 따라 고유한 사용자의 역할을 검색하기 위한 컨텍스트라는 면에서 rolesCtxDN와 차별됩니다.
  • roleAttributeID=string: 사용자의 역할(role)을 포함하는 속성의 이름입니다. 이것을 지정하지 않으면 기본값으로 roles이 설정됩니다.
  • roleAttributeIsDN=string: roleAttributeID가 role 객체 또는 role 이름의 완전히 구별되는 이름을 포함하는지를 가르키는 플래그입니다. false인 경우, role의 이름은 roleAttributeID의 값에서 가져옵니다. true인 경우에는 role 속성은 role 객체의 구별되는 이름을 표시합니다. role 이름은 구별되는 이름에 의해 컨텍스트 이름의 roleNameAttributeId 속성의 값으로부터 가져옵니다. 특정 디렉터리 스키마(가령 MS의 액티브디렉터리)에서 사용자 객체내의 role의 속성은 단순한 이름들 대신 role 객체에 DN을 저장하며, 여기서 이 속성은 true로 설정되어야만 합니다. 기본값은 false입니다.
  • roleNameAttributeID=string: role 이름을 포함하고 있는 구별되는 이름인 roleCtxDN에 의해 가리켜지는 컨텍스트내의 속성 이름입니다. roleAttributeIsDN 속성을 true로 설정했다면, 이 속성은 role 객체의 name 속성을 찾는데 사용됩니다. 기본값은 group 입니다.
  • uidAttributeID=string: userid에 대응되는 사용자의 role을 포함하고 있는 객체내의 속성 이름입니다. 사용자의 role의 위치를 설정하는데 사용됩니다. 이 값을 지정하지 않으면, 기본값으로 uid가 사용됩니다.
  • matchOnUserDN=true|false: 사용자의 role 검색이 사용자의 완전히 구별되는 이름과 일치해야만 하는지를 가르키는 플래그값입니다. false로 설정하면, username은 uidAttributeName 속성에 대해 일치하는 값으로 사용되어집니다. true로 설정하면, 완전한 형태의 userDN이 일치하는 값으로 사용됩니다.
  • unauthenticatedIdentity=string: 어떠한 인증 정보도 포함하고 있지 않은 요청에 할당되어져야 하는 principal의 이름입니다. 이 특성은 UsernamePasswordLoginModule 슈퍼클래스로부터 상속됩니다.
  • password-stacking=useFirstPass: password-stacking 옵션이 설정되면, 이 모듈은 먼저 공유된 username과 password를 로그인 모듈의 공유 상태 맵내에서 각각 javax.security.auth.login.namejavax.security.auth.login.password 속성 이름아래에서 찾습니다. 이것들을 찾는다면 principal의 이름과 암호로 사용됩니다. 찾지 못하는 경우에는 principal의 이름과 암호는 이 로그인 모듈에 의해 설정되고 각각 javax.security.auth.login.namejavax.security.auth.login.password 속성 이름아래에 저장됩니다.
  • hashAlgorithm=string: 암호를 해쉬하는데 사용하는 java.security.MessageDigest 알고리즘의 이름입니다. 기본값이 없으므로 이 옵션은 반드시 해쉬가 가능하도록 지정해주어야만 합니다. hashAlgorithm를 지정했다면, callbackhandler로부터 가져온 평문의 암호는 inputPassword 인수로써 UsernamePasswordLoginModule.validatePassword에 넘겨지기전에 해쉬되어집니다. LDAP 서버내에 저장되는 expectedPassword는 반드시 해쉬되어야만 합니다.
  • hashEncoding=base64|hex: 해쉬된 암호의 포맷 문자열이며 base64hex 두 값중에 하나이어야만 합니다. 기본값은 base64 입니다.
  • hashCharset=string: 평문 암호를 바이트 배열로 변환할 때 사용되는 인코딩. 플랫폼의 기본 인코딩이 기본값이 됩니다.
  • allowEmptyPasswords: 빈(길이가 0인) 암호가 LDAP 서버쪽으로 넘어가야하는지를 가리키는 플래그. 빈 암호는 몇몇 LDAP 서버에서 anonymous 로그인으로 간주되며, 이것은 권장할 만한 기능은 아닙니다. 이것을 false로 설정하여 빈 암호에 대해서 거부하게 할 수 있으며, true로 설정하여 LDAP 서버쪽에서 빈 암호에 대해 검증할 수 있도록 할 수 있습니다. 기본값은 true 입니다.

사용자의 인증(authentication)은 로그인 모듈 설정 옵션에 따라 LDAP 서버에 연결함으로써 수행됩니다. LDAP 서버에 연결하는 것은 이번 섹션 앞에서 설명했던 LDAP JNDI 속성의 환경을 구성하는 InitialLdapContext를 생성함으로써 이루어집니다. Context.SECURITY_PRINCIPAL을 설정하여 principalDNPrefixprincipalDNSuffix 옵션 값의 조합을 통해 callback 핸들러에서 사용자의 구별되는 이름을 얻을 수 있으며, useObjectCredential 옵션에 따라 Context.SECURITY_CREDENTIALS 속성은 String 암호나 Object credential 로 설정됩니다.

InitialLdapContext 인스턴스를 생성하는 것이 가능한 상태에 의해 인증 성공을 일단 통과하게 되면, rolesCtxDN 위치에서 roleAttributeNameuidAttributeName 옵션 값의 설정을 통해 search 속성들을 갖는 검색을 수행함으로써 사용자의 role이 질의되어집니다. role의 이름은 검색 결과셋내의 role 속성에 대한 toString 메쏘드를 호출함으로써 얻을 수 있습니다.

로그인 설정 항목에 대한 썬의 레가시 포맷의 샘플은 다음과 같습니다:

testLdap {
    org.jboss.security.auth.spi.LdapLoginModule required
    java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
    java.naming.provider.url="ldap://ldaphost.jboss.org:1389/"
    java.naming.security.authentication=simple
    principalDNPrefix=uid=
    uidAttributeID=userid
    roleAttributeID=roleName
    principalDNSuffix=,ou=People,o=jboss.org
    rolesCtxDN=cn=JBossSX Tests,ou=Roles,o=jboss.org
};

대응되는 XMLLoginConfig 포맷은 다음과 같습니다:

<policy>
    <application-policy name="testLdap">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.LdapLoginModule"
                          flag="required">
                <module-option name="java.naming.factory.initial">
                    com.sun.jndi.ldap.LdapCtxFactory
                </module-option>
                <module-option name="java.naming.provider.url">
                    ldap://ldaphost.jboss.org:1389/
                </module-option>
                <module-option name="java.naming.security.authentication">
                    simple
                </module-option>
                <module-option name="principalDNPrefix">uid=</module-option>
                <module-option name="uidAttributeID">userid</module-option>
                <module-option name="roleAttributeID">roleName</module-option>
                <module-option name="principalDNSuffix">,ou=People,o=jboss.org
                </module-option>
                <module-option name="rolesCtxDN">cn=JBossSX Tests,ou=Roles,o=jboss.org
                </module-option>
            </login-module>
        </authentication>
    </application-policy>
</policy>

여러분들이 LdapLoginModule의 모든 옵션들을 이해하는 것을 돕기위해, 그림 8.9, “ testLdap 샘플 환경에 대응되는 LDAP 서버의 환경설정” 에서 LDAP 서버 데이터 샘플을 제시하였습니다. 이 그림은 보이는 것처럼 testLdap 로그인 환경에 일치합니다.

testLdap 샘플 환경에 대응되는 LDAP 서버의 환경설정

그림 8.9. testLdap 샘플 환경에 대응되는 LDAP 서버의 환경설정.

그림 8.9, “testLdap 샘플 환경에 대응되는 LDAP 서버의 환경설정” 스키마와 비교를 하면서 testLdap 로그인 모듈 환경설정을 살펴보도록 하겠습니다. java.naming.factory.initial, java.naming.factory.url 그리고 java.naming.security 옵션들은 Sun LDAP JNDI provider 구현이 사용되는 것을 가르키며, LDAP 서버는 ldaphost.jboss.org 호스트의 1389번 포트에 위치해 있고, 간단한 username과 password가 LDAP 서버와 연결된 클라이언트 인증에 사용됩니다.

LdapLoginModule이 사용자 인증을 수행할 때는 java.naming.factory.url에서 지정한 LDAP 서버에 연결됨으로써 인증을 수행합니다. java.naming.security.principal 속성은 앞에서 언급했던 것 처럼 username에서 principalDNPrefixprincipalDNSuffix가 붙어져 만들어집니다. testLdap 설정 예제와 jduke username에 대해 java.naming.security.principal 문자열은 uid=jduke,ou=People,o=jboss.org이 됩니다. 이것은 Principal Context라는 라벨이 붙은 그림 8.9, “testLdap 샘플 환경에 대응되는 LDAP 서버의 환경설정”에서의 우측 하단부에 표시된 LDAP 컨텍스트에 대응됩니다. java.naming.security.credentials 속성은 암호로 넘겨지도록 설정되어지며 principal contextuserPassword 속성과 일치하게 됩니다. 인증에 대한 자격증명(credential) 정보를 LDAP 컨텍스트에서 어떻게 안전하게 저장하는가는 사용하는 LDAP 서버에 따라 다르므로, 여러분의 LDAP 서버에서 java.naming.security.credentials 속성의 검증을 처리하는 것은 다를 수도 있습니다.

일단 인증이 성공하게 되면, 인증에 기초가 되는 role은 rolesCtxDN 옵션 값에 의해 주어지는 구별되는 이름을 갖는 LDAP 컨텍스트의 JNDI 검색을 수행함으로써 가져오게 됩니다. testLdap 환경에서 이것은 cn=JBossSX Tests,ou=Roles,o=jboss.org 이며, 그림 8.9, “testLdap 샘플 환경에 대응되는 LDAP 서버의 환경설정”Roles Context로 표시되어 있는 좌측 하단부의 LDAP 컨텍스트에 대응됩니다. 검색에서는 uidAttributeID 옵션에 의해 주어진 이름과 로그인 모듈에 넘겨지는 username에 일치하는 값의 속성을 포함하는 그 어떤 서브컨텍스트의 위치를 찾습니다. 일치하는 컨텍스트에 대해서는 roleAttributeID 옵션에 의해 주어진 이름의 속성에 대한 모든 값들을 얻게됩니다. testLdap 환경에서 role을 포함하고 있는 속성의 이름은 roleName이라 불립니다. 획득된 roleName 값은 역할기반의 인증에서 사용되어질 Roles 그룹의 principal로써 LdapLoginModule 과 관련된 JAAS Subject내에 저장되어 집니다. 그림 8.9, “testLdap 샘플 환경에 대응되는 LDAP 서버의 환경설정”내에서 보여지는 LDAP 스키마에서 jduke 라는 유저에 할당되어질 역할은 TheDukeAnimatedCharacter 입니다.

8.4.6.4. org.jboss.security.auth.spi.DatabaseServerLoginModule

DatabaseServerLoginModule은 인증과 역할 매핑을 지원하는 JDBC 기반의 로그인 모듈입니다. 여러분이 사용자명, 암호 및 역할 정보를 JDBC로 액세스가능한 데이터베이스내에 갖고있을 경우 사용할 수 있는 로그인 모듈입니다. DatabaseServerLoginModule은 두개의 논리적 테이블에 기초를 두고 있습니다. :

Table Principals(PrincipalID text, Password text)
Table Roles(PrincipalID text, Role text, RoleGroup text) 

Principals 테이블은 사용자의 PrincipalID와 올바른 암호를 저장하며 Roles 테이블에는 역할세트와 함께 사용자의 PrincipalID가 저장됩니다. 사용자의 인가(permission)를 위해 사용되는 역할은 Roles 테이블의 RoleGroup 컬럼 값에 포함되어 있어야만 합니다. 테이블들은 로그인 모듈에서 사용하는 SQL 질의를 지정할 수 있기 때문에 논리적입니다. 필요한 모든 것은 앞에서 기술된 것 처럼 PrincipalsRoles 테이블과 동일한 논리적인 구조를 갖는 java.sql.ResultSet 뿐입니다. 테이블과 컬럼의 실제 이름은 결과가 컬럼 인텍스에 기반하여 액세스되어지므로 관련되지 않습니다. 이러한 의미를 명확히 하기위해, 앞에서 선언된 것과 같이 PrincipalsRoles 두개의 테이블을 갖는 데이터베이스를 생각해보겠습니다. 다음에 오는 문장들은 Passwordechoman을 갖는 PrincipalIDjava를 포함하는 Principals 테이블과 RoleGroupRolesEcho라는 이름의 역할을 갖는 PrincipalIDjavaRoles 테이블 그리고 RoleGroupCallerPrincipalcaller_java라는 이름의 역할을 갖는 PrincipalIDjavaRoles 테이블을 만듭니다 :

INSERT INTO Principals VALUES('java', 'echoman')
INSERT INTO Roles VALUES('java', 'Echo', 'Roles')
INSERT INTO Roles VALUES('java', 'caller_java', 'CallerPrincipal') 

로그인 모듈 설정 옵션에서 지원되는 것들은 다음과 같습니다:

  • dsJndiName: 논리적인 PrincipalsRoles 테이블을 포함하고 있는 데이터베이스의 DataSource에 대한 JNDI 이름입니다. 지정하지 않은 경우 기본값은 java:/DefaultDS가 됩니다.
  • principalsQuery: select Password from Principals where PrincipalID=?와 동등한 준비된 문장 질의(prepared statement query)입니다. 지정하지 않았다면, 이것은 사용될 바로 그(exact) 준비된 문장이 됩니다.
  • rolesQuery: select Role, RoleGroup from Roles where PrincipalID=?와 동등한 준비된 문장 질의입니다. 지정하지 않았다면, 이것은 사용될 바로 그 준비된 문장입니다.
  • unauthenticatedIdentity=string: 인증 정보를 포함하지 않은 요청에 할당되어져야 하는 principal의 이름입니다.
  • password-stacking=useFirstPass: password-stacking 옵션이 설정된 경우, 이 모듈은 먼저 로그인 모듈 공유 상태 맵내에서 각각 javax.security.auth.login.namejavax.security.auth.login.password 속성 이름아래쪽의 공유된 사용자명과 암호에 대해 찾습니다. 찾았다면, principal의 이름과 암호로 사용되어 집니다. 찾지 못하면 principal의 이름과 암호는 로그인 모듈에 의해 설정되고 각각 javax.security.auth.login.namejavax.security.auth.login.password 속성 이름 아랫쪽에 저장됩니다.
  • hashAlgorithm=string: 암호를 해쉬하는데 사용하는 java.security.MessageDigest 알고리즘의 이름입니다. 기본값이 없으므로 해쉬 기능을 구현하려면 이 옵션을 반드시 지정해주어야 합니다. hashAlgorithm을 지정했다면, callbackhandler로부터 획득한 처리않된 문자열 암호는 inputPassword 인수로써 UsernamePasswordLoginModule.validatePassword 쪽에 넘겨지기전에 해쉬됩니다. 데이터베이스로부터 얻어진 expectedPassword는 해쉬되어져야만 합니다.
  • hashEncoding=base64|hex: 해쉬된 암호에 대한 문자열 포맷이며 base64hex 중에 하나 이여야 합니다. 기본값은 Base64 입니다.
  • hashCharset=string: 처리되지 않은 깨끗한 문자열 암호를 바이트 배열로 변환할 때 사용되는 인코딩입니다. 플랫폼의 기본 인코딩이 기본값이 됩니다.
  • ignorePasswordCase=true|false: 암호 대조가 무시되어지는 경우인지를 가르키는 부울린 플래그. 해쉬된 암호가 중요하지 않은 경우 해쉬된 암호 인코딩에 대해 유용할 수 있습니다.
  • principalClass: Principal 구현 클래스를 지정하는 옵션입니다. 이것은 principal의 이름에 대한 문자열 인수를 갖는 컨스트럭쳐를 지원해야만 합니다.

예제 DatabaseServerLoginModule 설정에서 다음과 같은 커스텀 테이블 스키마를 생각해보겠습니다:

CREATE TABLE Users(username VARCHAR(64) PRIMARY KEY, passwd VARCHAR(64))
CREATE TABLE UserRoles(username VARCHAR(64), userRoles VARCHAR(32))

간단한 썬의 레가시 포맷에 대응되는 DatabaseServerLoginModule 설정은 다음과 같을 수 있습니다:

testDB {
    org.jboss.security.auth.spi.DatabaseServerLoginModule required
    dsJndiName="java:/MyDatabaseDS"
    principalsQuery="select passwd from Users username where username=?"
    rolesQuery="select userRoles, 'Roles' from UserRoles where username=?"
    ;
}; 

이에 대응되는 XMLLoginConfig 포맷은 다음과 같습니다:

<policy>
    <application-policy name="testDB">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
                             flag="required">
                <module-option name="dsJndiName">java:/MyDatabaseDS</module-option>
                <module-option name="principalsQuery">
                    select passwd from Users username where username=?</module-option>
                <module-option name="rolesQuery">
                    select userRoles, 'Roles' from UserRoles where username=?</module-option>
            </login-module>
        </authentication>
    </application-policy>
</policy>

8.4.6.5. BaseCertLoginModule

이것은 X509 인증에 기반을 둔 사용자 인증을 하는 로그인 모듈입니다. 이 로그인 모듈의 전형적인 사용경우(usecase)는 웹계층(web-tier)에서의 CLIENT-CERT 인증입니다. 이 로그인 모듈은 오로지 인증만을 수행합니다. 여러분이 안전한 웹 또는 EJB 컴포넌트 액세스를 완전하게 정의하기 위해서는 인가 역할을 획득할 수 있는 또 다른 로그인 모듈과 조합할 필요가 있습니다. 이 로그인 모듈의 두 서브클래스인 CertRolesLoginModuleDatabaseCertLoginModule은 속성 파일이나 데이터베이스중 하나로부터 인가 역할을 획득하기위해 특성(behavior)을 확장합니다.

BaseCertLoginModule은 사용자 검증(validation)을 수행하기 위해 KeyStore를 필요로 합니다. 이것은 org.jboss.security.SecurityDomain 구현을 통해 획득되어집니다. 일반적으로 SecurityDomain 구현은 다음에 주어진 jboss-service.xml 설정의 일부분에서 보여지는 것 처럼 org.jboss.security.plugins.JaasSecurityDomain MBean을 사용하여 설정됩니다. :

<mbean code="org.jboss.security.plugins.JaasSecurityDomain"
       name="jboss.web:service=SecurityDomain">
    <constructor>
        <arg type="java.lang.String" value="jmx-console"/>
    </constructor>
    <attribute name="KeyStoreURL">resource:localhost.keystore</attribute>
    <attribute name="KeyStorePass">unit-tests-server</attribute>
</mbean>

이를 통해 JBossSX 보안 도메인의 네이밍 패턴에 따라 java:/jaas/jmx-console 이름 아랫쪽에서 JNDI를 통해 사용가능한 SecurityDomain 구현의 jmx-console 이름을 갖는 보안 도메인을 생성합니다. 클라이언트의 인증(certs)과 역할 기반의 인증(authorization)을 사용하여 jmx-console.war와 같은 웹 어플리케이션을 안전하게 하기위해서는 인증(authentication)과 공인(authorization)을 위해 사용되는 허가된 역할과 보안 도메인과 함께 먼저 web.xml을 수정하여 리소스들을 보호합니다.

<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC
                  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
                  "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app> 
    ... 
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>HtmlAdaptor</web-resource-name>
            <description>An example security config that only allows users with
                the role JBossAdmin to access the HTML JMX console web
                application </description>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
            <role-name>JBossAdmin</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>CLIENT-CERT</auth-method>
        <realm-name>JBoss JMX Console</realm-name>
    </login-config>
    <security-role>
        <role-name>JBossAdmin</role-name>
    </security-role>
</web-app>

그 다음으로 우리는 jboss-web.xml내에서 JBoss의 보안 도메인을 지정해줄 필요가 있습니다:

<jboss-web>
    <security-domain>java:/jaas/jmx-console</security-domain>
</jboss-web>

마지막으로 여러분이 방금전에 지정해준 jmx-console 보안 도메인에 대한 로그인 모듈 설정을 정의해줍니다. 이것은 conf/login-config.xml 파일내에서 이루어집니다.

<application-policy name="jmx-console">
    <authentication>
        <login-module code="org.jboss.security.auth.spi.BaseCertLoginModule" 
                      flag="required">
            <module-option name="password-stacking">useFirstPass</module-option>
            <module-option name="securityDomain">java:/jaas/jmx-console</module-option>
        </login-module>
        <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" 
                      flag="required">
            <module-option name="password-stacking">useFirstPass</module-option>
            <module-option name="usersProperties">jmx-console-users.properties</module-option>
            <module-option name="rolesProperties">jmx-console-roles.properties</module-option>
        </login-module>
    </authentication>
</application-policy>

여기서 BaseCertLoginModule은 클라이언트의 인증서(cert)에 대한 인증을 위해 사용되고, UsersRolesLoginModulepassword-stacking=useFirstPass 옵션때문에 오직 인증에서만 사용됩니다. localhost.keystorejmx-console-roles.properties 두개 모두에서 클라이언트의 인증서와 관련된 principal로의 매핑 항목이 필요합니다. 기본적으로 principal은 이름으로 구별되는 클라이언트의 증명서를 사용하여 생성되어집니다 :

[starksm@banshee9100 conf]$ keytool -printcert -file unit-tests-client.export
Owner: CN=unit-tests-client, OU=JBoss Inc., O=JBoss Inc., ST=Washington, C=US
Issuer: CN=jboss.com, C=US, ST=Washington, L=Snoqualmie Pass, EMAILADDRESS=admin
@jboss.com, OU=QA, O=JBoss Inc.
Serial number: 100103
Valid from: Wed May 26 07:34:34 PDT 2004 until: Thu May 26 07:34:34 PDT 2005
Certificate fingerprints:
         MD5:  4A:9C:2B:CD:1B:50:AA:85:DD:89:F6:1D:F5:AF:9E:AB
         SHA1: DE:DE:86:59:05:6C:00:E8:CC:C0:16:D3:C2:68:BF:95:B8:83:E9:58

localhost.keystoreCN=unit-tests-client, OU=JBoss Inc., O=JBoss Inc., ST=Washington, C=US의 별명과 함께 인증서를 저장할 필요가 있으며 또한 jmx-console-roles.properties는 동일한 항목에 대한 항목을 필요로 합니다. DN은 보통 분리자(delimiter)로 취급되는 많은 문자들을 포함하기 때문에, 여러분은 다음에서 보여지는 것처럼 백슬래쉬('\')를 사용하여 이스케이프 문자 문제를 해결해줄 필요가 있습니다:

# UsersRolesLoginModule과 함께 사용하기 위한 roles.properties 파일의 샘플
CN\=unit-tests-client,\ OU\=JBoss\ Inc.,\ O\=JBoss\ Inc.,\ ST\=Washington,\ C\=US=JBossAdmin
admin=JBossAdmin

8.4.6.6. org.jboss.security.auth.spi.ProxyLoginModule

ProxyLoginModule은 현재의 쓰레드 컨텍스트 클래스 로더를 사용하여 LoginModule 위임을 로딩하는 로그인 모듈입니다. 이 모듈의 목적은 시스템의 클래스패쓰[2]상에 LoginModule을 필요로하는 현재의 JAAS 1.0 클래스 로더 한계사항을 해결하기 위함입니다. 일부 커스텀 LoginModule에서는 JBoss 서버의 lib/ext 디렉터리로부터 로딩되어지는 클래스들을 사용하며 시스템의 클래스경로상에 LoginModule이 위치해있다면 사용할 수 없습니다. 이러한 한계상황을 해결하기 위해 여러분은 ProxyLoginModule을 사용하여 커스텀 LoginModule을 띄워줄 수 있습니다. ProxyLoginModulemoduleName라 불리는 설정 옵션을 필요로 합니다. 로딩되어져야하는 LoginModule 구현의 완전한 형태의 클래스 이름을 지정합니다. 갯수에 상관없이 추가적인 설정 옵션들이 지정될 수 있으며, 이것들은 로딩된(bootstrapped) 로그인 모듈쪽으로 넘겨지게 됩니다.

예제에서는 JBoss의 lib/ext 디렉터리로부터 로딩된 일부 서비스를 사용하는 커스텀 로그인 모듈을 생각해보겠습니다. 커스텀 로그인 모듈의 클래스 이름은 com.biz.CustomServiceLoginModule 입니다. 로딩된 이 커스텀 로그인 모듈에 대한 ProxyLoginModule 설정 항목에 대한 알맞은 썬의 레가시 포맷은 다음과 같습니다:

testProxy {
    org.jboss.security.auth.spi.ProxyLoginModule required
    moduleName=com.biz.CustomServiceLoginModule
    customOption1=value1
    customOption2=value2
    customOption3=value3;
};

이에 대응되는 XMLLoginConfig 포맷은 다음과 같습니다:

<policy>
    <application-policy name="testProxy">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.ProxyLoginModule"
                          flag="required">
                <module-option name="moduleName">
                    com.biz.CustomServiceLoginModule
                </module-option>
                <module-option name="customOption1">value1</module-option>
                <module-option name="customOption2">value2</module-option>
                <module-option name="customOption3">value3</module-option>
            </login-module>
        </authentication>
    </application-policy>
</policy>

8.4.6.7. org.jboss.security.auth.spi.RunAsLoginModule

JBoss는 인증의 로그인 상태사이에서 역할로써 실행되도록 하고 commit 또는 취소(abort) 상태중에 한 역할로써 동작되어지는 RunAsLoginModule라고 불리는 헬퍼(helper) 로그인 모듈을 갖고 있습니다. 이 로그인 모듈의 목적은 다른 로그인 모듈에서 각자의 인증을 수행하도록 하기위해 보호되는 리소스들로의 액세스에 필요로 하는 역할을 제공하는 것입니다. 이에 대한 예로는 보호되는 EJB로의 액세스를 위한 로그인 모듈을 생각해볼 수 있습니다. 이 로그인 모듈은 성립된 역할로써 실행될 필요가 있는 로그인 모듈(들)의 설정에 앞서야만 합니다.

로그인 모듈의 설정 옵션은 다음의 하나만 존재합니다:

  • roleName: 로그인 상태(phase)중 역할로써 실행되어지는데 사용하는 역할 이름입니다. 지정하지 않았다면, 기본값인 nobody가 사용됩니다.

8.4.6.8. org.jboss.security.ClientLoginModule

ClientLoginModule는 JBoss 클라이언트에서 호출자(caller)의 식별과 자격증명(credential)의 성립을 위해 사용하는 LoginModule의 구현(implementation)입니다. 이것은 org.jboss.security.SecurityAssociation.principalcallbackhandler에 의해 채워진 NameCallback의 값으로, org.jboss.security.SecurityAssociation.credentialcallbackhandler에 의해 채워진 PasswordCallback 값으로 간단히 설정합니다. 이것은 현재의 쓰레드 호출자에 연결되도록 하는 클라이언트를 위해 지원되는 단 한가지 메커니즘입니다. 보안 환경이 JBossSX를 투명하게 사용하도록 설정되지 않은 JBoss의 EJB 클라이언트처럼 동작하는 독립형(stand-alone) 클라이언트 어플리케이션과 서버 환경 모두에서 ClientLoginModule을 사용해야할 필요가 있습니다. 물론 여러분은 항상 직접 org.jboss.security.SecurityAssociation 정보를 설정해줄 수도 있으나 경고없이 변경할 수 있도록 해주는 내부 API로 간주되어 집니다.

이 로그인 모듈에서는 어떠한 인증도 수행하지 않는다는 것에 주의해야 합니다. 이것은 단순히 서버쪽에서의 일련의 인증을 위한 JBoss 서버의 EJB 호출 계층쪽으로 제공되어지는 로그인 정보를 복사합니다. 사용자의 클라이언트쪽에서의 인증을 수행할 필요가 있다면, 여러분은 ClientLoginModule에 더하여 또 다른 로그인 모듈을 설정해주어야 할 필요가 있습니다.

지원되는 로그인 모듈 설정 옵션들은 다음과 같습니다:

  • multi-threaded=true|false: multi-threaded 옵션이 true로 설정되면, 각각의 로그인 쓰레드는 자신만의 principal과 credential 저장소를 갖습니다. 이것은 여러 사용자를 별도의 쓰레드에서 식별하는 클라이언트 환경에서 유용합니다. true인 경우 각각의 별도 쓰레드는 자신의 로그인을 수행해야 합니다. false로 설정하면, 로그인 식별과 자격증명들은 VM내의 모든 쓰레드에 적용되는 전역 변수가 됩니다. 이 옵션의 기본값은 false 입니다.
  • password-stacking=useFirstPass: password-stacking 옵션을 설정하면, 이 모듈은 먼저 로그인 모듈의 공유된 상태 맵내에서 각각 javax.security.auth.login.namejavax.security.auth.login.password를 사용하여 공유된 사용자명과 암호를 찾습니다. 이를 통해 JBoss쪽으로 넘겨져야만 하는 올바른 사용자명과 암호를 준비할 수 있도록 하는 작업을 모듈 설정 이전에 하도록 합니다. 여러분은 LdapLoginModule과 같은 또 다른 로그인 모듈을 사용하여 클라이언트에 대한 클라이언트측 인증을 수행하도록 원하는 경우에 이 옵션을 사용할 수 있습니다.
  • restore-login-identity=[true|false]: restore-login-identity를 true로 설정하면 login() 메쏘드쪽에 보여지는 항목인 SecurityAssociation의 principal과 credential은 취소되거나 로그아웃되면 저장되었다가 다시 복구되어집니다. false(기본값)로 설정하면, 취소하거나 로그아웃하면 SecurityAssociation은 그냥 지워집니다. 어떤 한 사람이 자신의 신원(identity) 변경한 후 원래의 호출자 신원으로 복구할 필요가 있다면 restore-login-identity를 true로 설정합니다.

ClientLoginModule에 대한 간단한 로그인 환경은 JBoss의 배포판에서의 client/auth.conf 파일내의 기본 설정 엔트리입니다. 설정은 다음과 같습니다:

other {
    // JBoss없이 동작하는 여러분의 로그인 모듈을 이곳에 넣어줍니다.
                
    // JBoss LoginModule
    org.jboss.security.ClientLoginModule required;
               
    // JBoss를 필요로 하는 여러분의 로그인 모듈을 이곳에 넣어줍니다. 
};  

8.4.7. 커스텀 로그인 모듈 만들기

JBossSX 프레임워크에 번들된 로그인 모듈이 여러분의 보안 환경에서 동작하지 않는다면, 동작할 수 있는 로그인 모듈 구현을 직접 제작해주어야 합니다.

JaasSecurityManager가 특정한 Subject principal 셋의 사용법 패턴을 요구한다는 것을 JaasSecurityManager 아키텍쳐를 언급했던 섹션에서 다루었다는 것을 기억해보십시오. 여러분은 JaasSecurityManager와 함께 동작하는 로그인 모듈을 만들기 위해서 JAAS Subject 클래스의 정보 저장소 기능과 요구되는 기능들에 대한 사용법을 숙지해야 할 필요가 있습니다. 이번 섹션에서는 여러분이 직접 자신의 로그인 모듈을 작성하는데 필요로 하는 사항들을 점검해보고 구현에 도움이 되는 두 개의 추상(abstract) 기반 LoginModule 구현을 소개하도록 하겠습니다.

여러분은 다음에 오는 메쏘드를 사용하여 여섯가지 방법으로 Subject와 관련된 보안 정보를 얻을 수 있습니다:

java.util.Set getPrincipals()
java.util.Set getPrincipals(java.lang.Class c)
java.util.Set getPrivateCredentials()
java.util.Set getPrivateCredentials(java.lang.Class c)
java.util.Set getPublicCredentials()
java.util.Set getPublicCredentials(java.lang.Class c)

Subject의 신원과 역할에 대해 JBossSX는 가장 자연스러운 선택을 하게됩니다: principal 셋은 getPrincipals()getPrincipals(java.lang.Class)를 통해 얻어집니다. 사용법 패턴은 다음과 같습니다:

  • 사용자의 신원(사용자명, 주민등록번호, 직원번호등등)은 SubjectPrincipals 셋내에서 java.security.Principal 객체로 저장되어 집니다. 사용자의 신원을 표시하는 Principal 구현은 반드시 principal의 이름에 대한 비교와 동등성에 기반을 두어야만 합니다. 적절한 구현은 org.jboss.security.SimplePrincipal 클래스로써 사용이 가능합니다. 다른 Principal 인스턴스들은 필요에 따라서 SubjectPrincipals 셋에 추가될 수도 있습니다.
  • 할당된 사용자 역할은 또한 Principals 셋내에 저장됩니다만 java.security.acl.Group 인스턴스를 사용하여 이름이 붙여진 역할 셋내에 그룹으로 묶여집니다. Group 인터페이스는 Principal 과/혹은 Group에 대한 컬렉션을 정의하며, java.security.Principal의 서브인터페이스입니다. 갯수에 상관없이 역할 셋은 하나의 Subject에 할당될 수 있습니다. 현재 JBossSX 프레임워크는 RolesCallerPrincipal라는 이름을 갖는 두 개의 널리 알려진 역할 셋을 사용합니다. Roles 그룹은 Subject가 인증되어진 어플리케이션 도메인내에 알려져 있는 이름을 갖는 역할에 대한 Principal의 컬렉션입니다. 이 역할 셋은 EJBContext.isCallerInRole(String)와 유사한 메쏘드들에 의해 사용됩니다. 여기서 EJB는 이름이 붙여진 어플리케이션 도메인의 역할에 속한 현재의 호출자인지를 확인하기 위해 사용할 수 있습니다. 또한 메쏘드 인가 유무를 체크하는 기능을 수행하는 보안 인터셉터 로직은 이 역할 셋을 사용합니다. CallerPrincipalGroup은 어플리케이션 도메인내에서 사용자에게 할당된 단일 Principal 신원으로 구성됩니다. EJBContext.getCallerPrincipal() 메쏘드는 CallerPrincipal을 사용하여 어플리케이션 도메인을 운영환경내의 신원에서 어플리케이션을 위해 적절한 사용자 신원으로 매핑할 수 있도록 합니다. SubjectCallerPrincipalGroup을 갖지 않을 경우, 어플리케이션 신원(identity)은 운영 환경 신원과 동일합니다.

8.4.7.1. Subject 사용법 패턴을 위한 지원

앞 섹션에서 언급했던 Subject 사용법 패턴의 올바른 구현을 간소화시키기위해, JBossSX에는 올바른 Subject의 사용을 준수시키는 템플릿 패턴을 갖는 인증된 Subject의 생성을 처리하는 두 개의 추상 로그인 모듈이 포함되어 있습니다. 이 두개의 가장 공통적인 것은 org.jboss.security.auth.spi.AbstractServerLoginModule 클래스입니다. javax.security.auth.spi.LoginModule 인터페이스의 구체적 구현을 제공하며, 운영 환경에서의 보안 인프라구조에 적합한 핵심 업무를 위한 이상적인(abstract) 메쏘드를 제공합니다. 클래스의 핵심사항들은 다음에 오는 클래스의 일부분중에 표시되어 있습니다. JavaDoc에서 서브클래스의 책무에 대한 세부사항을 언급하고 있습니다.

package org.jboss.security.auth.spi;
/**
 *  이 클래스는 JAAS 서버쪽 LoginModule에서 요구되는 범용적인 기능성을 구현하고
 *  신분과 역할을 저장하는 JBossSX 표준 Subject 사용법 패턴을 구현합니다. 
 *  여러분이 직접 LoginModule을 만들기위해서는 이 모듈을 서브클래스로 하고, 
 *  login(), getRoleSets() 및 getIdentity() 메쏘드를 덮어쓰십시오. 
 */
public abstract class AbstractServerLoginModule
    implements javax.security.auth.spi.LoginModule
{
    protected Subject subject;
    protected CallbackHandler callbackHandler;
    protected Map sharedState;
    protected Map options;
    protected Logger log;

    /** 공유된 credential을 사용해야하는가를 지정하는 플래그  */
    protected boolean useFirstPass;
    /** 
     * 로그인 상태(phase)가 성공했는지를 가르키는 플래그. 
     * login 메쏘드를 덮어쓴 서브클래스에서는 로그인이 성공적으로
     * 완료된 경우 이 것을 true로 설정해야만 합니다.
     */
    protected boolean loginOk;
                
    // ...
    /**
     * login 모듈을 초기화합니다. 이것은 로그인 세션을 위한 
     * subject, callbackHandler 그리고 sharedState 및 options
     * 을 저장합니다. 서브클래스 자신의 옵션을 처리해야 한다면 
     * 서브클래스도 덮어써주어야(override) 합니다.
     * 덮어쓰는 경우에는 반드시 super.initialize(...) 를 호출해야만 
     * 합니다. 
     *
     * <p>
     * 옵션들은 <em>password-stacking</em> 매개변수를 위해 점검되어집니다. 
     * 이 값이 "useFirstPass"로 설정되면, 로그인 신분은 sharedState 맵의 
     * <code>javax.security.auth.login.name</code> 값으로부터 가져오게 되고 
     * sharedState 맵의 <code>javax.security.auth.login.password</code> 값으로부터 
     * 신분의 증명을 합니다. 
     *
     * @param subject 로그인이 성공한 후 갱신되어야 하는 Subject.
     * @param callbackHandler 사용자의 신분과 자격증명을 획득하는데 사용하는 CallbackHandler .
     * @param sharedState 설정된 모든 로그인 모듈의 인스턴스사이에서 공유되는 맵.
     * @param options 로그인 모듈에 넘겨지는 매개변수들. 
     */
    public void initialize(Subject subject,
                           CallbackHandler callbackHandler,
                           Map sharedState,
                           Map options)
    {
        // ...
    }
    

    /**
     *  useFirstPass 옵션이 true인 경우, sharedState 맵내에서 
     *  javax.security.auth.login.name 과 javax.security.auth.login.password 값을 찾고, 
     *  존재한다면 true를 리턴합니다. 찾지못한다거나 null 이라면 이 메쏘드는 
     *  false를 반환합니다. 
     *  login 메쏘드를 덮어쓴 서브클래스에서는 로그인이 성공하여 
     *  Subject를 생성하기 위한 commit 상태인 경우 
     *  loginOk 변수가 true로 설정되어져야만 합니다. 
     *  이 구현에서는 login() 메쏘드가 true를 리턴하는 경우, 
     *  loginOk를 true로 설정해줍니다. 이외의 경우에는 
     *  loginOk를 false로 설정합니다. 
     */
    public boolean login() 
        throws LoginException
    {
        // ...
    }
    
    /**
     *  사용자의 주 신분에 대응되는 Principal를 
     *  리턴하기 위해 서브클래스에 의해 덮어쓰여짐.
     */
    abstract protected Principal getIdentity();
                
    /**
     *  사용자에게 할당된 역할 셋에 대응되는 Groups을 리턴하기위해 
     *  서브클래스에 의해 덮어씌여짐. 
     *  서브클래스는 최소한 사용자에게 할당된 역할을 포함하고 있는 
     *  "Roles" 라는 이름이 붙은 그룹을 만들어야 합니다. 
     *  두 번째 일반적인 그룹은 "CallerPrincipal" 로써, 
     *  보안 도메인의 신분보다는 사용자의 어플리케이션 신분을 
     *  제공하게 됩니다. 
     * 
     *  @역할 셋을 포함하는 Group[]을 반환합니다.
     */
    abstract protected Group[] getRoleSets() throws LoginException;
}

여러분은 loginOk 인스턴스 변수에 주의를 기울일 필요가 있습니다. 로그인이 성공한 경우 이 변수는 반드시 true로 설정되어져야만 하고, 그 이외의 경우에서는 login 메쏘드를 덮어쓰는 어떠한 서브클래스에 의해서 false가 됩니다. 이 변수를 올바르게 설정하는 것에 실패한다면 commit 메쏘드는 호출되어져야 할때 Subject를 업데이트하지 않거나 호출되어지지 않아야 할때 Subject를 업데이트하는 결과를 초래합니다. 로그인 상태에서 나타나는 결과들을 추적하는 것은 로그인을 성공적으로 수행하도록 하는 성공된 로그인 모듈에서 필요로 하지 않는 컨트롤 플래그와 함께 연결되어지는 로그인 모듈을 허용하도록 추가되어집니다.

커스텀 로그인 모듈에 적합한 두 번째 추상 기반의 로그인 모듈은 org.jboss.security.auth.spi.UsernamePasswordLoginModule 입니다. 로그인 모듈은 사용자의 신분으로써 문자열 기반의 사용자명과 인증 자격증명으로써의 char[] 암호로 맞춰줌으로써 커스텀 로그인 모듈 구현을 보다 간단하게 합니다. 또한 anonyous 사용자(사용자명과 암호가 null로 가르켜지는)를 역할없는 Principal에 매핑시키는 것까지도 지원합니다. 클래스의 중요한 세부 사항들은 다음에 오는 클래스 코드의 일부분내에 표시되어있습니다. JavaDoc에서는 서브클래스의 세부적인 요구사항에 대해 언급하고 있습니다.

package org.jboss.security.auth.spi;

/**
 *  신원(identity)이 부여된 AbstractServerLoginModule의 추상 서브클래스 
 *  == String username, credentials ==
 *  로그인 절차상에서의 String password 뷰.
 *  서브클래스는 사용자에 대한 기대되는 암호와 역할을 반환하기위해 
 *  getUsersPassword() 와 getUsersRoles() 을 덮어씁니다. 
 */
public abstract class UsernamePasswordLoginModule
    extends AbstractServerLoginModule
{
    /** 로그인 신원(identity) */
    private Principal identity;
    /** 로그인 신원의 증명 */
    private char[] credential;
    /** 빈 사용자명과 암호가 보일때 사용하는 principal  */
    private Principal unauthenticatedIdentity;

    /**
     * 암호를 해쉬하는데 사용하는 메시지 다이제스트 알고리즘. 
       널이면 평문 암호가 사용됩니다.  */
    private String hashAlgorithm = null;

    /**
     *  암호 문자열을 바이트 배열로 변환시킬 때 사용하는 문자셋/인코딩 이름. 
     *  기본값은 플랫폼의 기본 인코딩이 됩니다. 
     */
     private String hashCharset = null;

    /** 사용할 문자열 인코딩 포맷. 기본값은 base64 입니다. */
    private String hashEncoding = null;
                
    // ...
                
    /** 
     *  unauthenticatedIdentity 속성을 찾기위한 슈퍼클래스 메쏘드 덮어쓰기. 
     *  이 메쏘드는 우선 super 버전을 호출합니다. 
     *
     *  @param 옵션,
     *  @option unauthenticatedIdentity: 
     *  빈 사용자명과 암호가 나타날 때 principal의 이름을 할당하고 인증함.
     */
    public void initialize(Subject subject,
                           CallbackHandler callbackHandler,
                           Map sharedState,
                           Map options)
    {
        super.initialize(subject, callbackHandler, sharedState,
                         options);
        // unauthenticatedIdentity 옵션을 점검.
        Object option = options.get("unauthenticatedIdentity");
        String name = (String) option;
        if (name != null) {
            unauthenticatedIdentity = new SimplePrincipal(name);
        }
    }
                
    // ...
                
    /**
     *  입력된 암호가 기대되는 암호에 대해 올바른지를 검증하는 것을 
     *  변경하는 것을 가능케 해주는 서브클래스에 대한 훅.
     *  이 버전은 inputPassword 혹은 expectedPassword가 널값이 아닌지와 
     *  inputPassword.equals(expectedPassword)가 true인지를 확인합니다. 
     *
     *  @inputPassword가 올바르면 true를, 그렇치않으면 false를 리턴합니다.
     */
    protected boolean validatePassword(String inputPassword,
                                       String expectedPassword)
    {
        if (inputPassword == null || expectedPassword == null) {
            return false;
        }
        return inputPassword.equals(expectedPassword);
    }
    
    /**
     *  getUsername() 메쏘드를 통해 사용가능한 현재 사용자명에 대한 
     *  기대되는 암호를 얻습니다. 이것은 CallbackHandler가 사용자명(username)과 
     *  후보자(candidate) 암호를 반환한 후 login() 메쏘드내에서 호출되어집니다. 
     *
     * @올바른 암호 문자열이 리턴됩니다. 
     */
    abstract protected String getUsersPassword()
        throws LoginException;
}

UsernamePasswordLoginModule에 대한 AbstractServerLoginModule의 서브클래싱 선택은 여러분이 로그인 모듈로 작성한 인증 기술에서 사용할 수 있는 String 기반의 사용자명과 String 자격증명(credential)에 단순하게 기반을 두고 있습니다. 문자열 기반의 문법(semantic)이 올바르다면 UsernamePasswordLoginModule 서브클래스를 그렇치않다면 AbstractServerLoginModule 서브클래스를 사용합니다.

여러분이 커스텀 로그인 모듈을 작성할 때 수행에 필요로 하는 단계는 여러분이 어떤 로그인 모듈 클래스를 기반으로 하고 있느냐에 따라 다음과 같이 요약될 수 있습니다. 여러분의 보안 인프라구조에 통합되는 커스텀 로그인 모듈을 작성할 때라면, 여러분의 로그인 모듈이 JBossSX 보안 관리자에서 요구하고 있는 형태내에서 인증된 Principal 정보를 제공하는 지를 확실히 하기위해 AbstractServerLoginModule 혹은 UsernamePasswordLoginModule 서브클래싱으로부터 시작해야만 합니다.

AbstractServerLoginModule의 서브클래싱 경우, 여러분은 다음과 같은 사항들을 덮어쓸 필요가 있습니다:

  • void initialize(Subject, CallbackHandler, Map, Map): 파싱할 커스텀 옵션이 있는 경우.
  • boolean login(): 인증 활동을 수행하기 위함. 로그인이 성공하면 loginOk 인스턴스 변수가 true로 설정되고, 실패하면 false로 설정되는지 주의하십시오.
  • Principal getIdentity(): log() 단계에 의해 인증되는 사용자를 위한 Principal 객체를 반환하기 위함.
  • Group[] getRoleSets(): login() 수행동안 인증된 Principal에 할당되는 역할을 포함하는 Roles 이라는 이름을 갖는 Group을 최소한 하나 이상 리턴시키기 위함. 두번째 공통 GroupCallerPrincipal라는 이름을 갖으며 보안 도메인 신원보다는 사용자의 어플리케이션 신원을 제공합니다.

UsernamePasswordLoginModule을 서브클래싱하는 경우, 다음과 같은 것들을 덮어써줄 필요가 있습니다:

  • void initialize(Subject, CallbackHandler, Map, Map): 파싱할 커스텀 옵션을 갖는 경우.
  • Group[] getRoleSets(): login() 동안 인증된 Principal쪽에 할당된 역할을 포함하는 Roles이라는 이름의 Group을 최소한 하나 이상 리턴하기 위함. 두 번째 일반 Group의 이름은 CallerPrincipal 이며, 보안 도메인 신원보다는 사용자의 어플리케이션 신원을 제공합니다.
  • String getUsersPassword(): getUsername() 메쏘드를 통해 사용가능한 현재 사용자명에 대한 요구되는 암호를 리턴하기 위함. getUsersPassword() 메쏘드는 callbackhandler가 사용자명과 후보자의 암호를 리턴해준 후 login() 메쏘드내에서 호출되어집니다.

8.4.7.2. 커스텀 LoginModule 예제

이번 섹션에서 우리는 하나의 커스텀 로그인 모듈 예제를 만들어보게 됩니다. 새로 만들어질 로그인 모듈의 예제는 UsernamePasswordLoginModule을 확장한 것이며, JNDI 룩업으로부터 사용자의 암호와 역할 이름을 가져오게 됩니다. 이러한 아이디어는 password/<username> 형태의 이름을 갖는 컨텍스트에 대한 룩업을 수행할 경우 사용자의 암호를 리턴해주는 JNDI 컨텍스트 때문입니다. 여기서 <username>은 현재 인증되어진 사용자입니다. 간단히 정리하자면, roles/<username> 형태의 룩업은 요청된 사용자의 역할을 반환합니다.

예제의 소스코드는 이 책에서 제공하고 있는 예제 디렉토리의 src/main/org/jboss/chap8/ex2 에서 찾을 수 있습니다. 예제 8.12, “ 커스텀 로그인 모듈 JndiUserAndPass”에서는 커스텀 로그인 모듈로 작성된 JndiUserAndPass의 소스 코드를 보여주고 있습니다. 이 모듈이 JBoss의 UsernamePasswordLoginModule을 확장한 것이기 때문에 JNDI 저장소로부터 사용자의 암호와 역할을 가져왔다는 것을 상기해주시기 바랍니다. JndiUserAndPass는 그 자체로는 JAAS LoginModule 오퍼레이션을 고려하지 않았습니다.

예제 8.12.  커스텀 로그인 모듈 JndiUserAndPass

package org.jboss.chap8.ex2;
                    
import java.security.acl.Group;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;

import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

/** 
 *  JNDI 룩업으로부터 사용자에 대한 암호와 역할을 가져오는 
 *  커스텀 로그인 모듈 예제
 *     
 *  @author Scott.Stark@jboss.org
 *  @version $Revision: 1.15 $
*/
public class JndiUserAndPass 
    extends UsernamePasswordLoginModule
{
    /** 암호/사용자명 룩업을 처리하는 컨텍스트의 JNDI 이름 */
    private String userPathPrefix;
    /** 역할/사용자명 룩업을 처리하는 컨텍스트의 JNDI 이름 */
    private String rolesPathPrefix;
    
    /**
     * userPathPrefix 와 rolesPathPrefix 옵션을 가져오기 위해 덮어씀.
     */
    public void initialize(Subject subject, CallbackHandler callbackHandler,
                           Map sharedState, Map options)
    {
        super.initialize(subject, callbackHandler, sharedState, options);
        userPathPrefix = (String) options.get("userPathPrefix");
        rolesPathPrefix = (String) options.get("rolesPathPrefix");
    }
    
    /**
     * rolesPathPrefix + '/' + super.getUsername() JNDI 로케이션을 질의함으로써 
     * 현재 사용자가 속해있는 역할을 가져옴..
     */
    protected Group[] getRoleSets() throws LoginException
    {
        try {
            InitialContext ctx = new InitialContext();
            String rolesPath = rolesPathPrefix + '/' + super.getUsername();

            String[] roles = (String[]) ctx.lookup(rolesPath);
            Group[] groups = {new SimpleGroup("Roles")};
            log.info("Getting roles for user="+super.getUsername());
            for(int r = 0; r < roles.length; r ++) {
                SimplePrincipal role = new SimplePrincipal(roles[r]);
                log.info("Found role="+roles[r]);
                groups[0].addMember(role);
            }
            return groups;
        } catch(NamingException e) {
            log.error("Failed to obtain groups for
                        user="+super.getUsername(), e);
            throw new LoginException(e.toString(true));
        }
    }
                    
    /** 
     * userPathPrefix + '/' + super.getUsername() JNDI 로케이션을 질의함으로써 
     * 현재 사용자의 암호를 가져옴.     .
     */
    protected String getUsersPassword() 
        throws LoginException
    {
        try {
            InitialContext ctx = new InitialContext();
            String userPath = userPathPrefix + '/' + super.getUsername();
            log.info("Getting password for user="+super.getUsername());
            String passwd = (String) ctx.lookup(userPath);
            log.info("Found password="+passwd);
            return passwd;
        } catch(NamingException e) {
            log.error("Failed to obtain password for
                        user="+super.getUsername(), e);
            throw new LoginException(e.toString(true));
        }
    }   
}

JNDI 저장소에 대한 세부사항은 org.jboss.chap8.ex2.service.JndiStore MBean에서 찾아볼 수 있습니다. 이 서비스는 javax.naming.Context 프록시를 JNDI쪽에 리턴해주는 ObjectFactory를 바인딩합니다. 프록시는 자신의 passwordroles에 대한 룩업 이름의 접두사를 점검하여 룩업 오퍼레이션을 처리합니다. password로 시작되는 이름인 경우, 사용자의 암호가 요청되어집니다. roles로 시작되는 이름인 경우, 사용자의 역할이 요청되어집니다. 예제 구현에서는 사용자명이 무엇이든 상관없이 항상 theduke라는 암호와 {"TheDuke", "Echo"}에 일치하는 역할의 이름 배열을 리턴합니다. 여러분은 여러분이 원하는 다른 구현을 테스트해볼 수 있습니다.

예제 코드에는 커스텀 로그인 모듈을 테스트할 수 있는 간단한 세션 빈을 포함하고 있습니다. 예제를 빌드하고 배치한 후 실행시키려면 예제 디렉터리에서 다음의 명령을 실행시켜 주십시오. 예제 실행전에는 반드시 JBoss 서버가 실행되고 있는지 확인해 주셔야 합니다. 예제 8.13, “chap8-ex2의 안전한 클라이언트 액세스 출력”에서 클라이언트에 대한 중요한 내용이 보여지며, 예제 8.14, “JndiUserAndPass에 대한 chap8-ex2의 서버측 특성(behavior)”에서는 서버측 오퍼레이션을 보여주고 있습니다.

예제 8.13. chap8-ex2의 안전한 클라이언트 액세스 출력

[nr@toki examples]$ ant -Dchap=chap8 -Dex=2 run-example
Buildfile: build.xml
...
run-example2:
     [copy] Copying 1 file to /tmp/jboss-3.2.6/server/default/deploy
     [echo] Waiting for 5 seconds for deploy...
     [java] [INFO,ExClient] Login with username=jduke, password=theduke
     [java] [INFO,ExClient] Looking up EchoBean2
     [java] [INFO,ExClient] Created Echo
     [java] [INFO,ExClient] Echo.echo('Hello') = Hello

예제 8.14. JndiUserAndPass에 대한 chap8-ex2의 서버측 특성(behavior)

17:48:11,458 INFO  [EjbModule] Deploying EchoBean2
17:48:11,890 INFO  [JndiStore] Start, bound security/store
17:48:11,896 INFO  [SecurityConfig] Using JAAS AuthConfig: jar:file:/private/tmp/jboss-3.2.6/s
erver/default/tmp/deploy/tmp22821chap8-ex2.jar-contents/chap8-ex2.sar!/META-INF/login-config.x
ml
17:48:12,355 INFO  [EJBDeployer] Deployed: file:/private/tmp/jboss-3.2.6/server/default/deploy
/chap8-ex2.jar

사용자에 대한 서버측 인증을 위해 커스텀 로그인 모듈인 JndiUserAndPass를 사용하려는 선택은 예제의 보안 도메인을 위한 로그인 설정에 의해 결정됩니다. EJB JAR META-INF/jboss.xml 서술자에서 보안 도메인을 설정하고 sar META-INF/login-config.xml 서술자에서 로그인 모듈 설정을 정의합니다. 이들 서술자는 예제 8.15, “chap8-ex2의 보안 도메인과 로그인 모듈 설정” 예제 8.16, “chap8-ex2 어플리케이션을 위한 login-config.xml 설정파일의 일부분”에 보여지고 있습니다.

예제 8.15. chap8-ex2의 보안 도메인과 로그인 모듈 설정

<?xml version="1.0"?>
<jboss>
    <security-domain>java:/jaas/chap8-ex2</security-domain>
</jboss>

예제 8.16. chap8-ex2 어플리케이션을 위한 login-config.xml 설정파일의 일부분

<application-policy name = "chap8-ex2">
    <authentication>
        <login-module code="org.jboss.chap8.ex2.JndiUserAndPass"
                      flag="required">
            <module-option name = "userPathPrefix">/security/store/password</module-option>
            <module-option name = "rolesPathPrefix">/security/store/roles</module-option>
        </login-module>
    </authentication>
</application-policy>   

8.4.8. DynamicLoginConfig 서비스

login-config.xml 파일에서 정의된 보안 도메인들은 필히 정적입니다. 여기서 정의된 보안 도메인들은 JBoss 서버 구동시 읽혀들여지지만, 새로운 보안 도메인을 추가하거나 기존의 것에 대해 정의를 변경하기 위한 쉬운 방법은 없습니다. DynamicLoginConfig 서비스는 동적으로 보안 도메인들을 배치시킬 수 있도록 합니다. 이를 통해 여러분은 JAAS 로그인 설정을 정적인 login-config.xml 파일의 편집이 아닌 배치(또는 standalone 서비스로써)의 일부분으로 지정합니다.

서비스는 다음과 같은 속성들을 지원합니다:

  • AuthConfig: JAAS 로그인 설정 파일로 사용될 리소스의 경로. 기본값은 login-config.xml 입니다.
  • LoginConfigService: 로딩을 위해 사용할 XMLLoginConfig 서비스의 이름. 이 서비스에서는 설정을 로딩하기 위해 반드시 String loadConfig(URL) 오퍼레이션을 지원해야 합니다.
  • SecurityManagerService: 등록된 보안 도메인을 지워주는데 사용되는 SecurityManagerService 이름. 이 서비스에서는 인수로 넘겨지는 보안 도메인을 지워줄 수 있도록 반드시 flushAuthenticationCache(String) 오퍼레이션을 지원해야만 합니다. 이것을 지정함으로써 서비스가 정지할 때 인증 캐쉬가 지워지도록 트리거됩니다.

다음은 DynamicLoginConfig 서비스를 사용하여 MBean을 정의해주는 예입니다.

<server>
    <mbean code="org.jboss.security.auth.login.DynamicLoginConfig" name="...">
        <attribute name="AuthConfig">login-config.xml</attribute>
        <!-- login-config.xml 설정의 동적인 처리를 지원해주는 서비스 -->
        <depends optional-attribute-name="LoginConfigService">
            jboss.security:service=XMLLoginConfig </depends>
        <!-- 서비스에 의해 등록된 도메인의 인증 캐쉬를 지워주기위해 서비스를 
        종료시킬 때 사용하려는 보안 관리 서비스를 옵션으로 지정할 수 있습니다.
      -->
        <depends optional-attribute-name="SecurityManagerService">
            jboss.security:service=JaasSecurityManager </depends>
    </mbean>
</server>

적절한 리소스 URL을 갖는 loadConfig을 호출함으로써 지정된 LoginConfigService MBean을 사용하여 지정된 AuthConfig 리소스를 로딩시키게 됩니다. 서비스가 종료되면 설정이 제거됩니다. 지정된 리소스는 XML 파일이거나 Sun의 JAAS 로그인 설정중에 하나가 될 수 있습니다.

8.5. 안전한 원격 암호(Secure Remote Password(SRP)) 프로토콜

SRP 프로토콜이란 인터넷 표준 워킹 그룹의 RFC2945에서 기술되어 있는 공인키 교환에 대한 구현입니다. RFC2945는 추상적으로 기술되어 있습니다:

이 문서에서는 보안 원격 암호(SRP) 프로토콜로 알려진 암호화된 강력한 네트윅 인증 메커니즘을 설명하고 있습니다. 이 메커니즘은 사용자가 제시한 암호를 사용하여 안전한 연결을 사용하기위해 적합하며, 재사용 가능한 암호와 관련된 전통적인 보안 문제를 제거합니다. 또한 이 시스템은 세션이 살아있는동안 보안 계층(프라이버시 및/또는 무결성 보호)의 사용이 가능하도록 함으로써 인증 처리동안 보안키의 교환을 수행합니다. 인증된 키 서비스와 인증서 인프라구조는 필요치 않으며 클라이언트는 장기사용되는 키를 저장하거나 관리할 필요가 없습니다. SRP에서는 기존의 요청-응답(challenge-response) 기술을 통해 보안성과 적용의 잇점때문에 안전한 암호 인증이 필요한 경우 이상적인 대체 방법을 제공하고 있습니다.

참고: RFC2945의 완전한 사양서는 http://www.rfc-editor.org/rfc.html에서 얻을 수 있습니다. SRP 알로리즘의 추가적인 정보와 그 발전사는 http://www-cs-students.stanford.edu/~tjw/srp/에서 찾을 수 있습니다.

SRP는 Diffie-Hellman 과 RSA와 같은 다른 공개 키 교환 알고리즘과 개념적으로나 보안적으로 유사합니다. SRP는 서버쪽에 존재하는 평문 암호를 필요로 하지 않는 방식내에서 간단히 강력한 암호를 사용할 수 있는 기반에서 출발했습니다. 이것은 클라이언트 인증서를 필요로 하며 대응되는 인증 관리 인프라구조를 필요로 하는 다른 공개 키 기반의 알고리즘방식과 구별됩니다.

알고리즘은 공개 키 교환 알고리즘으로 알려진 Diffie-Hellman 및 RSA와 비슷합니다. 공개 키 알고리즘의 개념은 여러분이 두 개의 키를 갖으며, 하나는 모든 사람이 사용가능한 공개키이고 또 다른 하나는 여러분만이 알수 있는 사설 키가 존재한다는 것입니다. 만약 어떤 사람이 당신에게 암호화된 정보를 보내고 싶은 경우, 당신의 공개키를 사용해서 정보를 암호화 합니다. 그리고 당신만이 갖고 있는 사설키를 사용해서 암호화된 정보를 풀수 있습니다. 이것은 전통적인 공유 암호 기반의 암호화 스키마에서 보내는 사람과 받는 사람이 모두 동일한 암호를 사용하는 것과 구별됩니다. 공개 키 알고리즘에서는 공유된 암호의 필요성을 제거하였습니다. 공개키 알고리즘 뿐만 아니라 다른 암호화 알고리즘에 대한 보다 상세한 정보는 브루스 쉬니어씨가 쓴 Applied Cryptography, Second Edition, ISBN 0-471-11709-9 책을 참조하십시오.

JBossSX 프레임워크에는 다음과 같은 요소들로 구성된 SRP의 구현이 포함되어 있습니다:

  • 특정한 클라이언트/서버 프로토콜과 독립적인 SRP 교환 프로토콜의 구현.
  • 기본 클라이언트/서버 SRP 구현으로써 교환(handshake) 프로토콜의 RMI 구현.
  • 안전성을 위해 클라이언트 인증에 사용되는 RMI 구현을 이용하는 클라이언트측 JAAS LoginModule 구현.
  • RMI 서버 구현을 관리하기 위한 JMX MBean. 이 MBean을 통해 RMI 서버 구현을 JMX 프레임워크에 플러그인 시킬 수 있으며, 검증 정보 저장소에 대한 설정을 외부화(externalize) 시킬 수 있습니다. 또한 JBoss 서버의 JNDI 네임스페이스쪽에 연결된 인증 캐쉬를 구축(establish)할 수 있습니다.
  • SRP JMX MBean에 의해 관리되는 인증 캐쉬를 사용하는 서버측 JAAS LoginModule 구현.

그림 8.10, “SRP 클라이언트-서버 프레임워크의 JBossSX 컴포넌트”에서는 SRP 클라이언트/서버 프레임워크의 JBossSX 구현내에 참여하고 있는 중요 컴포넌트들의 다이어그램이 제공됩니다.

SRP 클라이언트-서버 프레임워크의 JBossSX 컴포넌트.

그림 8.10. SRP 클라이언트-서버 프레임워크의 JBossSX 컴포넌트.

클라이언트쪽에서 보면, SRP는 org.jboss.security.srp.SRPServerInterface 프록시를 통해 인증 서버와 통신하는 하나의 커스텀 JAAS LoginModule 구현입니다. 클라이언트는 org.jboss.security.srp.jaas.SRPLoginModule을 포함하는 로그인 설정 항목을 생성시켜 줌으로써 SRP를 사용한 인증을 사용가능하게 됩니다. 이 모듈은 다음과 같은 설정 옵션들을 지원하고 있습니다:

  • principalClassName: 이 옵션은 더이상 지원되지 않습니다. 이제 principal 클래스는 항상 org.jboss.security.srp.jaas.SRPPrincipal이 됩니다.
  • srpServerJndiName: SRP 인증 서버와 통신하기 위해 사용되는 SRPServerInterface 객체의 JNDI 이름. srpServerJndiNamesrpServerRmiUrl 두 옵션 모두 지정되었다면, srpServerRmiUrl에 앞서 srpServerJndiName으로 시도합니다.
  • srpServerRmiUrl: SRP 인증 서버와 통신을 위해 사용되는 SRPServerInterface 프록시의 로케이션에 대한 RMI 프로토콜 URL 문자열입니다.
  • externalRandomA: 클라이언트의 공개키 A의 랜덤 컴포넌트가 사용자의 콜백(callback)으로부터 와야하는지를 지시해주는 true/false 플래그입니다. 이 값은 하드웨어 토큰과 같은 강력한 암호화된 랜덤 숫자 입력을 사용할 수 있습니다.
  • hasAuxChallenge: 검증을 위해 서버에 대한 추가적인 챌런지로써 서버쪽에 전송되어질 문자열을 지시하는 true/false 플래그. 클라이언트 세션이 암호화된 부호(cipher)를 지원하는 경우, 임시 부호는 세션 사설 키를 사용하여 생성되고 챌런지 객체가 javax.crypto.SealedObject로써 전송되어집니다.
  • multipleSessions: 해당 클라이언트가 동시에 활성화된 다수의 SRP 로그인 세션을 갖을 수 있는지를 가르키는 true/false 플래그.

앞에서 보여지는 이름을 갖는 옵션과 일치하지 않는 또 다른 옵션이 넘어올 경우에는 JNDI 속성으로 처리되어 IntialContext 컨스트럭쳐쪽으로 넘겨진 환경변수로 사용되게 됩니다. 이것은 기본 IntialContext에서 SRP 서버 인터페이스가 사용가능 하지 않을 경우 유용합니다.

SRPLoginModule은 안전한 J2EE 컴포넌트로의 액세스를 검증할 때 사용되어질 수 있는 SRP 인증 자격증명(credential)이 가능하도록 하기위해 표준 ClientLoginModule에 따라 설정되어질 필요가 있습니다. 데모를 위한 로그인 설정 항목의 예제는 다음과 같이 설정됩니다:

srp {
    org.jboss.security.srp.jaas.SRPLoginModule required
    srpServerJndiName="SRPServerInterface"
    ;
            
    org.jboss.security.ClientLoginModule required
    password-stacking="useFirstPass"
    ;
};  

JBoss 서버쪽에서는 SRP 서버를 올바르게 구성하는 객체를 관리하기 위해 두 개의 MBean을 갖고 있습니다. 주(primary) 서비스는 org.jboss.security.srp.SRPService MBean으로 SRPServerInterface의 RMI 액세스가 가능한 버전을 노출시켜주는 것 뿐만 아니라 SRP 인증 세션 캐쉬를 갱신시켜줄 수 있도록 하는 임무를 갖습니다. 설정가능한 SRPService MBean 속성에는 다음의 것들이 있습니다:

  • JndiName: SRPServerInterface 프록시가 사용가능해야만 하는 JNDI 이름. 직렬화가 가능한(serializable) 동적인 프록시를 SRPServerInterface쪽에 연결시키는 SRPService의 로케이션. 지정되어 있지 않다면, srp/SRPServerInterface가 기본값이 됩니다.
  • VerifierSourceJndiName: SRPService에서 사용되어져야만 하는 SRPVerifierSource 구현의 JNDI 이름. 지정되어 있지 않다면, srp/DefaultVerifierSource가 기본값이 됩니다.
  • AuthenticationCacheJndiName: 연결된 캐쉬된 인증 정보를 위해 사용되어지는 인증 org.jboss.util.CachePolicy 구현 아랫쪽 JNDI 이름. SRP 세션 캐쉬는 이 바인딩을 통해 사용이 가능해지게 됩니다. 지정하지 않으면 기본값은 srp/AuthenticationCache입니다.
  • ServerPort: SRPRemoteServerInterface를 위한 RMI 포트 번호. 지정하지 않으면 기본값은 10099번 입니다.
  • ClientSocketFactory: SRPServerInterface를 노출시키는 과정에서 사용되는 옵션 커스텀 java.rmi.server.RMIClientSocketFactory 구현 클래스의 이름. 지정되어 있지 않으면 기본값으로 RMIClientSocketFactory가 사용됩니다.
  • ServerSocketFactory: SRPServerInterface의 노출과정에서 사용되는 옵션 커스텀 java.rmi.server.RMIServerSocketFactory 구현 클래스의 이름. 지정되어 있지 않으면 기본값으로 RMIServerSocketFactory가 사용됩니다.
  • AuthenticationCacheTimeout: 초단위로 캐쉬 정책의 타임아웃을 지정합니다. 지정되어 있지 않으면, 기본값은 1800초(30분)이 됩니다.
  • AuthenticationCacheResolution: 시간 제한 캐쉬 정책의 분석(resolution)을 초단위로 지정합니다. 이것은 타임아웃 확인사이의 간격을 제어합니다. 지정되어 있지 않을 경우, 기본값으 60초(1분)입니다.
  • RequireAuxChallenge: 클라이언트가 검증 상태의 일부분으로 보조적인 챌런지를 제공해야만 할때 지정합니다. 클라이언트에서 사용되는 SRPLoginModule 설정에서 useAuxChallenge 옵션이 활성화되어져야만 제어가 가능합니다.
  • OverwriteSessions: 기존 세션에 대해 사용자 인증이 성공하면 현재의 세션을 덮어쓰도록 할지를 가리키는 플래그입니다. 이것은 클라이언트가 사용자 모드들에 개별적으로 여러 세션을 사용할 수 없는 경우 서버의 SRP 세션 캐쉬의 특성을 제어합니다. 기본값은 false이며, false인 경우 사용자에 의해 두번째 시도에 대한 인증이 성공되었더라도 SRP 세션의 결과는 이전 SRP 세션 상태를 덮어쓰지 않게 됩니다.

inpu 설정중 하나는 VerifierSourceJndiName 속성입니다. 이것은 JNDI를 통해 제공받아야만 하고 구성이 가능해야하는 SRP 암호 정보 저장 구현의 로케이션입니다. org.jboss.security.srp SRPVerifierStoreService는 예제 MBean 서비스로써 영속적인 저장소로써 직렬화(serialized)된 객체들의 파일을 사용하는 SRPVerifierStore 인터페이스의 구현입니다. 비록 실제 환경에 적용하기는 무리가 따르지만, SRP 프로토콜의 테스트와 SRPVerifierStore 서비스를 위한 요구사항의 예로는 활용할 수 있습니다. 설정가능한 SRPVerifierStoreService MBean 속성에는 다음과 같은 것들이 있습니다.

  • JndiName: SRPVerifierStore 구현이 사용가능해야만 하는 JNDI 이름. 지정하지 않은 경우, 기본값은 srp/DefaultVerifierSource가 됩니다.
  • StoreFile: 직렬화된 객체 저장 파일의 사용자 암호 확인자(verifier) 로케이션. 이것은 URL 이나 클래스경로내에서 찾을 수 있는 리소스의 이름 중에 하나가 될 수 있습니다. 지정하지 않은 경우에 기본값은 SRPVerifierStore.ser가 됩니다.

또한 SRPVerifierStoreService MBean에서는 사용자를 추가하고 삭제하기 위한 addUserdelUser 오퍼레이션도 지원합니다. 사용방법(signatures)은 다음과 같습니다:

public void addUser(String username, String password) throws IOException;
public void delUser(String username) throws IOException;

이 서비스들의 예제 설정은 8.5., “안전한 원격 암호(SRP) 프로토콜”에서 제공됩니다.

8.5.1. SRP를 위한 암호 정보 제공하기

SRPVerifierStore 인터페이스의 기본 구현은 필요한 모든 암호들의 해쉬 정보로서 직렬화된 객체의 파일을 사용하도록 되어 있기 때문에 실제 여러분의 보안 적용 환경으로는 다소 적합하지 않습니다. 여러분은 기존의 보안 정보 저장소와 통합시킬 수 있는 SRPVerifierStore 인터페이스의 구현을 제공하는 MBean 서비스를 만들 필요가 있습니다. SRPVerifierStore 인터페이스는 다음과 같습니다.

예제 8.17. SRPVerifierStore 인터페이스

package org.jboss.security.srp;

import java.io.IOException;
import java.io.Serializable;
import java.security.KeyException;

public interface SRPVerifierStore
{
    public static class VerifierInfo implements Serializable
    {
        /**
         * 정보가 적용될 사용자명(username). 
         * 중복이 될지라도 객체 자신을 포함하도록 합니다. 
         */
        public String username;

        /** SRP 암호 검증자(verifier) 해쉬 */
        public byte[] verifier;
        /** 랜덤한 암호 salt는 암호를 검증하는데 원래 사용 */
        public byte[] salt;
        /** SRP 알고리즘 원시적 생성자(primitive generator) */
        public byte[] g;
        /** 알고리즘 safe-prime 계수(modulus) */
        public byte[] N;
    }
    
    /**
     *  가르켜지는 사용자의 암호 확인자 정보 가져오기.
     */
    public VerifierInfo getUserVerifier(String username)
        throws KeyException, IOException;
    /** 
     *  가르켜지는 사용자의 암호 확인자 정보 설정. 
     *  이것은 사용자의 암호를 변경하는 것과 동일하며 
     *  일반적으로 기존의 SRP 세션과 캐쉬를 무효화합니다.
     */
    public void setUserVerifier(String username, VerifierInfo info)
        throws IOException;

    /** 
     * 클라이언트로부터 서버쪽으로 전송된 옵션의 부수적인 챌런지 검증. 
     * 클라이언트에서 암호화되어 전송되었다면 auxChallaenge 객체는 
     * 해독되어 집니다. 부수적인 챌린지의 예로는 SRP 암호 교환을 
     * 보다 강화하기 위해 하드웨어 토큰을 검증하도록 하는 것입니다.      
     */
     public void verifyUserChallenge(String username, Object auxChallenge)
         throws SecurityException;
} 

SRPVerifierStore 구현의 중요한 기능은 주어진 사용자명에 대한 SRPVerifierStore.VerifierInfo 객체에 접근을 제공하는 것입니다. getUserVerifier(String) 메쏘드는 SRP 알고리즘에서 필요한 매개변수들을 얻기위해 사용자의 SRP 세션 시작시 SRPService에 의해 호출됩니다. VerifierInfo 객체의 요소들은 다음과 같습니다:

  • username: 로그인할때 사용하는 사용자의 이름이나 id.
  • verifier: 사용자가 자신의 신분의 증명을 위해 입력한 암호나 PIN의 단방향 해쉬입니다. org.jboss.security.Util 클래스는 암호를 해쉬하는 알고리즘을 수행하는 calculateVerifier 메소드를 갖고 있습니다. 아웃풋 암호는 RFC2945에 정의된 것 처럼 H(salt | H(username | ':' | password)) 입니다. 여기서 H 는 SHA 보안 해쉬 함수입니다. 사용자명은 문자열을 UTF-8 인코딩을 사용하여 byte[]로 변환합니다.
  • salt: 이것은 데이터베이스를 위협하려는 시도로 검증자(verifier) 암호 데이터베이스에 대해 무식한 방식으로 사전식 공격을 감행하기 힘들도록 복잡성을 증가시키는데 사용되는 랜덤 숫자입니다. 사용자의 기존 평문 암호를 해쉬할 때 암호학적으로 강력한 랜덤 숫자 알고리즘으로 생성시킨 값이 됩니다.
  • g: SRP 알고리즘 원시 생성자(primitive generator) 입니다. 보통 이것은 사용자당 설정보다는 잘 알려진 고정된 매개변수가 됩니다. org.jboss.security.srp.SRPConf 유틸리티 클래스에서는 SRPConf.getDefaultParams().g()를 통해 얻을 수 있는 괜찮은 디폴트값을 포함하는 g를 위한 몇몇 설정값을 제공합니다.
  • N: SRP 알고리즘의 safe-prime 계수(modulus) 입니다. 보통 이것은 사용자당 설정보다는 잘 알려진 고정 매개변수가 됩니다. org.jboss.security.srp.SRPConf 유틸리티에서는 SRPConf.getDefaultParams().N()를 통해 얻을 수 있는 괜찮은 디폴트값을 포함하는 N을 위한 몇몇 설정값을 제공합니다.

이제 여러분의 기존 암호 저장소를 통합하는 1 단계로 암호 정보의 해쉬된 버전을 생성합니다. 만약 여러분의 암호가 역행할 수 없는 해쉬된 형태로 저장되어 있다면, 예제에 대한 업그레이드 절차로써 사용자당 기반으로 밖에 구현할 수 없습니다. setUserVerifier(String, VerifierInfo) 메쏘드는 현재 SRPSerivce에 의해 사용되지 않으며 no-op 메쏘드로써 구현될 수 있거나 저장소가 읽기-전용이라는 예외를 던질 수도 있습니다.

2 단계로 여러분이 1 단계를 통해 생성한 저장소에서 VerifierInfo 을 어떻게 획득할지를 알려주는 커스텀 SRPVerifierStore 인터페이스 구현을 생성합니다. 인터페이스의 verifyUserChallenge(String, Object) 메쏘드는 클라이언트의 SRPLoginModule 설정이 hasAuxChallenge 옵션을 지정한 경우에만 호출됩니다. 이것은 SafeWord 나 Radius 같은 기존의 하드웨어 토큰 기반의 스키마를 SRP 알고리즘에 통합시킬 때 사용될 수 있습니다.

3 단계에서는 SRPVerifierStore 인터페이스의 2단계 구현을 JNDI를 통해 사용가능하도록 해주는 MBean을 생성하고 여러분이 필요로하는 설정가능한 매개변수들을 노출시키는 것입니다. 기본 org.jboss.security.srp.SRPVerifierStoreService 예제에 추가하여, 이번 장의 후반부에 제공되는 SRP 예제에서는 자바 속성 파일 기반의 SRPVerifierStore 구현을 제공합니다. 이 두 예제사이에서 여러분은 여러분의 보안 저장소 통합을 위한 충분한 정보를 얻을 수 있을 것입니다.

8.5.2. SRP 알고리즘의 내부

SRP 알고리즘의 매력(appeal)은 보안 통신 채널없이도 간단한 텍스트 암호를 사용하여 클라이언트와 서버의 상호 인증을 가능케 하는 것입니다. 여러분은 아마도 이것이 어떻게 가능한지 궁금해 할 것입니다. 이러한 알고리즘에 대해 세부적인 설명과 이론적 배경을 알고 싶다면, 처음 언급했던 SRP 레퍼런스를 참고하십시오. 인증을 완료하기까지에는 다음의 6 단계가 수행됩니다 :

  1. 클라이언트쪽 SRPLoginModule는 네이밍 서비스로부터 원격 인증 서버에 대한 SRPServerInterface 인스턴스를 가져옵니다.
  2. 클라이언트쪽 SRPLoginModule은 다음에 로그인을 시도하는 사용자명과 연관된 SRP 매개변수들을 요청합니다. 사용자 암호가 맨 처음으로 SRP 알고리즘을 사용하여 검증자(verifier) 형식으로 변경될 때는 선택되어져야만 하는 SRP 알고리즘에 포함된 다수의 매개변수들이 존재합니다. 하드-코딩으로 매개변수를 입력하기보다는 JBossSX 구현에서 교환 프로토콜의 일부분으로써 사용자에 관련된 이러한 정보들을 가져오게 해줄 수 있습니다. 이렇게 해줌으로써 입력오류에 따른 사항을 최소화 시킬 수 있습니다. getSRPParameters(username) 호출을 통해 주어진 사용자명에 대한 SRP 매개변수들을 가져올 수 있습니다.
  3. 클라이언트쪽 SRPLoginModule은 로그인 사용자명, 평문 암호 및 2 단계를 통해 획득한 SRP 매개변수들을 사용하여 SRPClientSession 객체를 생성함으로써 SRP 세션을 시작합니다. 그런 다음 클라이언트는 사설 SRP 세션 키를 빌드하기 위해 사용되어질 랜덤 숫자 A를 만듭니다. 이제 클라이언트는 SRPServerInterface.init 메쏘드를 호출하여 서버쪽의 SRP 세션을 초기화하고 사용자명과 클라이언트에서 생성된 랜덤 숫자 A를 넘겨줍니다. 서버는 자신의 랜덤 숫자 B를 리턴합니다. 이 단계는 공개키 교환에 대응됩니다.
  4. 클라이언트쪽 SRPLoginModule은 앞의 메시지 교환의 결과로써 생성되어진 사설 SRP 세션 키를 갖게됩니다. 이것은 로그인 Subject내에 사설 credential로 저장됩니다. 서버의 4 단계로부터의 챌린지 응답 M2SRPClientSession.verify 메쏘드 호출을 통해 검증됩니다. 성공하면, 클라이언트에서 서버로의 상호 인증과 서버에서 클라이언트로의 인증이 종료됩니다. 클라이언트쪽 SRPLoginModule은 다음에는 서버의 랜덤 숫자 B를 인수로 넘겨주는 SRPClientSession.response 메쏘드를 호출함으로써 서버쪽에 챌린지 M1을 생성합니다. 이 챌린지는 SRPServerInterface.verify 메쏘드를 통해 서버쪽으로 전송되며 M2로 저장됩니다. 이 단계는 챌린지의 교환에 해당됩니다. 이러한 관점에서 본다면 서버는 사용자가 자신이 누구인지를 말하는 가를 검증하게 되는 것입니다.
  5. 클라이언트쪽 SRPLoginModule은 로그인 사용자명과 M1 챌런지를 LoginModule sharedState 맵에 저장합니다. 이것은 표준 JBoss ClientLoginModule에서 Principal 이름과 credential로 사용됩니다. M1 챌린지는 J2EE 컴포넌트에 대한 임의의 메쏘드 호출에 대해 신분의 증명으로써 사용되는 암호로 사용됩니다. M1 챌린지는 sRP 세션과 관련된 암호화된 강력한 해쉬입니다. 제 삼자가 이를 중간에서 가로챈다해도 사용자의 암호를 얻을 수는 없습니다.
  6. 이 인증 프로토콜의 마지막 단계에서, SRPServerSession은 SRPCacheLoginModule에 의해 사용되는 일련의 SRPService 인증 캐쉬쪽에 자리잡게 됩니다.

비록 SRP가 많은 흥미로운 속성들을 갖고 있기는 하나, 여전히 JBossSX 프레임워크내에서 진화되어가는 컴포넌트이며 여러분이 사용하기 위해서는 갖고 있는 여러가지 제약사항에 주의를 해야 합니다. 고려해야 할 문제점들은 다음과 같습니다:

  • 인증을 수행하게되는 컴포넌트 컨테이너로부터 전송 프로토콜의 메쏘드를 JBoss가 분리시키는 방법때문에, 인가되지 않은 사용자가 SRP M1 챌런지를 몰래 빼낼 수 있으며 이에 관련된 사용자명으로 빼낸 챌런지를 사용하여 요청을 만들어 낼 수 있습니다. SRP 세션 키를 사용하는 챌런지를 암호화한 커스텀 인터셉터는 이러한 문제를 방지할 수 있습니다.
  • SRPService는 설정가능한 유효기간이후에도 SRP 세션의 캐쉬를 유지합니다. 일단 타임아웃되면, SRP 인증 자격증명(credential)과 투명하게 협상하기위한 메커니즘이 현재 없기 때문에 다음에 따라오는 J2EE 컴포넌트 액세스는 실패하게 됩니다. 여러분은 인증 캐쉬의 타임아웃을 아주 길게(2,147,483,647 초 이상-대략 68년) 설정하거나 타임아웃에 의해 실패하는 경우에 대비한 재-인증 코드를 만들어 주어야만 합니다.
  • 기본적으로 주어진 사용자명으로는 오직 하나만의 SRP 세션이 존재할 뿐입니다. 협상되어진 SRP 세션이 클라이언트와 서버사이의 암호화/복호화에 사용될 수 있는 사설 세션 키를 생성하기 때문에, 세션은 하나에 대서는 효과적으로 관리할 수 있습니다. JBoss에서 한명의 사용자에 대해 다수의 SRP 세션을 지원하기는 하지만, 여러분은 하나의 세션 키로 암호화한 데이터를 또 다른 키를 사용하여 복호화할 수는 없습니다.

J2EE 컴포넌트 호출에 대해 종단간(end-to-end)의 SRP 인증을 사용하기 위해서는 컴포넌트가 안전하게 사용하는 org.jboss.security.srp.jaas.SRPCacheLoginModule 아랫쪽에 보안 도메인 설정을 할 필요가 있습니다. SRPCacheLoginModule은 SRP 인증 CachePolicy 인스턴스의 JNDI 로케이션을 설정하는 cacheJndiName 라는 이름을 갖는 단일 설정 옵션입니다. 이것은 SRPService MBean의 AuthenticationCacheJndiName 속성 값에 일치해야만 합니다. SRPCacheLoginModule은 인증 캐쉬내의 SRPServerSession 객체로부터 획득한 클라이언트 챌런지로 사용자의 자격증명을 인증하고 이것을 사용자의 자격증명으로 넘겨진 챌린지와 비교합니다. 그림 8.11, “SRP 세션 캐쉬를 갖는 SRPCacheLoginModule의 상호작용을 표시한 시퀀스 다이어그램” 에서는 SRPCacheLoginModule.login 메쏘드 구현의 오퍼레이션을 설명합니다.

SRP 세션 캐쉬를 갖는 SRPCacheLoginModule의 상호작용을 표시한 시퀀스 다이어그램.

그림 8.11. SRP 세션 캐쉬를 갖는 SRPCacheLoginModule의 상호작용을 표시한 시퀀스 다이어그램.

8.5.2.1. SRP 예제

우리는 SRP에 대해 많은 내용을 다루어왔으며 이제 예제를 통해 실제로 SRP 데모를 보여줄 것입니다. 예제에서는 SRP를 통해 사용자에 대한 클라이언트쪽 인증뿐만 아니라 그 다음으로 발생되는 사용자의 자격증명으로써의 SRP 세션 챌런지를 사용하여 간단한 EJB에 안전하게 액세스하는 것까지 보여줄 것입니다. 테스트 코드는 서버쪽 로그인 모듈 설정과 SRP 서비스에 대한 설정을 위한 sar를 포함하고 있는 하나의 EJB JAR를 배치하게 됩니다. 앞의 예제에서처럼 우리는 SecurityConfig MBean을 사용하여 동적으로 서버쪽 로그인 모듈 설정을 설치할 것입니다. 또한 우리는 이번 예제에서 SRPVerifierStoreService에 의해 사용되는 직렬화된 객체 저장소 대신 자바 속성 파일로부터 발생된 것을 메모리 저장소에서 사용하는 SRPVerifierStore 인터페이스의 커스텀 구현을 사용합니다. 이 커스텀 서비스는 org.jboss.chap8.ex3.service.PropertiesVerifierStore입니다. 다음은 예제 EJB와 SRP 서비스를 포함하고 있는 JAR의 컨텐츠입니다:

[orb@toki examples]$ java -cp output/classes ListJar output/chap8/chap8-ex3.jar
output/chap8/chap8-ex3.jar
+- META-INF/MANIFEST.MF
+- META-INF/ejb-jar.xml
+- META-INF/jboss.xml
+- org/jboss/chap8/ex3/Echo.class
+- org/jboss/chap8/ex3/EchoBean.class
+- org/jboss/chap8/ex3/EchoHome.class
+- roles.properties
+- users.properties
+- chap8-ex3.sar (archive)
| +- META-INF/MANIFEST.MF
| +- META-INF/jboss-service.xml
| +- META-INF/login-config.xml
| +- org/jboss/chap8/ex3/service/PropertiesVerifierStore$1.class
| +- org/jboss/chap8/ex3/service/PropertiesVerifierStore.class
| +- org/jboss/chap8/ex3/service/PropertiesVerifierStoreMBean.class
| +- org/jboss/chap8/service/SecurityConfig.class
| +- org/jboss/chap8/service/SecurityConfigMBean.class

이 예제에서의 중요한 SRP 관련 아이템들은 SRP MBean 서비스 설정과 SRP 로그인 모듈 설정입니다. chap8-ex3.sarjboss-service.xml 서술자가 예제 8.18, “SRP 서비스를 위한 chap8-ex3.sar의 jboss-service.xml 서술자”에 보여지며, 예제 8.19, “클라이언트쪽 표준 JAAS 설정”예제 8.20, “서버쪽 XMLLoginConfig 설정”에서는 클라이언트쪽과 서버쪽 예제 로그인 모듈 설정을 보여주고 있습니다.

예제 8.18. SRP 서비스를 위한 chap8-ex3.sar의 jboss-service.xml 서술자

<server>
    <!-- 동적으로 config 설정을 갱신할 수 있는 기능을 갖춘 환경설정을 
            설치하고 있는 커스텀 JAAS login 환경 설정 -->

    <mbean code="org.jboss.chap8.service.SecurityConfig" 
           name="jboss.docs.chap8:service=LoginConfig-EX3">
        <attribute name="AuthConfig">META-INF/login-config.xml</attribute>
        <attribute name="SecurityConfigName">jboss.security:name=SecurityConfig</attribute>
    </mbean>

    <!-- SRP RMI 서버와 서버쪽 인증 캐쉬를 제공하는 SRP 서비스 -->
    <mbean code="org.jboss.security.srp.SRPService" 
           name="jboss.docs.chap8:service=SRPService">
        <attribute name="VerifierSourceJndiName">srp-test/chap8-ex3</attribute>
        <attribute name="JndiName">srp-test/SRPServerInterface</attribute>
        <attribute name="AuthenticationCacheJndiName">srp-test/AuthenticationCache</attribute>
        <attribute name="ServerPort">0</attribute>
        <depends>jboss.docs.chap8:service=PropertiesVerifierStore</depends>
    </mbean>

    <!-- 사용자 암호 검증자 정보를 제공하는 SRP 저장소 핸들러 서비스  -->
    <mbean code="org.jboss.chap8.ex3.service.PropertiesVerifierStore"
           name="jboss.docs.chap8:service=PropertiesVerifierStore">
        <attribute name="JndiName">srp-test/chap8-ex3</attribute>
    </mbean>
</server>

예제 8.19. 클라이언트쪽 표준 JAAS 설정

srp {
    org.jboss.security.srp.jaas.SRPLoginModule required
    srpServerJndiName="srp-test/SRPServerInterface"
    ;
                    
    org.jboss.security.ClientLoginModule required
    password-stacking="useFirstPass"
    ;
}; 

예제 8.20. 서버쪽 XMLLoginConfig 설정

<application-policy name="chap8-ex3">
    <authentication>
        <login-module code="org.jboss.security.srp.jaas.SRPCacheLoginModule"
                      flag = "required">
            <module-option name="cacheJndiName">srp-test/AuthenticationCache</module-option>
        </login-module>
        <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
                      flag = "required">
            <module-option name="password-stacking">useFirstPass</module-option>
        </login-module>
    </authentication>
</application-policy>           

예제 서비스는 ServiceConfigPropertiesVerifierStore 그리고 SRPService MBeans 입니다. PropertiesVerifierStoreJndiName 속성이 SRPServiceVerifierSourceJndiName 속성과 동일하고, SRPServicePropertiesVerifierStore에 의존적이라는 것에 주의하십시오. 이것은 SRPService가 사용자 암호 검증 정보에 액세스하기위한 SRPVerifierStore 인터페이스의 구현을 필요로 하기 때문에 요구되어집니다.

클라이언트쪽 로그인 모듈 설정은 JBoss 서버 컴포넌트 SRPService JndiName 속성 값(srp-test/SRPServerInterface)에 일치하는 srpServerJndiName 옵션 값을 갖는 SRPLoginModule을 사용가능하게 합니다. 또한 EJB 호출 계층쪽에 SRPLoginModule에 의해 생성된 사용자 인증 자격증명(credential)을 만들어주기 위한 password-stacking="useFirstPass" 값을 갖는 설정된 ClientLoginModule을 필요로 합니다.

서버쪽 로그인 모듈 설정에 대해 주의해야 하는 두 개의 문제점이 있습니다. 첫 번째는 cacheJndiName=srp-test/AuthenticationCache 설정 옵션이 SRPCacheLoginModule에게 SRPService에 대해 인증되어진 사용자를 위한 SRPServerSession을 포함하는 CachePolicy의 로케이션을 말해준다는 것에 주의하십시오. 이 값은 SRPService AuthenticationCacheJndiName 속성 값과 일치합니다. 두 번째는 설정에 password-stacking=useFirstPass 설정 옵션을 갖는 UsersRolesLoginModule을 포함하고 있다는 것입니다. SRP는 단지 인증 기술이기 때문에 SRPCacheLoginModule을 갖는 두 번째 로그인 모듈을 사용할 필요가 있습니다. 두 번째 로그인 모듈은 principal의 허가(permission)를 결정하는 principal의 역할을 설정하기 위해 SRPCacheLoginModule에 의해 검증되어진 인증 자격증명을 수락하도록 설정될 필요가 있습니다. UsersRolesLoginModule은 속성 파일 기반의 인증과 함께 SRP 인증이 증가(augmenting)됩니다. 사용자의 역할은 EJB JAR내에 포함되어있는 roles.properties 파일로부터 나옵니다.

이제 이 책에서 제공하는 예제의 디렉터리로부터 다음 명령을 실행시켜 예제3을 실행합니다:

[starksm@banshee examples]$ ant -Dchap=chap8 -Dex=3 run-example
Buildfile: build.xml
...
run-example3:
     [copy] Copying 1 file to /tmp/jboss-3.2.6/server/default/deploy
     [echo] Waiting for 5 seconds for deploy...
     [java] Logging in using the 'srp' configuration
     [java] Created Echo
     [java] Echo.echo()#1 = This is call 1
     [java] Echo.echo()#2 = This is call 2

examples/logs 디렉터리에서 여러분은 ex3-trace.log라는 파일을 찾을 수 있습니다. 이 파일은 SRP 알고리즘에 대한 클라이언트쪽 세부 트레이스입니다. 트레이스에서는 공개 키, 챌런지, 세션 키 및 검증에 대한 단계별 진행을 보여줍니다.

클라이언트가 다른 간단한 예제들에 비해 상대적으로 긴 시간동안 실행된다는 것에 주의하십시오. 이러한 이유는 클라이언트의 공개키 작업 때문입니다. 여기에는 암호화된 강력한 랜덤 번호의 생성과정을 포함하며, 맨 처음에 실행시키면 프로세스가 상당히 오랜 시간 걸리게 됩니다. 만약 여러분이 동일한 VM내에서 로그인과 로그아웃을 하게 되면, 프로세스는 보다 빨라질 수 있습니다. 또한 Echo.echo()#2가 인증 예외를 발생시켜 실패한다는 것에 주의하십시오. 클라이언트 코드에서는 SRPService의 캐쉬 만료(expiration)에 대한 특성(behavior)을 보여주기 위해 최초 호출이 이루어진 후 15초동안 대기합니다. SRPService 캐쉬 정책 타임아웃은 이러한 문제를 해결하기 위해 10초이상으로 설정되어 집니다. 앞에서 언급했듯이, 여러분은 캐쉬의 타임아웃을 매우 길게 설정하거나 실패에 따른 재-인증 처리를 구현해줘야 할 필요가 있습니다.

8.6. Java 2 보안 관리자를 JBoss에서 동작시키기

기본적으로 JBoss 서버는 Java 2 보안 관리자와 함께 시작되지 않습니다. 여러분이 Java 2 인가를 사용하여 코드의 특권을 제한하고 싶다면, 보안 관리자를 적용시켜 JBoss 서버를 실행시키도록 설정해줘야만 합니다. 배포되는 JBoss 서버의 bin 디렉터리내에 있는 run.bat 또는 run.sh 스크립트내에서 Java VM 옵션을 설정해줌으로써 가능합니다. 필요한 두 개의 VM 옵션은 다음과 같습니다:

  • java.security.manager: 사용되어져야만 하는 기본 보안 관리자를 지정하기 위해 어떠한 값도 없이 사용됩니다. 이것은 선호되는 보안 관리자입니다. 또한 여러분은 커스텀 보안 관리자 구현을 지정하기 위해 java.security.manager 옵션에 값을 넘겨줄 수도 있습니다. 값은 java.lang.SecurityManager의 서브클래스에 대한 완전한 형태의 클래스 이름이어야만 합니다. 이 폼은 정책 파일이 VM 설치에 의해 설정되어지는 기본 보안 정책을 증가시켜주도록 지정합니다.
  • java.security.policy: VM에 대한 기본 보안 정책 정보를 증가시키는 정책 파일을 지정해주는데 사용됩니다. 이것의 옵션은 두 개의 폼을 갖습니다 : java.security.policy=policyFileURLjava.security.policy==policyFileURL. 첫 번째 폼은 VM 설치에 의해 설정되는 기본 보안 정책을 증가시켜주는 정책 파일을 지정합니다. 두 번째 폼은 사용되어져야만 하는 정채 파일만을 지정해줍니다. policyFileURL 값은 프로토콜 핸들러가 존재하는 URL 이나 파일의 경로 값이 될 수 있습니다.

예제 8.21, “Java 2 보안 관리자와 함께 JBoss를 시작시키기 위한 Win32 run.bat 수정.”에서는 Java2 보안 관리자와 함께 JBoss 서버를 시작시키기 위해 커맨드 라인에 두 옵션을 추가해준 것을 보여주는 Win32용 시작 스크립트인 표준 run.bat의 일부가 제공됩니다.

예제 8.21. Java 2 보안 관리자와 함께 JBoss를 시작시키기 위한 Win32 run.bat 수정.

...
            
set CONFIG=%1
@if "%CONFIG%" == "" set CONFIG=default
set PF=../conf/%CONFIG%/server.policy
set OPTS=-Djava.security.manager
set OPTS=%OPTS% -Djava.security.policy=%PF%
echo JBOSS_CLASSPATH=%JBOSS_CLASSPATH%
java %JAXP% %OPTS% -classpath "%JBOSS_CLASSPATH%" org.jboss.Main %* 

예제 8.22, “Java 2 보안 관리자와 함께 JBoss를 시작시키기 위한 UNIX/Linux용 run.sh 수정.”에서는 Java2 보안 관리자와 함께 JBoss 서버를 시작시키기 위해 커맨드 라인에 두 옵션을 추가해준 것을 보여주는 UNIX/Linux 시스템용 시작 스크립트인 표준 run.sh의 일부가 제공됩니다.

예제 8.22. Java 2 보안 관리자와 함께 JBoss를 시작시키기 위한 UNIX/Linux용 run.sh 수정.

# ...
            
CONFIG=$1
if [ "$CONFIG" == "" ]; then CONFIG=default; fi
PF=../conf/$CONFIG/server.policy
OPTS=-Djava.security.manager
OPTS="$OPTS -Djava.security.policy=$PF"
echo JBOSS_CLASSPATH=$JBOSS_CLASSPATH
java $HOTSPOT $JAXP $OPTS -classpath $JBOSS_CLASSPATH org.jboss.Main $@

시작 스크립트 모두 보안 정책 파일에 JBoss 설정 파일 셋 디렉터리내의 스크립트의 첫 번째 인수로 넘겨지는 설정 이름에 대응되는 server.policy 파일을 설정합니다. 이를 통해 시작 스크립트를 수정하지 않으면서도 설정 파일 셋당 보안 정책을 유지관리할 수 있습니다.

Java 2 보안을 활성화시키는 것은 쉬운 작업입니다. Java 2 보안의 어려운 작업은 허용된 허가를 구축하는 것입니다. 기본 설정 파일 셋내에 포함되어 있는 server.policy 파일을 살펴본다면, 다음과 같은 허가를 수락하는 문장이 있다는 것을 찾을 수 있습니다:

grant {
    // Allow everything for now
    permission java.security.AllPermission;
};

이것은 어떠한 코드라도 모든 것을 할 수 있게 함으로써 효과적으로 모든 코드에 대한 보안 허가 점검을 가능하지 않도록 합니다. 이것은 합리적인 기본값은 아닙니다. 어떻게 합리적인 허가에 대한 셋을 구성하는지는 여러분에게 달렸습니다.

JBoss에 특화된(specific) java.lang.RuntimePermissions의 현재 셋에서 요구되는 사항들은 다음과 같습니다:

타겟이름 허용되는 인가 리스크
org.jboss.security.SecurityAssociation.getPrincipalInfo org.jboss.security.SecurityAssociation getPrincipal() 과 getCredentials() 메쏘드로의 액세스. 현재 쓰레드 호출자와 자격증명을 볼 수 있는 능력.
org.jboss.security.SecurityAssociation.setPrincipalInfo org.jboss.security.SecurityAssociation setPrincipal() 과 setCredentials() 메쏘드로의 액세스. 현재 쓰레드 호출자와 자격증명을 설정할 수 있는 능력.
org.jboss.security.SecurityAssociation.setServer org.jboss.security.SecurityAssociation setServer 메쏘드로의 액세스. 호출자의 principal과 credential에 대한 멀티쓰레드 저장소(storage)를 활성화시키거나 비활성시킬 수 있는 능력.
org.jboss.security.SecurityAssociation.setRunAsRole org.jboss.security.SecurityAssociation pushRunAsRole 과 popRunAsRole 메쏘드로의 액세스. 현재 호출자의 run-as 역할 principal을 변경할 수 있는 능력.

이번 논의를 끝맺기위해, 여기서는 보안 정책 설정을 디버깅하는데 잘 알려지지 않은 재미있는 이야기를 해보도록 하겠습니다. 보안 관리자가 어떻게 여러분의 보안 정책 파일을 사용하는지 뿐만 아니라 어떤 정책 파일이 허가에 관련되어 있는지를 알아보기위한 다양한 디버깅 플래그가 존재합니다. 다음과 같이 VM을 실행시키면 사용가능한 디버깅 플래그 설정을 보여줍니다:

[nr@toki bin]$ java -Djava.security.debug=help
            
all           turn on all debugging
access        print all checkPermission results
combiner      SubjectDomainCombiner debugging
jar           jar verification
logincontext  login context results
policy        loading and granting
provider      security provider debugging
scl           permissions SecureClassLoader assigns

The following can be used with access:

stack     include stack trace
domain    dumps all domains in context
failure   before throwing exception, dump stack
          and domain that didn't have permission

Note: Separate multiple options with a comma

-Djava.security.debug=all로 실행시키면 가장 많은 아웃풋을 제공하기는 하지만, 그 양이 굉장합니다. 이 옵션은 발생한 보안 실패를 전혀 이해하지 못하는 경우 좋은 시작이 될 수 있습니다. 나타나는 결과를 보다 적게하면서도 허가 실패에 대한 디버깅을 도울 수 있는 방법은 -Djava.security.debug=access,failure를 사용하는 것입니다. 이 옵션은 all에 비해 상대적으로 적은 아웃풋을 내보내기는 하지만, 액세스 실패에 대해서만 보안 도메인 정보를 보여줌으로써 all 모드를 사용하는 것만큼 좋은 옵션은 아닙니다.

8.7. JSSE를 사용하여 JBoss에서 SSL 이용하기

JBoss는 자바 보안 소켓 확장(Java Secure Socket Extension-JSSE)을 사용합니다. JSSE은 JBoss에 번들되어 있으며 JDK 1.4에 함께 들어가 있습니다. JSSE에 대한 보다 자세한 정보는 http://java.sun.com/products/jsse/index.html을 참조하십시오. JBoss에 번들링되어 있는 JSSE 사용을 위한 간단한 테스트는 다음과 같이 프로그램을 실행시켜보는 것입니다:

import java.net.*;
import javax.net.ServerSocketFactory;
import javax.net.ssl.*;
        
public class JSSE_install_check
{
    public static void main(String[] args) throws Exception
    {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        ServerSocketFactory factory = SSLServerSocketFactory.getDefault();
        SSLServerSocket sslSocket = (SSLServerSocket) factory.createServerSocket(12345);
        
        String [] cipherSuites = sslSocket.getEnabledCipherSuites();
        for(int i = 0; i < cipherSuites.length; i++) {
            System.out.println("Cipher Suite " + i + " = " + cipherSuites[i]);
        }
    }
}    

이 책에서 제공되는 예제에 포함되어 있는 테스트 프로그램을 실행시키기 위해서는 다음과 같은 명령을 수행하시면 됩니다. 이 명령을 실행시키면 VM쪽에 -Djavax.net.debug=all 옵션이 넘겨져 많은 량의 아웃풋이 생성됩니다.

[nr@toki examples]$ ant -Dchap=chap8 -Dex=4a run-example
...
run-example4a:
run-example4a:
     [echo] Testing JSSE availablility
     [java] keyStore is : 
     [java] keyStore type is : jks
     [java] init keystore
     [java] init keymanager of type SunX509
     [java] trustStore is: /System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Home
/lib/security/cacerts
     [java] trustStore type is : jks
     [java] init truststore
...
     [java] init context
     [java] trigger seeding of SecureRandom
     [java] done seeding SecureRandom
     [java] Cipher Suite 0 = SSL_RSA_WITH_RC4_128_MD5
     [java] Cipher Suite 1 = SSL_RSA_WITH_RC4_128_SHA
     [java] Cipher Suite 2 = TLS_RSA_WITH_AES_128_CBC_SHA
     [java] Cipher Suite 3 = TLS_DHE_RSA_WITH_AES_128_CBC_SHA
     [java] Cipher Suite 4 = TLS_DHE_DSS_WITH_AES_128_CBC_SHA
     [java] Cipher Suite 5 = SSL_RSA_WITH_3DES_EDE_CBC_SHA
     [java] Cipher Suite 6 = SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA
     [java] Cipher Suite 7 = SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA
     [java] Cipher Suite 8 = SSL_RSA_WITH_DES_CBC_SHA
     [java] Cipher Suite 9 = SSL_DHE_RSA_WITH_DES_CBC_SHA
     [java] Cipher Suite 10 = SSL_DHE_DSS_WITH_DES_CBC_SHA
     [java] Cipher Suite 11 = SSL_RSA_EXPORT_WITH_RC4_40_MD5
     [java] Cipher Suite 12 = SSL_RSA_EXPORT_WITH_DES40_CBC_SHA
     [java] Cipher Suite 13 = SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
     [java] Cipher Suite 14 = SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA

JSSE jars는 JBOSS_DIST/client 디렉터리내에 jcert.jar, jnet.jar 그리고 jsse.jar가 포함됩니다.

JSSE 실행 테스트를 해보았다면, 여러분은 SSL 서버 소켓에서 사용하는 X509 인증에 대한 형태로 공개키/사설키 쌍이 필요합니다. 이 예제를 위해 저희는 JDK keytool을 사용하여 자기-서명된(self-signed) 인증서를 생성하고, 만들어진 keystore 파일을 chap8 소스 디렉터리에 chap8.keystore로 저장해 놓았습니다. 다음의 명령어와 input에 의해 생성되어졌습니다:

[nr@toki examples]$ keytool -genkey -alias rmi+ssl -keyalg RSA -keystore chap8.keystore -v
alidity 3650
[orb@toki examples]$ keytool -genkey -alias rmi+ssl -keyalg RSA -keystore chap8.keystore -
validity 3650
Enter keystore password: rmi+ssl
What is your first and last name?
[Unknown]: Chapter 8 SSL Example
What is the name of your organizational unit?
[Unknown]: JBoss Book
What is the name of your organization?
[Unknown]: JBoss, Inc.
What is the name of your City or Locality?
[Unknown]: Issaquah
What is the name of your State or Province?
[Unknown]: WA
What is the two-letter country code for this unit?
[Unknown]: US
Is CN=Chapter 8 SSL Example, OU=JBoss Book, O="JBoss, Inc.", L=Issaquah, ST=WA, C=US corre
ct?
[no]: yes
 
Enter key password for <rmi+ssl>
(RETURN if same as keystore password):

이렇게 생성시킨 keystore 파일이 chap8.keystore 입니다. keystore는 보안 키에 대한 데이터베이스입니다. keystore내에는 두 개의 서로 다른 타입의 항목이 있습니다:

  • key entries: 각각의 항목에는 매우 민감한 암호화된 키 정보를 담고 있으며, 인가되지 않은 액세스를 방지하기 위한 보호된 포맷형태로 저장됩니다. 보통 이러한 형태의 항목으로 저장된 키는 비밀 키이거나, 공개키에 대응되는 인증서 체인에 의해 동반되는 사설키입니다. keytooljarsigner 툴은 후자 형태의 항목만을 처리하며, 이것은 인증서 체인과 이에 관련된 사설키입니다.
  • trusted certificate entries: 각각의 항목에는 다른 부분에 속해있는 단일 공개 키 인증서를 포함합니다. 이것이 신뢰된 인증서(trusted certificate)라고 불리는 이유는 인증서내의 공개키를 신뢰하는 keystore 소유자가 인증서의 subject(소유자)에 의해 식별되어진 신분에 속하기 때문입니다. 인증서의 발행자(issuer)가 인증서 사인을 통해 이것을 보장합니다.

keytool을 사용하여 src/main/org/jboss/chap8/chap8.keystore 예제 파일 컨텐츠 리스팅을 보면 하나의 자기-사인된 인증서가 보입니다:

[nr@toki examples]$ keytool -list -v -keystore src/main/org/jboss/chap8/chap8.keystore
Enter keystore password: rmi+ssl
 
Keystore type: jks
Keystore provider: SUN
 
Your keystore contains 1 entry
 
Alias name: rmi+ssl
Creation date: Nov 8, 2001
Entry type: keyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Chapter8 SSL Example, OU=JBoss Book, O="JBoss Group, LLC", L=Issaquah, ST=WA, C=
US
Issuer: CN=Chapter8 SSL Example, OU=JBoss Book, O="JBoss Group, LLC", L=Issaquah, ST=WA, C
=US
Serial number: 3beb5271
Valid from: Thu Nov 08 21:50:09 CST 2001 until: Sun Nov 06 21:50:09 CST 2011
Certificate fingerprints:
MD5: F6:1B:2B:E9:A5:23:E7:22:B2:18:6F:3F:9F:E7:38:AE
SHA1: F2:20:50:36:97:86:52:89:71:48:A2:C3:06:C8:F9:2D:F7:79:00:36
 
 
*******************************************
*******************************************

JSSE 작업과 JBoss 서버에서 사용하게 될 인증서를 갖는 keystore로, 여러분은 JBoss에서 EJB 액세스에 대해 SSL을 사용할 수 있는 설정 준비가 되었습니다. 이것은 EJB 호출자(invoker) RMI 소켓 팩토리 설정을 통해 이루어집니다. JBossSX 프레임워크는 SSL 암호화된 소켓을 통해 RMI를 사용할 수 있도록 해주는 java.rmi.server.RMIServerSocketFactoryjava.rmi.server.RMIClientSocketFactory 인터페이스 구현을 포함하고 있습니다. 구현 클래스는 각각 org.jboss.security.ssl.RMISSLServerSocketFactoryorg.jboss.security.ssl.RMISSLClientSocketFactory 입니다. EJB에 대한 RMI 액세스를 SSL을 사용하여 가능하게 하는 것은 두 단계로 이루어집니다. 첫 번째 단계는 SSL 서버 인증서용 데이터베이스로써 keystore를 사용할 수 있도록 하는 것이며, 이것은 org.jboss.security.plugins.JaasSecurityDomain MBean 설정을 통해 이루어집니다. chap8/ex4 디렉터리내의 jboss-service.xml 서술자에는 예제 8.23, “RMI/SSL용 JaasSecurityDomain config 예제”에서 보여지는 JaasSecurityDomain 정의가 포함되어 있습니다.

예제 8.23. RMI/SSL용 JaasSecurityDomain config 예제

<!-- The SSL domain setup -->
<mbean code="org.jboss.security.plugins.JaasSecurityDomain"
       name="jboss.security:service=JaasSecurityDomain,domain=RMI+SSL">
    <constructor>
        <arg type="java.lang.String" value="RMI+SSL"/>
    </constructor>
    <attribute name="KeyStoreURL">chap8.keystore</attribute>
    <attribute name="KeyStorePass">rmi+ssl</attribute>
</mbean> 

JaasSecurityDomain은 keystore에 대한 표기뿐만 아니라 JSSE KeyManagerFactoryTrustManagerFactory 액세스까지 추가하는 표준 JaasSecurityManager 클래스의 서브클래스입니다. 기본 보안 관리자에 보안 키를 필요로 하는 SSL과 다른 암호화된 오퍼레이션을 위한 지원을 가능하게 확장합니다. 이러한 설정은 지정된 암호를 사용하여 예제 4 MBean으로부터 chap8.keystore를 간단히 로딩합니다.

두 번째 단계에서는 SSL을 지원하는 JBossSX RMI 소켓 팩토리를 사용하는 EJB 호출자 설정을 정의합니다. 이를 위해서 여러분은 5 장, JBoss에서의 EJBs에서 보았었던 JRMPInvoker 뿐만 아니라 이 호출자를 사용가능하게 설정하는 EJB를 위한 커스텀 설정을 정의해줄 필요가 있습니다. 비상태 세션 빈을 SSL 액세스를 통해 액세스하는 RMI를 활성화시키는데 필요한 설정은 예제 8.24, “예제 4의 비상태(stateless) 세션 빈에서 SSL을 가능하게 하는 jboss-service.xml 설정” 예제 8.25, “예제 4의 비상태(stateless) 세션 빈에서 SSL을 가능하게 하는 jboss.xml 설정”에서 제공됩니다. 커스텀 JRMPInovker를 정의하는 jboss-service.xml 서술자의 윗부분과 예제 4 EchoBean4 설정의 밑부분은 SSL 호출자 사용이 필요합니다. 여러분은 이 설정을 비상태 세션 빈 예제에서 사용하게 됩니다.

예제 8.24. 예제 4의 비상태(stateless) 세션 빈에서 SSL을 가능하게 하는 jboss-service.xml 설정.

<mbean code="org.jboss.invocation.jrmp.server.JRMPInvoker
       name="jboss:service=invoker,type=jrmp,socketType=SSL">
    <attribute name="RMIObjectPort">14445</attribute>
    <attribute name="RMIClientSocketFactory">
        org.jboss.security.ssl.RMISSLClientSocketFactory
    </attribute>
    <attribute name="RMIServerSocketFactory">
        org.jboss.security.ssl.RMISSLServerSocketFactory
    </attribute>
    <attribute name="SecurityDomain">java:/jaas/RMI+SSL</attribute>
    <depends>jboss.security:service=JaasSecurityDomain,domain=RMI+SSL</depends>
</mbean> 

예제 8.25. 예제 4의 비상태(stateless) 세션 빈에서 SSL을 가능하게 하는 jboss.xml 설정.

<?xml version="1.0"?>
<jboss>
    <enterprise-beans>
        <session>
            <ejb-name>EchoBean4</ejb-name>
            <configuration-name>Standard Stateless SessionBean</configuration-name>
            <home-invoker>jboss:service=invoker,type=jrmp,socketType=SSL</home-invoker>
            <bean-invoker>jboss:service=invoker,type=jrmp,socketType=SSL</bean-invoker>
        </session>
    </enterprise-beans>
</jboss>

예제 4 코드는 이 책 예제의 src/main/org/jboss/chap8/ex4 디렉터리 아래에 위치합니다. 이것은 자신의 input 인수를 리턴해주는 echo 메쏘드를 갖는 또다른 간단한 비상태 세션 빈입니다. 실패하기 전까지는 SSL이 사용중인지 알려주기가 매우 힘듭니다. 따라서 우리는 EJB 배치가 SSL을 사용하고 있는지를 보여주기 위해 두 개의 서로 다른 방법으로 예제 4 클라이언트를 실행시켜보게 될 것입니다. 기본 설정을 사용하여 JBoss 서버를 시작시킨 후, 예제 4b를 다음과 같이 실행시킵니다:

[nr@toki examples]$ ant -Dchap=chap8 -Dex=4b run-example
...
run-example4b:
     [copy] Copying 1 file to /tmp/jboss-3.2.6/server/default/deploy
     [echo] Waiting for 15 seconds for deploy...
     [java] Exception in thread "main" java.rmi.ConnectIOException: error during JRMP conn
ection establishment; nested exception is: 
     [java]     javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorExcep
tion: No trusted certificate found
     [java]     at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:274)
...
     [java] Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.Validat
orException: No trusted certificate found
     [java]     at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.a(DashoA12275)
...
     [java] Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.Validat
orException: No trusted certificate found
     [java]     at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.a(DashoA12275)
...

예외가 발생되어져야 하며, 이것은 예제 4b 버전의 목적이기도 합니다. 예외 스택 트레이스는 이 책 포맷에 적합하도록 편집되었기 때문에 다소 다를 수 있다는 것에 주의해주십시오. 예외에서 주의 깊에 살펴볼 중요 항목은 JBoss EJB 컨테이너와 통신하는 Sun JSSE 클래스를 사용한다는 것을 분명히 보여준다는 것입니다. 예외에서는 JBoss 서버 인증서로써 사용하는 자기-서명된 인증서가 어떠한 기본 인증서 기관(authorities)으로부터 서명되었다는 것을 검증할 수 없다는 것을 말해주고 있습니다. JSSE 패키지에 따라오는 기본 인증서 권한 keystore에서는 단지 널리 알려진 인증서 기관인 VeriSign, Thawte 및 RSA Data Security 만을 포함하고 있기 때문에 예상되었던 사항입니다. 여러분의 자기-서명한 인증서가 올바른것으로 인식되도록 EJB 클라이언트를 만들기 위해서는 자신의 truststore로써 여러분의 chap8.keystore를 사용하는 JSSE 클래스를 알려주는 것이 필요합니다. truststore는 다른 인증서 사인을 위해 사용된 공개 키 인증서를 포함하는 단순한 keystore입니다. 이를 위해서 javax.net.ssl.trustStore 시스템 속성을 사용하여 올바른 truststore의 위치를 넘겨줄 수 있도록 -Dex=4b가 아닌 -Dex=4를 사용하여 예제 4를 실행합니다:

[nr@toki examples]$ ant -Dchap=chap8 -Dex=4 run-example
...
run-example4:
     [copy] Copying 1 file to /tmp/jboss-3.2.6/server/default/deploy
     [echo] Waiting for 5 seconds for deploy...
     [java] 0 [HandshakeCompletedNotify-Thread] DEBUG org.jboss.security.ssl.RMISSLClientS
ocketFactory  - SSL handshakeCompleted, cipher=SSL_RSA_WITH_RC4_128_MD5, peerHost=127.0.0.
1
     [java] Created Echo
     [java] Echo.echo()#1 = This is call 1

이번에는 SSL handshakeCompleted 메시지 때문에 SSL 소켓이 포함되었다는 것만 표시됩니다. 이것은 디버깅 레벨 로그 메시지로써 RMISSLClientSocketFactory 클래스로부터 나옵니다. 여러분이 log4j 디버그 레벨 메시지에서 출력될 수 있도록 클라이언트를 설정하지 않았다면, SSL이 포함되어있다는 것을 직접적으로 가르킬 수는 없습니다. 여러분이 런타임과 시스템 CPU의 부하를 살펴본다면, 분명히 다른점을 발견할 수 있습니다. SRP처럼 SSL은 처음 사용하게 될때 암호화된 강력한 랜덤 숫자를 생성시키는데 시간이 걸리게 됩니다. 이것은 높은 CPU 사용률과 시작 시간으로 나타나게 됩니다.

이로 인한 결과중에 하나는 이 책에서 실행시킨 예제를 사용했던 시스템 보다 더 느린 시스템에서 실행하면, 예제 4b를 실행시켰던 것처럼 다음에 오는 예외와 유사한 결과를 보게 될 수도 있습니다:

javax.naming.NameNotFoundException: EchoBean4 not bound
   at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer
   at sun.rmi.transport.StreamRemoteCall.executeCall
   at sun.rmi.server.UnicastRef.invoke
   at org.jnp.server.NamingServer_Stub.lookup
   at org.jnp.interfaces.NamingContext.lookup
   at org.jnp.interfaces.NamingContext.lookup
   at javax.naming.InitialContext.lookup
   at org.jboss.chap8.ex4.ExClient.main(ExClient.java:29)

이러한 문제는 JBoss 서버가 클라이언트가 헝요한 시간내에 예제 EJB를 배치하는 것을 완료하지 못했기 때문입니다. 이것은 SSL 서버 소켓에서 사용하는 안전한 랜덤 숫자 생성의 초기 설정 시간때문입니다. 만일 이러한 문제가 발생되었다면, 다시 예제를 실행시켜보거나 chap8 build.xml Ant 스크립트에서 배치 대기 시간을 증가시켜 주십시오.

8.8. 방화벽뒤에서 JBoss를 사용하도록 설정하기

JBoss는 많은 소켓 기반의 서비스에서 수신 포트들을 열게 됩니다. 이번 섹션에서 우리는 포트를 오픈시키는 서비스들의 목록을 나열하여 JBoss를 방화벽 안쪽에 구축한 경우 설정해야 될 작업들을 고려하도록 하겠습니다. 다음 표에서는 기본 설정 파일 셋내에 정의된 서비스들을 위한 포트들, 소켓 타입, 관련 서비스들을 보여주고 있습니다. 표 8.2, “all 환경에서의 추가적인 포트들” 에서는 all 환경 파일 셋트내에 존재하는 추가적인 포트들에 대한 정보를 동일한 방식으로 보여주고 있습니다.

표 8.1. 기본 설정에서의 포트들

포트번호타입서비스
1099 TCPorg.jboss.naming.NamingService
1098 TCPorg.jboss.naming.NamingService
1162 UDPorg.jboss.jmx.adaptor.snmp.trapd.TrapdService
4444 TCPorg.jboss.invocation.jrmp.server.JRMPInvoker
4445 TCPorg.jboss.invocation.pooled.server.PooledInvoker
8009 TCPorg.jboss.web.tomcat.tc4.EmbeddedTomcatService
8080 TCPorg.jboss.web.tomcat.tc4.EmbeddedTomcatService
8083 TCPorg.jboss.web.WebService
8090 TCPorg.jboss.mq.il.oil.OILServerILService
8092 TCPorg.jboss.mq.il.oil2.OIL2ServerILService
8093 TCPorg.jboss.mq.il.uil2.UILServerILService
0[a]TCPorg.jboss.mq.il.rmi.RMIServerILService
0[b]UDPorg.jboss.jmx.adaptor.snmp.agent.SnmpAgentService

[a] 이 서비스는 anonymous TCP 포트에 연결되며 포트나 연결되는 인터페이스에 대한 설정을 지원하지 않습니다.

[b] 이 서비스는 anonymous UDP 포트에 연결되며 포트나 연결되는 인터페이스에 대한 설정을 지원하지 않습니다.

Table 8.2. all 환경에서의 추가적인 포트들

포트번호타입서비스
1100TCPorg.jboss.ha.jndi.HANamingService
0[a]TCPorg.jboss.ha.jndi.HANamingService
1102UDPorg.jboss.ha.jndi.HANamingService
3528TCPorg.jboss.invocation.iiop.IIOPInvoker
45566[b]UDPorg.jboss.ha.framework.server.ClusterPartition

[a] 현재는 anonymous 이지만 RmiPort 속성으로 설정될 수 있음.

[b] 두 개의 추가적인 anonymous UDP 포트에 더하여, 하나는 rcv_port를 사용하여 설정될 수 있고 다른 하나는 설정될 수 없음.

8.9. JBoss 서버를 안전하게 하는 방법

JBoss는 배치할 때 관리자 기능에 비인가된 액세스가 접근하지 못하도록 할 필요가 있는 관리 액세스 포인트들을 갖고 있습니다. 이번 섹션에서는 다양한 관리 서비스들과 이들의 안전성 확보 방법을 논의하도록 하겠습니다.

8.9.1. jmx-console.war

deploy 디렉터리에 있는 jmx-console.war에서는 JMX 마이크로커널쪽에 html 뷰를 제공합니다. 따라서 서버를 셧다운시키거나 서비스의 중지, 새로운 서비스의 배치등과 같은 임의의 관리 형태의 액세스가 제공됩니다. 이것은 다른 여타의 웹 어플리케이션처럼 보호를 하거나 제거해주어야 합니다.

8.9.2. web-console.war

deploy/management 디렉터리에 있는 web-console.war는 또 다른 버전의 JMX 마이크로커널에 대한 웹 어플리케이션 뷰를 제공합니다. 이것은 애플릿과 HTML 뷰를 조합하여 jmx-console.war에서 처럼 동일한 수준으로 관리자 기능에 액세스 할 수 있도록 합니다. 따라서 이것 또한 보호하거나 제거해주어야만 합니다. web-console.war는 자신의 WEB-INF/web.xml내에 주석으로 처리된 기본 보안 템플릿뿐만 아니라 WEB-INF/jboss-web.xml내에 보안 도메인을 위한 설정까지 포함되어 있습니다.

8.9.3. http-invoker.sar

deploy 디렉터리내에 존재하는 http-invoker.sar는 EJB들과 JNDI에 액세스하는 RMI/HTTP를 제공하는 서비스입니다. 이것에는 MBeanServer쪽으로 처리되어져야 하는 요청들을 표현하는 마샬링된 org.jboss.invocation.Invocation 객체들의 포트들을 처리해주는 서블릿을 포함하고 있습니다. HTTP post 포맷을 어떻게 만들어주는지 알아 낼 수 있기 때문에 HTTP를 통해 분리된 호출자 오퍼레이션을 지원하는 MBean으로의 액세스가 효율적으로 허용됩니다. 이러한 액세스 포인트를 안전하게 하려면, http-invoker.sar/invoker.war/WEB-INF/web.xml 서술자내에서 찾을 수 있는 JMXInvokerServlet 서블릿을 안전하게 해 줄 필요가 있습니다. 기본적으로 /restricted/JMXInvokerServlet 경로를 위한 보안 매핑 정의가 존재하며, 간단하게 다른 경로들을 제거해주고 http-invoker.sar/invoker.war/WEB-INF/jboss-web.xml 서술자에서 http-invoker 보안 도메인 설정을 해 줄 수 있습니다.

8.9.4. jmx-invoker-adaptor-server.sar

jmx-invoker-adaptor-server.sar는 RMI/JRMP 분리된 호출자 서비스를 사용하여 RMI 호환 인터페이스를 통해 JMX MBeanServer 인터페이스를 노출시켜주는 서비스입니다. 현재 이 서비스를 안전하게 할 수 있는 유일한 방법은 RMI/HTTP로 프로토콜을 전환하고 앞의 섹션에서 논의된것 처럼 http-invoker.sar를 보호하는 것입니다. 향후 이 서비스는 역할 기반의 액세스 점검을 지원하는 보안 인터셉터와 함께 XMBean으로써 배치될 것입니다. 오늘 당장 이러한 설정을 사용하려면 다음과 같은 절차로 XMBean 예제로 된 데모를 참고하십시오 : 2.4.3.2.3 절, “버전 3, JNDIMap에 보안과 원격 액세스 추가하기”.



[2] 이러한 문제를 해결하기 위해 고유의 JAAS 구현을 갖추고 있기 때문에 보통 JBoss 3.x에서는 ProxyLoginModule이 필요하지 않으며, JDK1.4 JAAS 구현도 동일한 방법으로 동작합니다. ProxyLoginModule은 아랫 버전과의 호환성때문에 남겨져 있습니다.