ASP.NET(VB)でトランザクショントークンを実装する
Strutsで標準装備されているアレを実装したいっていうお話です。
トランザクショントークンとは?
・表示する画面の中にhiddenでランダムなID(トークン)を埋め込んでおき、そのトークンをサーバー側でもセッション内に保持しておく。
・その画面でサブミットされると、hiddenに埋められていたトークンがリクエストに入れられてサーバーに届く。
・サーバーでは、届いたhiddenのトークンとセッション内に保持していたトークンを比較する。一致していれば正しい遷移と判断する。
・別の画面のhiddenには別のトークンが埋められている為、意図しない画面から来た場合はセッション内のトークンとは一致しないので 誤った遷移であると判断できる。
・比較直後にセッション内からトークンを削除しておけば、重複サブミットの二度目の処理を防止できる。
・つまり、連続してサブミットボタンが押されたような場合には同一のトークンが再び届くが、セッションのトークンは既に存在しないので一致しない。すなわち不正なサブミットだと判断できる。
用途としては、
などです。(※必ずしも最適解ではないですが。。)
実際には、全てのページを対象とするか否か?(更新系だけとか)、Ajaxの非同期リクエストやモーダルウィンドウからのリクエストの考慮などが必要です。
ポイント
- hiddenの直接操作は行わずViewStateを使います。
- セキュリティレベルはViewStateレベルということになります。
実装例
Public Class TransactionToken Private Const TOKEN_NAME As String = "transaction-token" Private Shared lock As New Object Private Shared previous As Long Private Shared Function GenerateTocken(ByVal id As String) As String Dim current As Long = Now.Ticks If current = previous Then current += 1 previous = current Dim data As Byte() = System.Text.Encoding.Default.GetBytes(id & current.ToString) Dim md5csp As New System.Security.Cryptography.MD5CryptoServiceProvider() data = md5csp.ComputeHash(data) Return Bytes2Hex(data) End Function Private Shared Function Bytes2Hex(ByVal bytes() As Byte) As String Dim result As New System.Text.StringBuilder For Each b As Byte In bytes result.Append(b.ToString("x2")) Next Return result.ToString End Function Public Shared Sub SaveToken(ByVal v As StateBag, ByVal s As HttpSessionState) SyncLock lock Dim token As String = GenerateTocken(s.SessionID) v.Add(TOKEN_NAME, token) s.Remove(TOKEN_NAME) s.Add(TOKEN_NAME, token) End SyncLock End Sub Public Shared Function IsValidToken(ByVal v As StateBag, ByVal s As HttpSessionState) As Boolean If v(TOKEN_NAME) Is Nothing OrElse s(TOKEN_NAME) Is Nothing Then Return True Return s(TOKEN_NAME).Equals(v(TOKEN_NAME)) End Function End Class
使用例
例えば、全ページの共通基底クラス PageBase を準備しているとします。
この場合、PreRenderとLoadにSaveTokenとIsValidTokenを組み込むと全ページに適用できます。
Imports System.Data Public Class PageBase Inherits System.Web.UI.Page Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender TransactionToken.SaveToken(Me.ViewState, Me.Session) End Sub Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not TransactionToken.IsValidToken(Me.ViewState, Me.Session) Then Me.Server.Transfer("/IllegalOperation.html") End Sub End Class
Work! Enjoy it!