Java/Servlet & JSP

웹 애플리케이션 보안

체리필터 2009. 4. 10. 18:54
728x90
반응형
보안을 위해서는 인증(Authentication), 인가(Authorization), 데이터 무결성(Data Integrity), 기밀성(Confidentiality)이 보장 되어야 한다.
인증이란 쉽게 말해 로그인을 통해 누구인지 서버(컨테이너)가 알 수 있도록 체크하는 단계이다.
인가는 인증된 사용자의 권한 레벨을 확인할 수 있도록 체크하는 단계이다.
데이터 무결성이란 클라이언트가 서버에 보낸 정보가 변경되지 않도록 하는 것이며, 기밀성이란 중간에 해당 데이터를 엿보지 못하도록 하는 것이다.

인증 및 인가 작업은 php에서도 많이 하는 작업이므로 그 의미에 대해서 별 다른 설명은 필요해 보이지 않는다.
자세한 설정 방법 및 사용 법은 아래 예제들을 통해 알 수 있다.

우선은 서버 자체에서 지원하는 인증 방식을 알아보면 다음과 같다.

tomcat-user.xml

<tomcat-users>
    <role rolename="Admin"/>
    <role rolename="Member"/>
    <role rolename="Guest"/>
    <user name="Annie" password="admin" roles="Admin, Member, Guest" />
    <user name="Diane" password="coder" roles="Member, Guest" />
    <user name="Ted" password="newbie" roles="Guest" />
</tomcat-users>

서버 설치 디렉토리 또는 그 하위 디렉토리 중 conf 디렉토리에 있는 tomcat-user.xml에 유저 정보를 위와 같이 기록한다.

