誰もトークンと ttl の両方を使用して XSRF を実行できないようにする抽象基本クラスまたはマスター ページ ソリューションを探しています。誰かが私を正しい方向に向けることができますか?
編集: 理想的なソリューションは、既定のメンバーシップ プロバイダーがクライアントに送信する Cookie を利用することです。
マスター ページに非表示フィールドを配置し、マスター ページの Page_Load イベント中にキーを生成し、そのキーを非表示フィールドの値として割り当て、その値を Cookie に追加することができます。次に、それらの値を比較します。
マスター ページが継承できる基本クラスを開始しました。このアプローチでは、ページなどの複数のフォームについて心配する必要がないため、非表示の入力を下に置く代わりにビューステートを使用することを選択しました。また、この値を見つけるには、単純な「ソースを表示」するよりも少し手間がかかります
以下は、私が修正しようとしているいくつかの問題です。
ページを更新すると (ポストバックではなく) ビューステートと非表示の入力 (このアプローチを開始したとき) の値は、Cookie のように更新されません。
アプリ内の新しいページに移動すると、新しいページが有効なビューステートなしで開始されるため、この場合の比較は失敗します...
以下は進行中の私の作業です;)
public class PreventXSRF : MasterPage
{
HttpCookie mCookie = null;
FormsAuthenticationTicket mPreviousAuthenticationTicket = null;
FormsAuthenticationTicket mNewAuthenticationTicket = null;
public bool IsXSRF()
{
if ((Request.Cookies(".ASPXAUTH") != null)) {
mCookie = Request.Cookies(".ASPXAUTH");
//get the current auth ticket so we can verify the token (userData) matches the value of the hidden input
mPreviousAuthenticationTicket = FormsAuthentication.Decrypt(mCookie.Value);
}
else {
///'the membership cookie does not exist so this is not an authenticated user
return true;
}
//** ** **
// verify the cookie value matches the viewstate value
// if it does then verify the ttl is valid
//** ** **
if ((mPreviousAuthenticationTicket != null)) {
if (mPreviousAuthenticationTicket.UserData == Token) {
if ((TTL != null)) {
if (Convert.ToDateTime(TTL).AddMinutes(5) < DateTime.Now()) {
///'the ttl has expired so this is not a valid form submit
return true;
}
}
else {
//** ** **
// ?? what about a hack that could exploit this when a user tries to BF
// a value for the token and simply keeps the viewstate for ttl null ??
//** ** **
}
}
else {
//** ** **
// ?? I hit this when I navigate to another page in the app (GET)
// in this event, it was hit because the cookie has a valid token
// but the page is new so viewstate is not valid ... ??
//** ** **
///'the cookie value does not match the form so this is not a valid form submit
return true;
}
}
else {
///'the authentication ticket does not exist so this is not a valid form submit
return true;
}
//** ** **
// if the code gets this far the form submit is 99.9% valid, so now we gen a new token
// and set this new value on the auth cookie and reset the viewstate value
// so it matches the cookie
//** ** **
//gen a new ttl and set the viewstate value
TTL = GenerateTTL();
//gen a new token and set the viewstate value
Token = GenerateToken();
if ((mPreviousAuthenticationTicket != null)) {
//** ** **
// create a new authticket using the current values + a custom token
// we are forced to do this because the current cookie is read-only
// ** ** **
mNewAuthenticationTicket = new FormsAuthenticationTicket(mPreviousAuthenticationTicket.Version, mPreviousAuthenticationTicket.Name, mPreviousAuthenticationTicket.IssueDate, mPreviousAuthenticationTicket.Expiration, mPreviousAuthenticationTicket.IsPersistent, Token);
}
else {
///'TODO: if no auth ticket exists we need to return as this won't be valid
}
if ((mCookie != null)) {
//** ** **
// take the new auth ticket with the userdata set to the new token value
// encrypt this, update the cookie, and finally apply this to the users machine
//** ** **
mCookie.Value = FormsAuthentication.Encrypt(mNewAuthenticationTicket);
Response.Cookies.Add(mCookie);
}
else {
///'TODO: if no cookie exists we need to return as this won't be valid
}
//if we got this far without a return true, it must not be a xsrf exploit so return false
return false;
}
private string GenerateToken()
{
RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
byte[] randBytes = new byte[32];
random.GetNonZeroBytes(randBytes);
return Convert.ToBase64String(randBytes);
}
private string GenerateTTL()
{
return DateTime.Now();
}
private string TTL {
get { return ViewState("TTL"); }
set { ViewState("TTL") = value; }
}
private string Token {
get { return ViewState("Token"); }
set { ViewState("Token") = value; }
}
}