GlassFish の BASIC認証 とセキュリティロール

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