WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

パスワードに有効期限を設定

by WebSurfer 2011年10月22日 16:31

Forms 認証でパスワードに有効期限を設定するにはどうしたらよいでしょうか?

有効期限切れパスワードの変更

有効期限を知るためのデータ(例えば、最初にアカウントを作成した日時またはパスワードを変更した日時)をデータベースに記録し、ユーザーがログインした際にそのデータを取得して期限が過ぎているか否かの判断をするという操作が必要です。

組み込みの機能にそのようなものはないと思っていましたが、よく調べてみたら、標準の Forms 認証用 SQL Server データベースの aspnet_Membership テーブルに LastPasswordChangedDate というフィールドがあり、それを取得するために MembershipUser.LastPasswordChangedDate プロパティ が用意されていました。

これを利用すれば、パスワードに有効期限を設け、ユーザーがログインする際に有効期限が過ぎていたら ChangePassword コントロール を表示して、ユーザーにパスワード変更を促すというような操作が簡単に可能になります。

以下のコードは、パスワードの有効期限を 90 日とし、ユーザーがログインする際に 90 日を過ぎていたら Login コンロトールを非表示にするとともに ChangePassword コントロールを表示し(上の画像がそれです)、パスワードを変更したら Login コンロトールを再度表示して新しいパスワードでログインするというシナリオになっています。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Security" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  double expireDays = 90;

  protected void Login1_LoggingIn(object sender, 
    LoginCancelEventArgs e)
  {
    MembershipUser user = 
      Membership.GetUser(Login1.UserName);

    if (user == null)
    {
      // MembershipUser が取得できなければその後の
      // 処置は Login コントロールに任せる。
      return;
    }

    if (user.LastPasswordChangedDate.AddDays(expireDays) <
      DateTime.Now)
    {
      Msg.Visible = true;
      Msg.Text = "パスワードが有効期限を過ぎています。";
      ChangePassword1.UserName = Login1.UserName;
      ChangePasswordPanel.Visible = true;
      LoginPanel.Visible = false;
      e.Cancel = true;
    }
    else
    {
      Msg.Visible = false;
    }
  }

  protected void _ChangingPassword(object sender, 
    LoginCancelEventArgs e)
  {
    if (ChangePassword1.CurrentPassword == 
      ChangePassword1.NewPassword)
    {
      Msg.Visible = true;
      Msg.Text = "新旧パスワードが同じです。";
      e.Cancel = true;
    }
    else
    {
      Msg.Visible = false;
    }        
  }

  protected void _ChangedPassword(object sender, EventArgs e)
  {
    ChangePasswordPanel.Visible = false;
    LoginPanel.Visible = true;
    Msg.Visible = true;
    Msg.Text = "新しいパスワードでログインしてください。";
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Login</title>
</head>
<body>
  <form id="form1" runat="server">
    <h3>有効期限つきパスワードでのログイン操作</h3>

    <asp:Label id="Msg" 
      runat="server" 
      ForeColor="maroon"  
      Visible="False" />
    <br />

    <asp:Panel ID="LoginPanel" 
      runat="server">
      <asp:Login ID="Login1" 
        runat="server" 
        DisplayRememberMe="False" 
        OnLoggingIn="Login1_LoggingIn">
      </asp:Login>
    </asp:Panel>

    <asp:Panel ID="ChangePasswordPanel" 
      runat="server" 
      Visible="False">
      <asp:ChangePassword ID="ChangePassword1" 
        runat="server" 
        DisplayUserName="True" 
        OnChangingPassword="_ChangingPassword" 
        OnChangedPassword="_ChangedPassword" 
        CancelDestinationPageUrl="~/Default.aspx">
      </asp:ChangePassword>
    </asp:Panel>
  </form>
</body>
</html>

参考にしたのは MembershipUser.LastPasswordChangedDate プロパティChangePassword.CurrentPassword プロパティ のサンプルコードです。

Tags: ,

Authentication

厳密な名前付きアセンブリ

by WebSurfer 2011年9月13日 22:37

サンデープログラマーの自分が、作成したアセンブリに厳密名を付けることはないですが、わけあって厳密名の付け方についていろいろ調べましたので、備忘録として書いておきます。

まず、厳密名 (Strong Name) とは何かですが、MSDN ライブラリ 厳密な名前付きアセンブリ によると「単純テキスト名(アセンブリ名)、バージョン番号、カルチャ情報から成るアセンブリの識別子と、公開キーおよびデジタル署名」だそうです。

web.config で、ライブラリへの参照や HTTP モジュールの参照を設定する際以下のようにしますが、これが厳密名を指定したことになるようです。

<add 
  assembly="System.Core, 
  Version=3.5.0.0, 
  Culture=neutral, 
  PublicKeyToken=B77A5C561934E089"/>

<add 
  name="ScriptModule" 
  type="System.Web.Handlers.ScriptModule, 
    System.Web.Extensions, 
    Version=3.5.0.0, 
    Culture=neutral, 
    PublicKeyToken=31BF3856AD364E35"/>

厳密な名前を付ける理由や、それが推奨されるケース/されないケースについては MSDN ライブラリの マネージ アプリケーションに対する厳密な名前による署名 に書いてあるのでそちらを参照してください。手抜きですみません。(笑)

厳密名のうち、アセンブリ名、バージョン番号、カルチャ情報は開発者が自分で設定する(というより、デフォルトで設定されている)ものです。下の画像にそれらが示されています。

Visual Studio のアセンブリ情報の画面

厳密名のなかで、公開キーとデジタル署名、および公開キートークン (PublicKeyToken) が具体的に何かよく分かってなかったので調べてみました。

アセンブリにデジタル署名するには、まず、秘密キー/公開キーペアから成るキーファイルを作成します。次に、アセンブリの一部分のハッシュを作成し、それを秘密キーで暗号化し、公開キーとともにアセンブリに追加(署名)します。

アセンブリをロードする際、アセンブリに含まれる公開キーと秘密キーで暗号化されたハッシュを取り出し、暗号化されたハッシュを公開キーで復号します。次に、ロードするア���ンブリ自体のハッシュを作成して、それと復号したハッシュとを比較して一致することを確認します。これにより正しいアセンブリがロードされることを保障できます。加えて、改ざんされていないかどうかもチェックできますので、セキュリティも向上するということのようです。

さて、キーファイルの作成とアセンブリの署名をどのように行うかですが、Visual Studio 2008, 2010(未確認ですが、たぶん 2005 も)には、プロパティページに[署名]タブがありますので、それでキーペアの作成が可能です。キーペアを作成した上でビルドすればアセンブリに署名できます。下の画像を参照してください。ちなみに以前(たぶん Visual Studio 2003 以前)は Sn.exe (厳密名ツール)Al.exe (アセンブリ リンカー) を使って手動で行っていたようです。

Visual Studio の厳密な名前キーの作成画面

上の画面で[アセンブリの署名]チェックボックスにチェックを入れ、[厳密な名前のキー ファイルを選択してください]ボックスの一覧の[<新規作成...>」を選択すると[厳密な名前キーの作成]ダイアログボックスが表示されます。

[厳密な名前キーの作成]ダイアログボックスで、新しいキーファイルの名前を入力し、さらに、必要に応じてパスワードを入力します。パスワードを指定すると、Personal Information Exchange (.pfx) ファイルが作成され、指定しないと Strong Name Key (.snk) ファイルが作成されます。[OK]をクリックすると、以下の画像のようにキーファイルが作成されます。

Visual Studio で作成したキーファイル

.pfx ファイルと .snk ファイルの中身が具体的にどのように違うかは調べていませんが、MSDN ライブラリ [キー ファイルのインポート] ダイアログ ボックス によると、キーファイルをインポートするには .pfk ファイル形式にする必要があるそうです。

アセンブリが正しくロードされるという保障も、改ざんが防止できるというセキュリティ機能も、キーファイルに含まれる秘密キーをアセンブリの提供者(ソフトウェア会社)のみが持っているという前提の上に成り立っています。従って、このキーファイルはソフトウェア会社に固有となるようにし、流出しないよう厳重に管理する必要があるそうです。

上の画像のように、キーファイルを作成してプロジェクトに配置した後は、ビルドすれば自動的にキーファイルに含まれる公開キーと秘密キーを使ってアセンブリに署名されます。

そのようにして厳密名を付けたアセンブリを参照する例を書きます。例えば HTTP モジュールを dll として bin フォルダに配置する場合を考えます。web.config でアセンブリ名、バージョン番号、カルチャ情報、公開キートークンを以下のように設定します

<add 
  name="HelloWorldModule" 
  type="MyHttpModule.HelloWorldModule, 
    MyHttpModule, 
    Version=1.0.0.0, 
    Culture=neutral, 
    PublicKeyToken=90cefab791a0b403"/>

アセンブリ名、バージョン番号、カルチャ情報はすぐ分かりますが、問題は公開キートークン(PublicKeyToken)です。公開キートークンとは、公開キーにハッシュをかけて抽出した値の最後の 8 バイトです。公開キーのサイズは 128 バイトもあるので、それをそのまま参照に使うと、参照しているアセンブリが 1 つ増えるたびに 128 バイトずつサイズが増えてしまうことになります。それではサイズが増えすぎて困るので、そのようにしているらしいです。

公開キートークンを取得するには Sn.exe (厳密名ツール) を利用します。コマンドラインから -T オプションを使用して以下のようにします。

sn.exe を使用して公開キートークンを取得

このようにして web.config で厳密名(アセンブリ名、バージョン番号、カルチャ情報、公開キートークン)を指定すると、アセンブリをロードする際それらの 項目をチェックし、矛盾があると実行時に以下の画像ようなエラーが出ます。

厳密名が異なる場合のエラー画面

最後に、Visual Studio のプロパティページの[署名]タブの[遅延署名のみ(Y)]について簡単に書きます。

これは組織が厳重に保護している(すなわち、一般の開発者にはアクセスが許可されていない)キーファイル(特に秘密キー)を使用しなくても、開発に支障がないようにするためのオプションです。

秘密キーがないと署名(アセンブリのハッシュを取って、それを秘密キーで暗号化した情報のアセンブリへの埋め込み)ができません。それでは開発に支障が出るので、アセンブリに署名をせずに公開キーだけを埋め込み、一時的に署名の検証を無効にして開発を進められるようにします。開発が完了した後、秘密キーにアクセスできる限られた者が、秘密キーを使って署名してリリースします。後で署名するので「遅延署名 (Delay Sign)」と言うらしいです。

詳しくは、第2回 アセンブリのアイデンティティ が参考になると思います。

Tags:

.NET Framework

パスの設定

by WebSurfer 2011年9月4日 16:07

ASP.NET ベースの Web アプリケーションにおけるパスの設定において、サーバーコントロールではないページの要素(img など)で、ルート演算子 (~) が使えない場合のパスの設定方法について書きます。

VirtualPathUtility のメソッドの使用結果

標準的なパスの設定方法については、MSDN ライブラリの ASP.NET Web サイトのパス に詳しく書いてあります。

そのページに書いてあるように、~ 演算子を使用すると、アプリケーション間での移植や、同一アプリケーション内でもページを別のフォルダーへ移動する場合、リンクや相対パスの維持が容易になります。

だたし、~ 演算子はサーバーコントロールにしか使用できません。しかしながら、img などのサーバーコントロールではない要素でも ~ 演算子を使用する、もしくは、それに代わる方法があります。

  1. runat="server" 属性を追加してサーバーコントロールにしてしまう。
  2. VirtualPathUtility.ToAbsolute メソッド を利用して絶対パスに変換する。

前者は簡単で、<img runat="server" src="~/Images/sample.jpg" ... のようにして ~ 演算子を使うことができます。

後者は、VirtualPathUtility.ToAbsolute メソッドを利用して ~ に相当するパスを取得する方法です。例えば、アプリケーションのエイリアスが MsdnTest の場合、引数に "~" を設定すると戻り値は "/MsdnTest" になります。

VirtualPathUtility.ToAbsolute メソッドを利用した方法を汎用的に使えるようにするには、以下のようにするのが良いと思います。

まず変換したい仮想パスを、以下のように web.config ファイルの appSettings 要素に設定します。下のケースでは、例えばアプリケーションのエイリアスが MsdnTest の場合、"/MsdnTest/" という文字列を取得することを考えています。

<appSettings>
  <!-- value は配置に応じて適宜変更 -->
  <add key="VirtualPath" value="~/"/>
</appSettings>

次に、以下のようなクラスを定義し、その RelativeWebRoot プロパティをコード表示ブロック <%= %> で html 要素の属性(例えば、img 要素なら src 属性)に設定します。

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Configuration;

namespace MyWebsite
{
  public static class Utils
  {
    private static string _relativeWebRoot;

    public static string RelativeWebRoot
    {
      get
      {
        if (_relativeWebRoot == null)
        {
          _relativeWebRoot =
            VirtualPathUtility.ToAbsolute(
            WebConfigurationManager.AppSettings["VirtualPath"]);
        }

        return _relativeWebRoot;
      }
    }
  }
}

上記の 2 つの方法を使ったサンプルコードを以下に示します。加えて、参考に、VirtualPathUtility の各メソッドの使用例も含めました。上の画像が実行結果です。

ちなみに、このページは 155_VirtualPathUtilityExample.aspx という名前でアプリケーションルート直下の 100_Over フォルダにあります。アプリケーションのエイリアスは MsdnTest です。画像 image1.jpg および image2.jpg は、アプリケーションルート直下の Images フォルダにあります。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  protected void Page_Load(object sender, EventArgs e)
  {
    StringBuilder sb = new StringBuilder();
    String path = Context.Request.FilePath;
    sb.Append("FilePath = " + path + "<br />");
    sb.Append("GetFileName = " + 
      VirtualPathUtility.GetFileName(path) + 
      "<br />");
    sb.Append("GetExtension = " + 
      VirtualPathUtility.GetExtension(path) + 
      "<br />");
    sb.Append("GetDirectory = " + 
      VirtualPathUtility.GetDirectory(path) + 
      "<br />");
    sb.Append("IsAbsolute = " + 
      VirtualPathUtility.IsAbsolute(path).ToString() + 
      "<br />");
    sb.Append("IsAppRelative = " + 
      VirtualPathUtility.IsAppRelative(path).ToString() + 
      "<br />");
    string toAppRelative = 
      VirtualPathUtility.ToAppRelative(path);
    sb.Append("ToAppRelative = " + toAppRelative + 
      "<br />");
    sb.Append("ToAbsolute = " +
      VirtualPathUtility.ToAbsolute(toAppRelative) + 
      "<br />");
    Literal1.Text += sb.ToString();
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>VirtualPathUtility Example</title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:Literal ID="Literal1" runat="server">
    </asp:Literal>

    <img id="image1" 
      runat="server" 
      alt="イメージ1" 
      src="~/Images/image1.jpg" />

    <img 
      alt="イメージ2" 
      src='<%=MyWebsite.Utils.RelativeWebRoot + 
        "Images/image2.jpg"%>' />
  </div>
  </form>
</body>
</html>

上記のコードから ASP.NET がレンダリングした img 要素は以下のようになります。

<img src="../Images/image1.jpg" id="image1" alt="イメージ1" />
<img 
  alt="イメージ2" 
  src='/MsdnTest/Images/image2.jpg' />

Tags:

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar