こんばんは。yumix と申します。

前回、これっきりにしてくださいと言ったはずなのに、またゲスト寄稿させて頂くことになりました。今度こそ最後だと思いますが、どうぞよろしくお付き合いください。

さて、前回は、GlassFishのBASIC認証とセキュリティロールと題しまして、BASIC 認証の大まかな流れと、認証・許可・セキュリティロールといったことについてご説明しました。簡単に復習しますと、

認証とは...
  • サーバにアクセスしてきたユーザが本当にそのユーザか (そのユーザを騙る別人でないか) を確認すること。
  • 認証は原則としてそれぞれのサーバ単位で行う。認証されたユーザはグループという単位でまとめられることが多い。
許可とは...
  • 認証したユーザに対して、やってよいこと (あるいはやってはいけないこと) を定めること。
  • 許可はアプリケーション単位で行う。許可はセキュリティロール (あるいは単にロール) ごとに決める。
  • 認証したユーザは、アプリケーションごとに glassfish-web.xml / sun-web.xml などでロールに関連付けられる。
今回は認証について見てゆきます。

まず、一般論として、認証はどのタイミングで行われるのでしょうか?

例えば Apache HTTP Server の認証について考えてみます。ブログ主は iPlanet Web Server にご執心のようでとにかく iPlanet を持ち出そうとしますが、私も iPlanet は良い製品だとは思いますがそこまでの思い入れもないので、素直に Apache を取り上げます。

さて、Apache で公開するリソースに対して認証を課す場合には、大きく次の二通りの方法があります。
  • httpd.conf に対象となるリソースを <Directory /> で指定する。
  • 個別のリソースに対して .htaccess ファイルを置く。
Apache の場合、実際にはこれらの設定の中で許可についても指定してしまうため、純粋に認証だけを設定するわけではないのですが、これらの設定がきっかけとなり BASIC 認証や DIGEST 認証が行われるようになります。

これを見る限り Apache では許可を設定した場合には必ず認証も一緒についてきます。この傾向は Web のシステムでは一般的のようです。私も Apache と GlassFish と Tomcat しか知らない身なので何とも言えませんけど。

そもそも、認証だけ行っても、その後の許可がなければなんの意味も持たないので、許可を設定すれば認証もされるようになるという流れは、ある意味自然なのかもしれません。

Apache の場合は以上ですが、GlassFish についてはどうでしょう?

これは GlassFish に限らず Tomcat にも共通するのですが、アプリケーションの web.xml に <security-constraint /> が存在し、その中に許可の記述が存在すれば、認証が行われるようになります。

許可の対象としてリソースの URL パターン (/admin/* など)、許可として HTTP のメソッド (GET, POST, PUT, DELETE)、許可の対象としてロール名を指定します。例えば、こんな感じ:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
  <!-- snip -->
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>ReadOnly</web-resource-name>
      <url-pattern>/protected/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
  </security-constraint>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Admin</web-resource-name>
      <url-pattern>/admin/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
      <http-method>PUT</http-method>
      <http-method>DELETE</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin</role-name>
    </auth-constraint>
  </security-constraint>
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>file</realm-name>
  </login-config>
</web-app>
この例では、<security-constraint /> の後に <login-config /> があり、そこで BASIC 認証を使うことと file という名前のレルムを使うことを指定している、ように取れます。実際にそうなのですが。

<auth-method> を BASIC 以外、例えば DIGEST などにすると、その方式で認証が行われるようになります。

レルム、というのは認証に使う仕組み、例えばユーザの一覧はどこから取得するのか (GlassFish ではファイル、JDBC、LDAP、UNIX PAM 認証、Solaris 認証が使用できます)、などをまとめたものです。

GlassFish はアプリケーション用に準備された認証用ファイルを使うファイルレルムがデフォルトです。GlassFish の管理用にも別のファイルレルムが存在します。ファイルレルムのユーザ一覧は管理コンソールで編集することができます。

ちなみに、ファイルレルムのユーザ名とパスワードを記録したファイルは暗号化されていますので、そのままでは読めません。あしからず。

レルムは GlassFish でいくつも作成することができます。ファイルレルムばかり 10 個作る、といったことも可能です。レルムが複数存在する場合はデフォルトレルムを選択する必要がありますが、デフォルトレルムで BASIC 認証を行う分には web.xml の <login-config /> を省略できるようになります。

Servlet 3.0 以降では、web.xml のほとんどを Servlet 本体のアノテーションで代替できるようになりました。<security-constraint /> の代わりに代替となる @ServletSecurity アノテーションを Servlet 本体に設定しても、GlassFish の認証 (と許可) が働くようになります。

今回は、
  • HTTP の認証は、リソースへの許可を設定した箇所で (自動的に) 行われる。
  • GlassFish の認証は、アプリケーションにおいて web.xml の <servlet-constraint /> か、代替となるアノテーションが存在する場合に行われる。
の二点についてご説明しました。

この調子だとブログ主が図に乗って第三の依頼を出しかねないので、@misaka_shiori bot と遊んでいる暇があったら、21 の小娘に押し付けないで自分で記事を書け! とブログ主に言ってやってください。
はじめまして、yumix と申します。
ブログ主に依頼されまして、GlassFish の BASIC 認証とセキュリティロールについてゲスト寄稿させて頂くことになりました。どうぞよろしくお願いします。

まず、認証とは何でしょう?平たく言うと本人確認です。例えばこのブログのシステムに私 yumix がアクセスするとき、それが本当に yumix なのか (yumix の名を騙る偽者だったりしないか) を確認するのが認証 (authentication/atn) です。このシステムの場合はユーザ名とパスワードを求められてきたので、ブログ主から事前に連絡のあったユーザ名と仮パスワードで認証しました。

現実世界でも似たようなことをします。

例えば携帯電話の機種変更を行うとき、まず運転免許証などの本人確認書類の提示を求められます。私は免許を持っていないので、いつも海外渡航もしないのに作ったパスポートを提示していますが、ショップの人は本来 yumix だけが持っているはずのパスポートが提示されたこと、それから記載事項が私の申告やショップ側のデータベース上の登録情報と一致していることなどから、来店したのが yumix 本人であると判定するわけです。

さて、コンピュータの世界の話に戻りましょう。

タイトルにもある BASIC 認証とは、HTTP がプロトコル仕様の一部として持っている認証の方法です。HTTP の認証方式は他にも DIGEST 認証などがありますけど、BASIC 認証は HTTP 1.0 の仕様書 (RFC 1945) の Section 11 に規定されている、文字通り基本的な方式です。

参考まで、HTTP 1.1の仕様書 (RFC 2616) では本文からBASIC認証の仕様が消え、BASIC 認証と DIGEST 認証の仕様書 (RFC 2617) を参照するようになりました。

BASIC 認証は以下の流れで行われます。

  1. クライアント: サーバのリソース (例えば /private/index.html) に対してリクエストを送信する
  2. サーバ: クライアントのリクエストを見て、Authorization ヘッダがない (このタイミングでは通常、クライアントが Authorization ヘッダをリクエストに含めることはない) ことを確認し、レスポンスコード 401 を返す
  3. クライアント: サーバからレスポンスコード 401 を受け取ると、リクエストに Authorization ヘッダをつけてもう一度リクエストを送信する
  4. サーバ: クライアントのリクエストを見て、Authorization ヘッダがあるので、ヘッダの内容をもとに認証を行う → 成功すればレスポンスコード 200 でリソースを返す (失敗なら再び 401)
  5. クライアント: 無事にリソース (/private/index.html) を受け取る (めでたしめでたし)

BASIC 認証では Authorization ヘッダの内容が Basic [ユーザ ID とパスワードを ":" でつないだものを BASE64 エンコードしたもの] となります。

具体的には、ユーザ ID が yumix、パスワードが password で認証した場合、yumix:password を BASE64 でエンコードすると eXVtaXg6cGFzc3dvcmQ= になりますから、
Authorization: Basic eXVtaXg6cGFzc3dvcmQ= 
というヘッダが付けられることになります。もうお気づきの方もいらっしゃるでしょうが、BASIC認証ではパスワードが暗号化されません。たまに勘違いされますが、BASE64 は符号化の方式であって暗号化のアルゴリズムではありません。

参考まで、DIGEST 認証ではヘッダの内容が Digest で始まり、パスワードなどはそのものではなく、MD5 のハッシュ関数で作成したメッセージダイジェストを送信します。

メッセージダイジェストはその値が異なれば元のデータも異なる、値が同じであれば元のデータも多分同じ、という性質を持ちます。多分同じ、というのがミソで、メッセージダイジェストの値が同じでも元のデータが違っているケースが絶対存在することは数学的に証明されています (ハッシュの衝突と言われるもの) が、そういうことが実用上無視できるくらい稀なアルゴリズムがメッセージダイジェストに使用されます。

なお、MD5 には意図的なハッシュ衝突が可能という脆弱性があり、特にパスワードを MD5 で変換して処理することは危険だと言われています。

それはともかく、BASIC 認証や DIGEST 認証は HTTP で標準として用意されているものですので、GlassFish 固有の機能というわけではありません。むしろ Web アプリケーションサーバを標榜するのであれば持っていて当然の機能といえます。

ここまで認証のことをお話ししましたので、次にセキュリティロールのお話をします。その前に、セキュリティロールのもとになる許可について触れておきます。

許可 (authorization/atz) は認可とも訳され、認証した相手がやってよいこと (またはやってはいけないこと) を定めることを言います。先ほど同様このブログの認証の話に絡めると、認証した yumix に対してやってよいことを定めるのが許可です。具体的には、yumix はこのブログについて、この記事の作成と編集を許可されています (その他のこと、例えば公開はできないようです)。

一番極端な許可は All or Nothing です。つまりすべてを許すか、まったく許さないか (後者はいわゆるアクセス拒否) です。認証についても同様で、全員を受け入れる (匿名アクセス) か、誰も受け入れない (現実にはあまりないですが) ケースがあります。

許可は本来、認証した人に対して与えるものです。しかし、認証した人ごとに許可を与えるのはあまり効率的ではありません。

ユーザが数人で、許可の種類も数種類程度なら問題ないでしょう。でもユーザーが数百人だったら? あるいは許可の種類が 1,000 種類あったら? 

ユーザーが数百人なんてざらで、私の在籍する大学では数千人規模です。世の中にはもっと大きな組織もたくさんあるでしょう。

許可の種類はアプリケーションごとに考え方が違ってくるので、複数のアプリケーションを配備できる GlassFish では許可がいくつ存在しうるかをサーバ管理者が予想するのは困難です。

幸いなことに、数千人のユーザがいる大学でも、その内訳は大多数が学生、それから教員、そして大学職員、あと大学職員ではありますがシステム管理者くらいに分類できます。

また、違った見方をするなら、学内アクセスしかできないユーザ、自宅から VPN でアクセスできるユーザ、特定の施設のみ利用できるユーザ (図書館の利用パスを持っている卒業生など)、ゲストユーザ (オープンキャンパスに参加した受験生など) という分類もできます。

このような分類はアプリケーションごとに必要とされるものが違ってくるでしょうが、少なくとも許可の数を把握できるレベルの数に抑えることができるでしょう。

最初の例でいう学生や教員、2番目の例でいう学内アクセスしかできないユーザや VPN でアクセスできるユーザなど、ユーザの役割 (role) に注目した分類であることから、これらをセキュリティロール、あるいは単にロールといいます。

続いて認証と許可の関係について。

認証はサーバにおいて、ユーザ単位で行います。ユーザは、ユーザ情報を管理するうえで何らかの分類がなされていることが多く、これをグループ等と呼んでいます。GlassFish ではそのものずばりグループと呼んでいます。

許可はアプリケーションにおいて、ロール単位で行います。

GlassFish は、アプリケーションでどのようなロールが定義されているのかを、それ自身は持っていません。ましてや、自身が認証したユーザがどのアプリケーションのどのロールに割り当てられるかなど知りません。

何らかの形でユーザとロールの対応付けが必要になります。それを行うのはアプリケーション側の glassfish-web.xml または sun-web.xml という設定ファイルです。このファイルでロールとユーザの対応付け、またはロールとグループの対応付けを行います。例えばこんな感じのファイルを、web.xmlの隣に置いてあげます。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
  <context-root>/SampleApplication</context-root>
  <!-- user to role mapping -->
  <security-role-mapping>
  	<role-name>student</role-name>
  	<user-name>yumix</user-name>
  	<user-name>mako</user-name>
  </security-role-mapping>
  <security-role-mapping>
  	<role-name>programmer</role-name>
  	<user-name>btnrouge</user-name>
  </security-role-mapping>
  <!-- group to role mapping -->
  <security-role-mapping>
  	<role-name>users</role-name>
  	<group-name>users</group-name>
  </security-role-mapping>
  <class-loader delegate="true"/>
  <jsp-config>
    <property name="keepgenerated" value="true">
      <description>Keep a copy of the generated servlet class java code.</description>
    </property>
  </jsp-config>
</glassfish-web-app>
この辺は WebLogic とかだとまた違ってくると思います。また、Tomcat ではアプリケーションではなくサーバ側で設定 (tomcat-users.xml) します。

これで、BASIC 認証とセキュリティロールのことは一通りご説明できたかと思います。もう二度と私がゲスト寄稿させて頂くことがないよう、ブログ主には正確な記述をお願いしたいと思います。

カテゴリ

  • インターネット
    • Web
    • インターネットサービス
    • セキュリティ
  • Java
  • JavaScript
  • C/C++/その他
  • Windows
    • Windows Server
    • .NET Framework
  • Unix/Linux
    • Solaris
  • 開発環境
    • Eclipse
    • NetBeans
    • Visual Studio
  • データベース
    • Oracle
  • 雑談
    • 勉強会
    • 書評
    • その他
  • ゲスト起稿 (2)

ウェブページ

  • images
  • about