📄 nobot.cs
字号:
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.
using System;
using System.Data;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace AjaxControlToolkit
{
[Designer("AjaxControlToolkit.NoBotDesigner, AjaxControlToolkit")]
[DefaultEvent("GenerateChallengeAndResponse")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Bot", Justification = "Bot is a commonly used term")]
[System.Drawing.ToolboxBitmap(typeof(NoBot), "NoBot.NoBot.ico")]
public class NoBot : WebControl, INamingContainer
{
// Statics
private static SortedList<DateTime, string> _pastAddresses = new SortedList<DateTime, string>();
// Properties
private int _responseMinimumDelaySeconds = 2;
private int _cutoffWindowSeconds = 60;
private int _cutoffMaximumInstances = 5;
// Internals
private NoBotExtender _extender; // = null;
private NoBotState _state = NoBotState.InvalidUnknown;
/// <summary>
/// Set the html tag to be div instead of the default span to be xhtml compliant
/// </summary>
public NoBot() : base(HtmlTextWriterTag.Div)
{ }
// Create extender and properties
protected override void CreateChildControls()
{
base.CreateChildControls();
// The Label gives something to attach the extender to
Label label = new Label();
label.ID = this.ID + "_NoBotLabel";
Controls.Add(label);
_extender = new NoBotExtender();
_extender.ID = this.ID + "_NoBotExtender";
_extender.TargetControlID = label.ID;
Controls.Add(_extender);
}
// Determine the challenge and the required response
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
EnsureChildControls();
CheckResponseAndStoreState(); // Do this (if it hasn't already been done) before preparing the next challenge
// Default challenge is to perform a trivial complement of a number
NoBotEventArgs eventArgs = new NoBotEventArgs();
DateTime utcNow = DateTime.UtcNow;
int randomValue = utcNow.Millisecond;
eventArgs.ChallengeScript = string.Format(CultureInfo.InvariantCulture, "~{0}", randomValue.ToString(CultureInfo.InvariantCulture));
eventArgs.RequiredResponse = (~randomValue).ToString(CultureInfo.InvariantCulture);
// If a custom challenge has been provided, use it instead
if (null != GenerateChallengeAndResponse)
{
GenerateChallengeAndResponse(this, eventArgs);
}
// Send down the challenge
_extender.ChallengeScript = eventArgs.ChallengeScript;
_extender.ClientState = "";
// Save values for use during postback
ViewState[ResponseTimeKey] = utcNow.AddSeconds(_responseMinimumDelaySeconds);
string sessionKey = CreateSessionKey(utcNow.Ticks);
ViewState[SessionKeyKey] = sessionKey;
Page.Session[sessionKey] = eventArgs.RequiredResponse;
}
// Return whether the user is believed to be valid along with relevant details
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "Out parameter state allows all override forms to return a bool")]
public bool IsValid(out NoBotState state)
{
EnsureChildControls();
CheckResponseAndStoreState();
state = _state;
return (NoBotState.Valid == state);
}
// Return whether the user is believed to be valid
public bool IsValid()
{
NoBotState unused;
return IsValid(out unused);
}
// Gets a copy of the user address cache
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Method performs a copy")]
public static SortedList<DateTime, string> GetCopyOfUserAddressCache()
{
lock (_pastAddresses)
{
return new SortedList<DateTime, string>(_pastAddresses);
}
}
// Empties the user address cache
public static void EmptyUserAddressCache()
{
lock (_pastAddresses)
{
_pastAddresses.Clear();
}
}
// Check the last response (if any) and prepare for the next one
private void CheckResponseAndStoreState()
{
if (NoBotState.InvalidUnknown == _state)
{
try
{
// Report valid when page first loaded (to avoid breaking pages that unconditionally check IsValid)
//
if (!Page.IsPostBack)
{
_state = NoBotState.Valid;
return;
}
// Report invalid if response arrives too soon
//
DateTime responseTime = (DateTime)ViewState[ResponseTimeKey];
DateTime utcNow = DateTime.UtcNow;
if (utcNow < responseTime)
{
_state = NoBotState.InvalidResponseTooSoon;
return;
}
// Report invalid if too many responses from same IP address
// NOTE: The performance of the following code can be improved
// if performance becomes an issue.
//
lock (_pastAddresses)
{
// Add user address to address cache, taking care not to duplicate keys
string userAddress = Page.Request.UserHostAddress;
DateTime utcAdd = utcNow;
while (_pastAddresses.ContainsKey(utcAdd))
{
utcAdd = utcAdd.AddTicks(1);
}
_pastAddresses.Add(utcAdd, userAddress);
// Calculate cutoff window for cached addresses
DateTime utcCutoff = utcNow.AddSeconds(-_cutoffWindowSeconds);
// Determine number of expired addresses
int cutoffs = 0;
foreach (DateTime time in _pastAddresses.Keys)
{
if (time < utcCutoff)
{
cutoffs++;
}
else
{
break;
}
}
// Remove expired addresses
while (0 < cutoffs)
{
_pastAddresses.RemoveAt(0);
cutoffs--;
}
// Determine number of instances of user address in cache
int instances = 0;
foreach (string address in _pastAddresses.Values)
{
if (userAddress == address)
{
instances++;
}
}
// Fail if too many
if (_cutoffMaximumInstances < instances)
{
_state = NoBotState.InvalidAddressTooActive;
return;
}
}
// Report invalid if response is wrong
//
string sessionKey = (string)ViewState[SessionKeyKey];
string requiredResponse = (string)Page.Session[sessionKey];
Page.Session.Remove(sessionKey);
if (requiredResponse != _extender.ClientState)
{
_state = NoBotState.InvalidBadResponse;
return;
}
// All checks OK, report valid
//
_state = NoBotState.Valid;
}
catch (NullReferenceException)
{
_state = NoBotState.InvalidBadSession;
}
}
}
// Return the unique key for storing the response time
private string ResponseTimeKey
{
get
{
return string.Format(CultureInfo.InvariantCulture, "NoBot_ResponseTimeKey_{0}", UniqueID);
}
}
// Return the unique key for storing the session key
private string SessionKeyKey
{
get
{
return string.Format(CultureInfo.InvariantCulture, "NoBot_SessionKeyKey_{0}", UniqueID);
}
}
// Create a unique session key
private string CreateSessionKey(long ticks)
{
return string.Format(CultureInfo.InvariantCulture, "NoBot_SessionKey_{0}_{1}", UniqueID, ticks);
}
public event EventHandler<NoBotEventArgs> GenerateChallengeAndResponse;
public int ResponseMinimumDelaySeconds
{
get { return _responseMinimumDelaySeconds; }
set { _responseMinimumDelaySeconds = value; }
}
public int CutoffWindowSeconds
{
get { return _cutoffWindowSeconds; }
set { _cutoffWindowSeconds = value; }
}
public int CutoffMaximumInstances
{
get { return _cutoffMaximumInstances; }
set { _cutoffMaximumInstances = value; }
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -