by WebSurfer
2011年11月25日 22:57
ASP.NET の標準の Forms 認証で、Login コントロールを使用してログインのページを作ると、デフォルトで [次回のために保存] チェックボックスが表示されます。
これにチェックを入れてログインすると永続的な cookie が発行される、と MSDN ライブラリなどに書いてあります。
永続的というのは、cookie の有効期限が 50 年だからというのはあちこちで目にするので(例:Login.RememberMeSet プロパティ、ASP.NET の組み込み機能を活用し、Web 攻撃を回避する)、信じて疑わなかったのですが、どうやら間違い(修正漏れ?)のようです。
[次回のために保存] チェックボックスをオン(Login.RememberMeSet を true)に設定してログインすると、Set-Cookie: ヘッダーに expires が指定されますが、それは 50 年先ではなく、timeout で設定した時間(デフォルトで 30 分)だけ先の時間になりました。
「永続的ってデフォルトで 30 分なの?」って感じです。(苦笑)
MSDN ライブラリの authentication の forms 要素 (ASP.NET 設定スキーマ) のメモに "ASP.NET V1.1 の場合、永続的な Cookie は timeout 属性の設定に関係なくタイムアウトしません。一方、ASP.NET V2.0 の場合、永続的な Cookie は timeout 属性に従ってタイムアウトします。" とありますが、どうやらこれが正しいようです。
なお、認証クッキーの中にある認証チケットの有効期限は認証クッキーの有効期限と同じに設定され、slidingExpiration も期待通り動く(有効期限が延長されたクッキーが発行される)のは確認しました。
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 プロパティ のサンプルコードです。
by WebSurfer
2011年8月6日 14:59
今回は URI に認証チケットを追加して送る場合の Forms 認証のログインとログアウト動作について調べてみました。クッキーを使用した場合については、先の記事 Forms 認証のログイン・ログオフ動作 を参照してください。
web.config で authentication の forms 要素 の cookieless 属性を AutoDetect に設定し、Firefox 5 で「サイトから送られてきた Cookie を保存する」のチェックを外してテストしました。
URI を使用する場合の問題は、上の画像に示したように、ブラウザのアドレスバーに認証チケットが表示されるので、認証チケットが一般ユーザーの目に直接さらられてしまうことです。というわけで、できればクッキーのみを使うようにしたほうが無難だと思われます。
***** ログイン *****
-
匿名アクセスが許可されているページ default.aspx で、ログインしていない状態から LoginStatus をクリックする。
-
JavaScrip が起動してポストバック(default.aspx への POST 要求)がかかる。
-
サーバーからリダイレクト指示(HTTP/1.1 302 Found)が返ってくる。応答ヘッダーに含まれる Location(リダイレクト先)は web.config で指定したログインページ login.aspx となる。
-
ブラウザはリダイレクト先のページ login.aspx を GET 要求する。・・・ここまではクッキーを使った場合と同じ。
-
再び、サーバーからリダイレクト指示(HTTP/1.1 302 Found)が返ってくる。応答ヘッダーに含まれる Location は同じく login.aspx だが、クエリ文字列に AspxAutoDetectCookieSupport=1 が追加されている。
-
再び、ブラウザはリダイレクト先のページ login.aspx を GET 要求する。前回の要求の時と違うのは、クエリ文字列に AspxAutoDetectCookieSupport=1 が追加されていること。
-
サーバーから応答(login.aspx)が返ってくる。
-
ユーザーが ID とパスワードを入力して[ログイン]ボタンをクリックする。
-
[ログイン]ボタンのクリックで login.aspx にポストバックがかかる。ただし、form 要素の action 属性に (X(1))/ が追加されているので、POST 先は login.aspx ではなく (X(1))/login.aspx となる。クエリ文字列
にも AspxAutoDetectCookieSupport=1 が追加されている。
-
サーバーからリダイレクト指示(HTTP/1.1 302 Found)が返ってくる。応答ヘッダーに含まれる Location は (X(1)F(mfZdwXpqsmZ ... 2Qg2g1))/default.aspx となり、mfZdwXpqsmZ ... 2Qg2g1 という認証チケットが追加されている。
-
リダイレクト指示を受けて、ブラウザは (X(1)F(mfZdwXpqsmZ ... 2Qg2g1))/default.aspx を GET 要求する。
-
サーバーから応答(default.aspx)が返ってくる。認証チケットが URI に含まれているのでユーザーは認証済みとなる。
***** ログアウト *****
-
ログインしている状態のページ (X(1)F(mfZdwXpqsmZ ... 2Qg2g1))/default.aspx で LoginStatus をクリック。
-
JavaScrip でポストバックがかかる。
-
サーバーからリダイレクト指示(HTTP/1.1 302 Found)が返ってくる。Location は (X(1))/default.aspx となる。この時、URI から認証チケットは削除されている。
-
ブラウザは (X(1))/default.aspx を GET 要求する。
-
サーバーから応答 (X(1))/default.aspx が返ってくる(ログアウトした後も URI に (X(1)) は含まれる)。ユーザーはログアウト状態となる。