web.xml

  <login-config>
      <auth-method>BASIC</auth-method>
  </login-config>
 
  <security-role>
      <role-name>Admin</role-name>
  </security-role>
  <security-role>
      <role-name>Member</role-name>
  </security-role>
  <security-role>
      <role-name>Guest</role-name>
  </security-role>
  
  <security-constraint>
      <web-resource-collection>
          <web-resource-name>UpdateRecipes</web-resource-name>
          <url-pattern>/Beer/AddRecipe/*</url-pattern>
          <url-pattern>/Beer/ReviewRecipe/*</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
          <role-name>Admin</role-name>
          <role-name>Member</role-name>
      </auth-constraint>
  </security-constraint>

인증방식(auth-method)은 BASIC으로 하고 컨테이너에 지정한 내용(tomcat-user.xml)과 맵핑하는 정보를 기술(security-role)한다.
security-constraint에서는 특정 디렉토리에 인증을 걸 수 있게 url-pattern을 지정한다. 또한 인증을 허가할 http method도 지정한다.
지정을 안한 메소드들은 인증 없이 접근할 수 있다.
auth-constraint에는 접근할 수 있는 유저의 이름을 지정한다.
위와 같이 한 상태로 서버를 재시작 한 다음 http://localhost:8080/Beer/AddRecipe/ 에 접근하면 아래와 같은 로그인 창이 뜨게 된다.



* <auth-constraint> 규칙

<auth-constraint>은 옵션이다.
<auth-constraint> 항목이 있다는 것은 컨테이너에게 관련 URL에 대해 인증을 실시하라고 명령하는 것이다.'
<auth-constraint>가 없다면 해당 URL에 대해 인증없이 접근할 수 있다.
  - <auth-constraint/>와 같은 형식으로 되어 있다면 모든 사용자가 접근할 수 없다는 것이다.

* <role-name> 규칙

<role-name>은 옵션이다.
<role-name> 항목은 접근 허용목록을 컨테이너에게 알려주는 것이다.
<role-name>이 하나도 없다면, 어떤 사용자도 접근할 수 없다.
<role-name>*</role-name>와 같이 되어 있다면, 모든 사용자가 접근 가능한 것이다.
<role-name>은 대소문자를 구분한다.

동일 자원(url)에 대해서 <security-constraint> 항목이 두개 이상이라면, 더 큰 범위에 설정 된 값을 따른다.

서블릿에서 특정 레벨을 가진 역할명을 임의로 하드 코딩해 뒀다면, 해당 레벨명과 DD(web.xml)에서 정의한 레벨명을 서로 맵핑할 필요가 있다.
즉 다음과 같은 경우가 될 것이다.

if(request.isUserInRole("Manager")) {
    ...
} else {
    ...
}

DD에는 Manager란 롤 이름이 없다.
따라서 위와 같은 경우에는 DD에 다음과 같이 설정해 줄 수 있다.

  <servlet>
      <security-role-ref>
          <role-name>Manager</role-name>
          <role-link>Admin</role-link>
      </security-role-ref>
  </servlet>

  <security-role>
      <role-name>Admin</role-name>
  </security-role>

위와 같이 하게 되면 Sevlet에서 사용하는 Manager란 role은 Admin으로 처리 되게 된다.
물론 실제 Manager란 role이 있다 하더라도 위와 같이 설정하게 되면 Manager는 Admin의 role 역할을 수행하게 된다.

* 인증의 종류

인증의 종류에는 총 4가지가 있다.

1. BASIC

BASIC 방법은 위 예제에서 사용해 보았다.
로그인 정보를 base64 encoding 한 상태로 보내기 때문에, 중간에 '이즈드롭퍼(Eavesdropper)'가 가로챌 경우 쉽게 노출 될 수 있다.

2. DIGEST

암호화 매커니즘을 이용하여 전송하기에 좀더 안전하지만, 많이 사용되지 않는 암호화 기법이라서 J2EE 컨테이너가 반드시 지원 안할 수도 있다.

3. CLIENT-CERT

클라이언트가 인증서를 가지고 공인키 인증방식을 사용하여 로그인 하는 방식이다.
클라이언트가 인증서를 가지고 있어야지만 로그인이 되므로, 비즈니스 환경에서만 사용된다.

4. FORM

일반적인 웹폼(html)을 이용하여 로그인 정보를 서버에 전송하는 방식이다.
암호화 되지 않은 로그인 정보를 그대로 전송하므로, '이즈드롭퍼(Eavesdropper)'가 가로챌 경우 쉽게 노출 될 수 있다.

위 4가지 방식을 구현하기 위해서는 위의 예제 가운데 있었던 <login-config> 부분을 아래와 같이 수정하면 된다.

  <login-config>
      <auth-method>BASIC</auth-method>
  </login-config>

  <login-config>
      <auth-method>DIGEST</auth-method>
  </login-config>

  <login-config>
      <auth-method>CLIENT-CERT</auth-method>
  </login-config>

  <login-config>
      <auth-method>FORM</auth-method>
      <form-login-config>
          <form-login-page>/loginPage.html</form-login-page>
          <form-error-page>/loginError.html</form-error-page>
      </form-login-config>
  </login-config>

우리가 일반적으로 보는 FORM 방식의 로그인 인증 방식은 base64 인코딩도 안된 상태로 로그인 정보가 전송 되기 때문에, 보안에 가장 취약하다.
하지만 https를 통해서 로그인 정보를 전송하게 된다면 안전하게 정보를 전달할 수 있다.
그렇게 하기 위해서 DD에 다음과 같이 설정할 수 있다.

  <security-constraint>
      <web-resource-collection>
          <web-resource-name>UpdateRecipes</web-resource-name>
          <url-pattern>/Beer/AddRecipe/*</url-pattern>
          <url-pattern>/Beer/ReviewRecipe/*</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
          <role-name>Admin</role-name>
          <role-name>Member</role-name>
      </auth-constraint>
      <user-data-constraint>
          <transport-guarantee>CONFIDENTIAL</transport-guarantee>
      </user-data-constraint>
  </security-constraint>

transport-guarantee에 들어갈 수 있는 값으로는 다음의 3가지가 있다.

NONE, INTEGRAL, CONFIDENTIAL

NONE : 디폴트 값으로 데이터 보호를 하지 않겠다는 의미이다.
INTEGRAL : 전송 중 데이터가 변경되지 않음을 보장한다는 의미이다. - 데이터 무결성(Data Integrity)
CONFIDENTIAL : 전송 중 데이터를 누구도 훔쳐보지 않았음을 보장한다는 의미이다. - 기밀성(Confidentiality)

그런데 데이터 무결성과 기밀성은 클라이언트나 서버 단에서 보장할 수 있는 것이 아니다.
따라서 위에서 설정한 INTEGRAL, CONFIDENTIAL 과 같은 내용은 다음과 같은 방법으로 보장한다는 의미이다.

전송 보장이 설정된 경우, 클라이언트가 해당 자원에 보안이 되지 않은 상태로 요청을 하게 되면, 컨테이너는 301 코드를 내려 보내게 된다.
301 코드는 클라이언트에게 새로운 주소로 새로운 요청을 하라는 말인데, 여기서는 주소가 아니라 전송 방식을 http에서 https로 수정해서 보내도록 요청하는 것이다.
https로 다시 요청이 들어오면 해당 자원에 인증이 필요함을 확인 후 401 코드를 내려 보낸다.
클라이언트는 401 코드를 확인 후 유저 정보를 안전하게 https로 전송하게 되는 것이다.


728x90
반응형

'Java > Servlet & JSP' 카테고리의 다른 글

필터 - RESPONSE  (0) 2009.04.28
필터 - REQUEST  (1) 2009.04.16
웹 애플리케이션 배포하기  (0) 2009.04.07
부모 자식 태그간의 통신  (0) 2009.03.26
클래식 커스텀 태그  (0) 2009.03.26