在Global.asax中過濾POST請求的非法參數。

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web;
using System.Globalization;數組

namespace HNAS.OA.OAWebApp
{
/// <summary>
/// 以Post方式提交的變量的集合。
/// </summary>
/// <remarks>
/// 不包含提交的文件。
/// </remarks>
internal class PostVariableCollection : NameValueCollection
{
/// <summary>
/// Content Type
/// </summary>
private string contentType = string.Empty;緩存

/// <summary>
/// 分界符
/// </summary>
private byte[] boundary;app

/// <summary>
/// 初始化類 PostVariableCollection 的一個新實例
/// </summary>
public PostVariableCollection()
{
FillFormStream(Encoding.Default);
}this

/// <summary>
/// 初始化類 PostVariableCollection 的一個新實例
/// </summary>
/// <param name="isUrlDecode">是否進行Url解碼</param>
/// <param name="encoding">編碼類型</param>
public PostVariableCollection(Encoding encoding)
{
FillFormStream(encoding);
}編碼

/// <summary>
/// 使用HTTP實體主體內容填充集合
/// </summary>
/// <param name="isUrlDecode"></param>
/// <param name="encoding"></param>
private void FillFormStream(Encoding encoding)
{
contentType = HttpContext.Current.Request.ContentType;url

if (!string.IsNullOrEmpty(contentType))
{
System.IO.Stream entityStream = HttpContext.Current.Request.InputStream;spa

// 獲取HTTP實體主體的內容
byte[] bytes = GetEntityBody(entityStream, 0);3d

if (bytes == null || bytes.Length <= 0)
{
return;
}指針

// 由於是字節數據,全部的數據都須要解碼
if (contentType.StartsWith("application/x-www-form-urlencoded", System.StringComparison.CurrentCultureIgnoreCase))
{
try
{
FillFromBytes(bytes, encoding);
return;
}
catch (Exception ex)
{
throw new HttpException("Invalid_urlencoded_form_data", ex);
}
}code

if (contentType.StartsWith("multipart/form-data", System.StringComparison.CurrentCultureIgnoreCase))
{
if (GetMultipartBoundary())
{
try
{
// 獲取各個參數
FillFromMultipartBytes(bytes, encoding);
return;
}
catch (Exception ex)
{
throw new HttpException("Invalid_multipart_form_data", ex);
}
}
}
}
}

/// <summary>
/// 從字節數組讀取變量填充到集合
/// </summary>
/// <param name="bytes"></param>
/// <param name="encoding"></param>
private void FillFromBytes(byte[] bytes, Encoding encoding)
{
// 字節數組長度
int bLen = (bytes != null) ? bytes.Length : 0;

// 遍歷字節數組
for (int i = 0; i < bLen; i++)
{
string parameterName;
string parameterValue;

//參數名開始位置
int startIndex = i;

//參數名結束位置
int endIndex = -1;

while (i < bLen)
{
byte bt = bytes[i];

// 符號:=
if (bt == 0x3d)
{
if (endIndex < 0)
{
endIndex = i;
}
}
else if (bt == 0x26) //符號:&
{
break;
}
i++;
}

if (endIndex >= 0)
{
parameterName = HttpUtility.UrlDecode(bytes, startIndex, endIndex - startIndex, encoding);
parameterValue = HttpUtility.UrlDecode(bytes, endIndex + 1, (i - endIndex) - 1, encoding);
}
else
{
parameterName = null;
parameterValue = HttpUtility.UrlDecode(bytes, startIndex, i - startIndex, encoding);
}

base.Add(parameterName, parameterValue);

if ((i == (bLen - 1)) && (bytes[i] == 0x26))
{
base.Add(null, string.Empty);
}
}
}

/// <summary>
/// 從多部件的實體主體內容中讀取變量填充到集合,文件排除在外。
/// </summary>
/// <param name="bytes"></param>
/// <param name="isUrlDecode"></param>
/// <param name="encoding"></param>
private void FillFromMultipartBytes(byte[] bytes, Encoding encoding)
{
// 字節數組長度
int bLen = (bytes != null) ? bytes.Length : 0;

// 當前字節索引
int currentIndex = 0;

// 當前行開始索引
int lineStartIndex = -1;

// 當前行結束索引
int lineEndIndex = currentIndex;

// 當前行字節長度
int lineLength = -1;

// 是否最後一個分界符
bool isLastBoundary = false;

// 上一行是否分界符行
bool prevIsBoundary = false;

// 上一行是否參數名稱行
bool prevIsParaName = false;

// 上一行是否參數名稱行的結束索引
int prevParaNameLineEndIndex = 0;

// 參數名稱
string paraName = string.Empty;

// 參數值
string paraValue = string.Empty;

// 遍歷字節數組
for (int i = 0; i < bLen; i++)
{
//查找行,行由char(13)+char(10)結束
while (lineEndIndex < bLen)
{
// 換行
if (bytes[lineEndIndex] == 10)
{
lineStartIndex = currentIndex;
lineLength = lineEndIndex - currentIndex;

// 回車
if (lineLength > 0 && bytes[lineEndIndex - 1] == 13)
{
lineLength--;
}

currentIndex = lineEndIndex + 1;

break;
}

if (++lineEndIndex == bLen)
{
lineStartIndex = currentIndex;
lineLength = lineEndIndex - currentIndex;
currentIndex = bLen;
}
}

// 處理行
if (lineStartIndex >= 0)
{
// 若是是分界符行
if (AtBoundaryLine(bytes, lineLength, lineStartIndex, out isLastBoundary))
{
// 獲取參數值
if (prevIsParaName)
{
paraValue = GetParaValueFromContent(bytes, bLen, prevParaNameLineEndIndex, lineStartIndex, encoding);
prevIsParaName = false;
base.Add(paraName, paraValue);
}

prevIsBoundary = true;

// 最後一行了
if (isLastBoundary)
{
break;
}
}
else
{
// 若是上一行是分界符行,則處理本行
if (prevIsBoundary)
{
if (lineLength <= 0)
{
continue;
}

byte[] buffer = new byte[lineLength];
Array.Copy(bytes, lineStartIndex, buffer, 0, lineLength);

string l = encoding.GetString(buffer);
int colonIndex = l.IndexOf(':');
if (colonIndex >= 0)
{
string str2 = l.Substring(0, colonIndex);

if (string.Equals(str2, "Content-Disposition", StringComparison.CurrentCultureIgnoreCase))
{
// 獲取參數名稱
paraName = this.GetParaNameFromContent(l, colonIndex + 1, "name");

//// 獲取文件名稱
//string paraFileName = this.GetParaNameFromContent(l, colonIndex + 1, "filename");

// 參數名不爲空,且非文件
if (!string.IsNullOrEmpty(paraName) && !l.Contains("filename"))
{
// 標記上一行是參數名稱行
prevIsParaName = true;

// 行結束的索引
prevParaNameLineEndIndex = lineEndIndex;
}
}
}
}

prevIsBoundary = false;
}
}

// 處理下一行
lineEndIndex++;
i = lineEndIndex;
}
}

/// <summary>
/// 獲取HTTP實體主體的內容的字節數組形式
/// </summary>
/// <param name="stream"></param>
/// <param name="bufferLen"></param>
/// <returns></returns>
private byte[] GetEntityBody(System.IO.Stream stream, int bufferLen)
{
// 若是指定的無效長度的緩衝區,則指定一個默認的長度做爲緩存大小
if (bufferLen < 1)
{
bufferLen = 0x8000;
}

// 初始化一個緩存區
byte[] buffer = new byte[bufferLen];

// 已讀取的字節數
int read = 0;

// 緩衝區中的總字節數
int block;

// 每次從流中讀取緩存大小的數據,直到讀取完全部的流爲止
// 若是當前可用的字節數沒有請求的字節數那麼多,則總字節數可能小於請求的字節數;若是已到達流的末尾,則爲零 (0)
while ((block = stream.Read(buffer, read, buffer.Length - read)) > 0)
{
// 從新設定讀取位置
read += block;

// 檢查已讀取字節數是否到達了緩衝區的邊界
if (read == buffer.Length)
{
// 嘗試讀取一個字節,檢查是否還有能夠讀取的信息
int nextByte = stream.ReadByte();

// 讀取失敗則說明讀取完成能夠返回結果
if (nextByte == -1)
{
return buffer;
}

// 調整數組大小準備繼續讀取
byte[] newBuf = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuf, buffer.Length);
newBuf[read] = (byte)nextByte;

// buffer是一個引用(指針),這裏意在從新設定buffer指針指向一個更大的內存
buffer = newBuf;
read++;
}
}

// 若是緩存太大則收縮前面while讀取的buffer,而後直接返回
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}

/// <summary>
/// 獲取邊界字符串
/// </summary>
/// <returns></returns>
private bool GetMultipartBoundary()
{
// 獲取邊界字符串屬性的值
string attributeFromHeader = GetAttributeFromHeader(contentType, "boundary");
if (attributeFromHeader == null)
{
return false;
}

// 每個邊界符前面都須要加2個連字符「--」
attributeFromHeader = "--" + attributeFromHeader;
boundary = Encoding.ASCII.GetBytes(attributeFromHeader.ToCharArray());

return true;
}

/// <summary>
/// 判斷是否在分界符行
/// </summary>
/// <param name="bytes"></param>
/// <param name="lineLength"></param>
/// <param name="lineStartIndex"></param>
/// <param name="isLastBoundary"></param>
/// <returns></returns>
private bool AtBoundaryLine(byte[] bytes, int lineLength, int lineStartIndex, out bool isLastBoundary)
{
isLastBoundary = false;

int length = this.boundary.Length;
if (lineLength != length && lineLength != (length + 2))
{
return false;
}

for (int i = 0; i < length; i++)
{
if (bytes[lineStartIndex + i] != this.boundary[i])
{
return false;
}
}

// 最後一個分界符後兩個字符是「--」
if (lineLength != length)
{
if ((bytes[lineStartIndex + length] != 0x2d) || (bytes[(lineStartIndex + length) + 1] != 0x2d))
{
return false;
}

isLastBoundary = true;
}

return true;
}

/// <summary>
/// 獲取ContentType中屬性的值
/// </summary>
/// <param name="headerValue">ContentType</param>
/// <param name="attrName">屬性名稱</param>
/// <returns></returns>
private string GetAttributeFromHeader(string headerValue, string attrName)
{
int index;
if (headerValue == null)
{
return null;
}

int headerValueLen = headerValue.Length;
int attrNameLen = attrName.Length;

// 獲取attrName的起始索引位置
int startIndex = 1;
while (startIndex < headerValueLen)
{
// ContentType結構相似:multipart/form-data; boundary=---------------------------7db2693010b2a
startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, startIndex, CompareOptions.IgnoreCase);

// 不包含「attrName」,跳出循環
if ((startIndex < 0) || (startIndex + attrNameLen >= headerValueLen))
{
break;
}

// 符合以下條件即跳出循環
// attrName前一個字符能夠爲 ; 或 , 或 空白(char 11 12 13)
// attrName後一個字符能夠爲 = 或 空白(char 11 12 13)
char c = headerValue[startIndex - 1];
char ch2 = headerValue[startIndex + attrNameLen];
if ((c == ';' || c == ',' || char.IsWhiteSpace(c)) && ((ch2 == '=') || char.IsWhiteSpace(ch2)))
{
break;
}

// 不符合條件,索引前進,繼續查找
startIndex += attrNameLen;
}

// 不包含符合條件的「attrName」
if ((startIndex < 0) || (startIndex >= headerValueLen))
{
return null;
}

// ContentType中包含了attrName,獲取attrName的值
startIndex += attrNameLen;

// 若是startIndex是空白,則索引++,直到非空白
while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))
{
startIndex++;
}

// 移動到符號 =
if ((startIndex >= headerValueLen) || (headerValue[startIndex] != '='))
{
return null;
}

// 繼續前進到值
startIndex++;

while ((startIndex < headerValueLen) && char.IsWhiteSpace(headerValue[startIndex]))
{
startIndex++;
}

// 若是索引超出,則返回
if (startIndex >= headerValueLen)
{
return null;
}

// 若是是被雙引號包含的
if ((startIndex < headerValueLen) && (headerValue[startIndex] == '"'))
{
if (startIndex == (headerValueLen - 1))
{
return null;
}

// 獲取結束的雙引號
index = headerValue.IndexOf('"', startIndex + 1);

if ((index < 0) || (index == (startIndex + 1)))
{
return null;
}

// 截取雙引號之間的值
return headerValue.Substring(startIndex + 1, (index - startIndex) - 1).Trim();
}

// 索引前進,查找空格或逗號等分隔符
// 若是找不到,索引到倒數第二個字符
index = startIndex;
while (index < headerValueLen)
{
if ((headerValue[index] == ' ') || (headerValue[index] == ','))
{
break;
}

index++;
}

if (index == startIndex)
{
return null;
}

// 截取返回
return headerValue.Substring(startIndex, index - startIndex).Trim();
}

/// <summary>
/// 獲取參數名稱
/// </summary>
/// <param name="l"></param>
/// <param name="pos"></param>
/// <param name="name"></param>
/// <returns></returns>
private string GetParaNameFromContent(string l, int pos, string name)
{
string str = name + "=\"";
int startIndex = CultureInfo.InvariantCulture.CompareInfo.IndexOf(l, str, pos, CompareOptions.IgnoreCase);
if (startIndex < 0)
{
return null;
}
startIndex += str.Length;
int index = l.IndexOf('"', startIndex);
if (index < 0)
{
return null;
}
if (index == startIndex)
{
return string.Empty;
}

return l.Substring(startIndex, index - startIndex);
}

/// <summary>
/// 獲取參數值
/// </summary>
/// <param name="bytes"></param>
/// <param name="bLen"></param>
/// <param name="pos"></param>
/// <param name="lineStartIndex"></param>
/// <param name="encoding"></param>
/// <returns></returns>
private string GetParaValueFromContent(byte[] bytes, int bLen, int pos, int lineStartIndex, Encoding encoding)
{
int valueStart = pos + 3;
int valueLength = -1;
int valueEndIndex = lineStartIndex - 1;

if (valueStart > bLen || valueEndIndex > bLen)
{
return null;
}

if (bytes[valueEndIndex] == 10)
{
valueEndIndex--;
}
if (bytes[valueEndIndex] == 13)
{
valueEndIndex--;
}

valueLength = (valueEndIndex - valueStart) + 1;

return encoding.GetString(bytes, valueStart, valueLength);
}
}
}


//注意我在這個類中剔除了獲取文件項的方法,只能獲取其它表單項的值。
//使用的時候能夠以下調用:

//查看源代碼
//打印?

/// <summary>/// 獲取以Post方式提交的參數集合。/// </summary>/// <param name="isUrlDecode">是否要進行Url解碼</param>/// <param name="encoding">Url解碼時用的編碼</param>/// <returns>參數集合。</returns>/// <example>/// string paras = string.Empty;/// System.Collections.Specialized.NameValueCollection paraCollection = RequestHelper.GetFormStrings(Encoding.UTF8);////// foreach (string key in paraCollection.AllKeys)/// {/// paras += key + ":" + paraCollection[key] + "\r\n";/// }/// </example>//public static NameValueCollection GetFormStrings(Encoding encoding)//{//return new PostVariableCollection(encoding);//}

相關文章
相關標籤/搜索