セッション管理の不備に対する根本的対策は、以下の記事を参照してください。
これを ASP.NET で実現すると以下のようになります。
■1.推測困難なセッション ID を利用する
ASP.NET のセッション ID の生成は特に問題ないので、ASP.NET のセッション ID 作成機能を利用します。
■2.セッション ID を URL に含めない
ASP.NET はデフォルトでセッション ID を URL に含みません。
■3.HTTPS 通信で利用する Cookie には secure 属性を付与する
ASP.NET で Cookie に secure 属性を付与するには以下のように記述します。HttpOnly 属性も付与したほうがよいでしょう。
・C#
HttpCookie cookie = new HttpCookie("sample", "value"); cookie.Secure = true; cookie.HttpOnly = true; Response.Cookies.Add(cookie);
・VB.NET
Dim cookie As HttpCookie = New HttpCookie("sample", "value") cookie.Secure = True cookie.HttpOnly = True Response.Cookies.Add(cookie)
レスポンスヘッダーには以下のように出力されます。
Set-Cookie: sample=value; path=/; secure; HttpOnly
セッション ID に secure 属性と HttpOnly 属性を付与する方法は以下の記事を参照してください。
■4-1.ログイン後にセッションを新規に開始する
■4-2.ログイン後にセッション ID とは別の秘密情報を持ち、各ページでその値をチェックする
ASP.NET にはセッション ID を振り直す機能がないため 4-1 は採用できません。そのため、ASP.NET では 4-2 の方法を採用します。
秘密情報は CSRF の独自トークンと同じ要件なので、CSRF の独自トークンを作成するクラスを流用します。詳しくは以下の記事を参照してください。
下図の簡単なログイン機能を作成します。
・Login.aspx(C#)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace TestCS { public partial class Login : System.Web.UI.Page { protected void ButtonLogin_Click(object sender, EventArgs e) { //パラメーター取得 string id = this.TextBoxID.Text; string passwword = this.TextBoxPassword.Text; //簡易ログイン if (id == "yamada" && passwword == "pass") { //ログイン成功 //トークン作成 string token = Token.GetToken(); HttpCookie tokenCookie = new HttpCookie("token", token); tokenCookie.Secure = true; tokenCookie.HttpOnly = true; Response.Cookies.Add(tokenCookie); //セッションに値格納 Session["id"] = id; Session["token"] = token; //リダイレクト Response.Redirect("Welcome.aspx"); } } } }
・Login.aspx(VB.NET)
Public Class Login1 Inherits System.Web.UI.Page Protected Sub ButtonLogin_Click(sender As Object, e As EventArgs) Handles ButtonLogin.Click 'パラメーター取得' Dim id As String = Me.TextBoxID.Text Dim password As String = Me.TextBoxPassword.Text '簡易ログイン' If id = "yamada" And password = "pass" Then 'ログイン成功' 'トークン作成' Dim myToken As String = Token.GetToken() Dim tokenCookie As HttpCookie = New HttpCookie("token", myToken) tokenCookie.Secure = True tokenCookie.HttpOnly = True Response.Cookies.Add(tokenCookie) 'セッションに値格納' Session("id") = id Session("token") = myToken 'リダイレクト' Response.Redirect("Welcome.aspx") End If End Sub End Class
・Tokenクラス(C#)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Text; using System.Security.Cryptography; namespace TestCS { public class Token { private static int TOKEN_LENGTH = 16; //16*2=32バイト //32バイトのトークンを作成 public static string GetToken() { byte[] token = new byte[TOKEN_LENGTH]; RNGCryptoServiceProvider gen = new RNGCryptoServiceProvider(); gen.GetBytes(token); StringBuilder buf = new StringBuilder(); for (int i = 0; i < token.Length; i++) { buf.AppendFormat("{0:x2}", token[i]); } return buf.ToString(); } } }
・Tokenクラス(VB.NET)
Imports System.Security.Cryptography Public Class Token Private Shared TOKEN_LENGTH As Integer = 15 '16*2=32バイト' '32バイトのトークンを作成' Public Shared Function GetToken() As String Dim token As Byte() = New Byte(TOKEN_LENGTH) {} Dim gen As RNGCryptoServiceProvider = New RNGCryptoServiceProvider() gen.GetBytes(token) Dim buf As StringBuilder = New StringBuilder() For i As Integer = 0 To token.Length - 1 buf.AppendFormat("{0:x2}", token(i)) Next Return buf.ToString() End Function End Class
・Welcome.aspx(C#)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace TestCS { public partial class Welcome : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { //トークンチェック HttpCookie token = Request.Cookies["token"]; if ((token == null) || (Request.Cookies["token"].Value != ((string)Session["token"]))) { //エラーの場合はログイン画面にリダイレクト Response.Redirect("Login.aspx"); } this.LabelID.Text = HttpUtility.HtmlEncode((string)Session["id"]); } } }
・Welcome.aspx(VB.NET)
Public Class Welcome Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 'トークンチェック' Dim token As HttpCookie = Request.Cookies("token") 'エラーの場合はログイン画面にリダイレクト' If (IsNothing(token)) Then Response.Redirect("Login.aspx") ElseIf Request.Cookies("token").Value <> CType(Session("token"), String) Then Response.Redirect("Login.aspx") End If Me.LabelID.Text = HttpUtility.HtmlEncode(CType(Session("id"), String)) End Sub End Class
参考
Webアプリケーションセキュリティに関する記事は、以下のページにまとまっています。ぜひご確認ください。
コメント