c#&asp.net

从客户端中检测到有潜在危险的 Request.Form 值

2008-10-15

当客户端输入的值,有可能危及应用程序或者产生跨站脚本攻击时,比如从客户端服务器端传输html代码,js脚本时。asp.net可能会报告下面错误:

从客户端中检测到有潜在危险的 Request.Form 值

说明: 请求验证过程检测到有潜在危险的客户端输入值,对请求的处理已经中止。该值可能指示危及应用程序安全的尝试,如跨站点的脚本攻击。通过在 Page 指令或 配置节中设置 validateRequest=false 可以禁用请求验证。但是,在这种情况下,强烈建议应用程序显式检查所有输入。

异常详细信息: System.Web.HttpRequestValidationException: 从客户端(…)中检测到有潜在危险的 Request.Form 值。

解决方法

如果没有安全性考虑的话,可以在webconfig中加入下面代码:

<pages validateRequest="false"/>

如果有安全性考虑,应该使用HtmlEncode函数转义或过滤html标签与js脚本。

C#正则表达式 — 语言元素

2008-10-14

正则表达式中的语言元素,有些模糊的内容我加入了简单的示例。

1,@

禁止转义CS字符串(这个是我加的。与正则无关)

2,字符转义

大多数重要的正则表达式语言运算符都是非转义的单个字符。转义符 \(单个反斜杠)通知正则表达式分析器反斜杠后面的字符不是运算符。例如,分析器将星号 (*) 视为重复限定符,而将后跟星号的反斜杠 (\*) 视为 Unicode 字符 002A。

下表中列出的字符转义在正则表达式和替换模式中都会被识别。

一般字符 除 . $ ^ { [ ( | ) * + ? \ 外,其他字符与自身匹配。
\a 与响铃(警报)\u0007 匹配。
\b 如果在 [] 字符类中,则与退格符 \u0008 转义字符 \b 是一个特例。在正则表达式中,\b 表示单词边界(在 \w 和 \W 之间),不过,在 [] 字符类中,\b 表示退格符。在替换模式中,\b 始终表示退格符。
\t 与 Tab 符 \u0009 匹配。
\r 与回车符 \u000D 匹配。
\v 与垂直 Tab 符 \u000B 匹配。
\f 与换页符 \u000C 匹配。
\n 与换行符 \u000A 匹配。
\e 与 Esc 符 \u001B 匹配。
\040 将 ASCII 字符匹配为八进制数(最多三位);如果没有前导零的数字只有一位数或者与捕获组号相对应,则该数字为后向引用。(有关更多信息,请参见反向引用。) 例如,字符 \040 表示空格。
\x20 使用十六进制表示形式(恰好两位)与 ASCII 字符匹配。
\cC 与 ASCII 控制字符匹配;例如,\cC 为 Ctrl-C。
\u0020 使用十六进制表示形式(恰好四位)与 Unicode 字符匹配。
\ 在后面带有不识别为转义符的字符时,与该字符匹配。例如,\* 与 \x2A 相同。

3,替换

只在替换模式中允许替换。对于正则表达式中的类似功能,使用后向引用(如 \1)。有关后向引用的详细信息,请参见 反向引用 和后向引用构造。

字符转义和替换是在替换模式中识别的唯一的特殊构造。下面几部分描述的所有语法构造只允许出现在正则表达式中;替换模式中不识别它们。例如,替换模式 a*${txt}b 会插入字符串“a*”,该字符串后跟按 txt 捕获组匹配的子字符串,该子字符串后跟字符串“b”(如果有)。在替换模式中,* 字符不会识别为元字符。与此类似,在正则表达式匹配模式中不识别 $ 模式。在正则表达式中,$ 指定字符串的结尾。

下表显示如何定义命名并编号的替换模式。

字符 说明

$ 数字 替换按组号 number(十进制)匹配的最后一个子字符串。
${name} 替换由 (? ) 组匹配的最后一个子字符串。
$$ 替换单个“$”字符。
$& 替换完全匹配本身的一个副本。
$` 替换匹配前的输入字符串的所有文本。
$’ 替换匹配后的输入字符串的所有文本。
$+ 替换最后捕获的组。
$_ 替换整个输入字符串。

using System;
using System.Text.RegularExpressions;

/// <summary>
/// 2008-10-07 copyright 可爱的猴子
/// http://www.dreamdu.com/blog/
/// </summary>
public class RegexTest
{
	public static void Main()
	{
	string str = "ab cb db bb";
	string pattern = @"(?<txt>b{1})";
	foreach (Match m in Regex.Matches(str,pattern))
	{
		Console.WriteLine("{0}--{1}",m.Value, m.Index);
	}
	//str = Regex.Replace(str, pattern, "a*${txt}");
	str = Regex.Replace(str, pattern, "a*$1");
	Console.WriteLine(str);
	}
}

4,字符类

字符类表示一组可以匹配输入字符串的字符。组合原义字符、转义符和字符类以构成正则表达式模式。

[字符分组] (正字符分组。) 匹配指定字符分组内的任何字符。字符分组由串连的一个或多个原义字符、转义符、字符范围或字符类组成。例如,若要指定所有元音字母,使用 [aeiou]. 若要指定所有标点符号和十进制数字符,使用代码 [\p{P}\d]。

[^字符分组] (负字符分组。) 匹配不在指定字符分组内的任何字符。字符分组由串连的一个或多个原义字符、转义符、字符范围或字符类组成。前导符 (^) 是强制的,指示字符分组为负字符分组,而不是正字符分组。例如,若要指定除元音字母以外的所有字符,使用 [^aeiou]. 若要指定除标点符号和十进制数字符以外的所有字符,使用 [^\p{P}\d]。

[第一个字符-最后一个字符] (字符范围。) 匹配字符范围中的任何字符。字符范围是一系列连续的字符,定义的方法是:指定系列中的第一个字符,连字符 (-),然后指定系列中的最后一个字符。如果两个字符具有相邻的 Unicode 位,则这两个字符是连续的。可以串连两个或更多字符范围。例如,若要指定从“0”至“9”的十进制数范围、从“a”至“f”的小写字母范围,以及从“A”至“F”的大写字母范围,使用 [0-9a-fA-F]。

. (句点字符。) 匹配除 \n 以外的任何字符。如果已用 Singleline 选项做过修改,则句点字符可与任何字符匹配。有关更多信息,请参见正则表达式选项。请注意,正字符分组或负字符分组中的句点字符(方括号内的句点)将被视为原义句点字符,而非字符类。

\p{ 名称 } 匹配通过名称(例如 Ll、Nd、Z、IsGreek 和 IsBoxDrawing)指定的 Unicode 通用类别或命名块中的任何字符。

\P{ 名称 } 匹配不在名称中指定的 Unicode 通用类别或命名块中的任何字符。

\w 与任何单词字符匹配。等效于 Unicode 通用类别 [\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \w 等效于 [a-zA-Z_0-9]。

\W 与任何非单词字符匹配。等效于 Unicode 通用类别 [^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \W 等效于 [^a-zA-Z_0-9]。

\s 与任何空白字符匹配。等效于转义符和 Unicode 通用类别 [\f\n\r\t\v\x85\p{Z}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \s 等效于 [ \f\n\r\t\v]。

\S 与任何非空白字符匹配。等效于转义符和 Unicode 通用类别 [^\f\n\r\t\v\x85\p{Z}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 \S 等效于 [^ \f\n\r\t\v]。

\d 与任何十进制数字匹配。对于 Unicode 类别的 ECMAScript 行为,等效于 \p{Nd},对于非 Unicode 类别的 ECMAScript 行为,等效于 [0-9]。

\D 与任何非数字字符匹配。对于 Unicode 类别的 ECMAScript 行为,等效于 \P{Nd},对于非 Unicode 类别的 ECMAScript 行为,等效于 [^0-9] 。

4.1,字符类减法 — 就只听听名字就知道很爽了!

语法: [基本分组-[排除分组]]

[a-z-[m-p]] 从a-z排除m-p
[a-z-[d-w-[m-o]]] 从d-w排除m-o,为组合一,再在a-z中排除组合一。

5,正则表达式选项

就是RegexOptions啦,这个有ECMAScript选项,不错!搞javascript正则的应该比较熟悉!我感觉IgnoreCase,Multiline,Compiled(提高效率),ECMAScript比较常用。

RegexOption 成员 内联字符 说明

None N/A 指定不设置任何选项。
IgnoreCase i 指定不区分大小写的匹配。
Multiline m 指定多行模式。更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。
ExplicitCapture n 指定唯一有效的捕获是显式命名或编号的 (?<name>…) 形式的组。这允许圆括号充当非捕获组,从而避免了由 (?:…) 导致的语法上的笨拙。
Compiled N/A 指定正则表达式将被编译为程序集。生成该正则表达式的 Microsoft 中间语言 (MSIL) 代码;以较长的启动时间为代价,得到更快的执行速度。
Singleline s 指定单行模式。更改句点字符 (.) 的含义,以使它与每个字符(而不是除 \n 之外的所有字符)匹配。
IgnorePatternWhitespace x 指定从模式中排除非转义空白并启用数字符号 (#) 后面的注释。(有关转义空白字符的列表,请参见字符转义。) 请注意,空白永远不会从字符类中消除。
RightToLeft N/A 指定搜索是从右向左而不是从左向右进行的。具有此选项的正则表达式将移动到起始位置的左边而不是右边。(因此,起始位置应指定为字符串的结尾而不是开头。) 为了避免构造具有无限循环的正则表达式的可能性,此选项不能在中流指定。但是,(?<) 回顾后发构造提供了可用作子表达式的类似替代物。RightToLeft 只更改搜索方向。它不会反转所搜索的子字符串。预测先行和回顾后发断言不改变:预测先行向右搜索;回顾后发向左搜索。
ECMAScript N/A 指定已为表达式启用了符合 ECMAScript 的行为。此选项仅可与 IgnoreCase 和 Multiline 标志一起使用。将 ECMAScript 同任何其他标志一起使用将导致异常。
CultureInvariant N/A 指定忽略语言中的区域性差异。有关更多信息,请参见在 RegularExpressions 命名空间中执行不区分区域性的操作。

6,原子零宽度断言

下表中描述的元字符不会使引擎在字符串中前进或使用字符。它们只是根据字符串中的当前位置使匹配成功或失败。例如,^ 指定当前位置在行或字符串的开头。因此,正则表达式 ^FTP 只会返回那些在行的开头出现的字符串“FTP”的匹配项。

^ 指定匹配必须出现在字符串的开头或行的开头。有关更多信息,请参见正则表达式选项中的Multiline 选项。
$ 指定匹配必须出现在以下位置:字符串结尾、字符串结尾处的\n之前或行的结尾。有关更多信息,请参见正则表达式选项中的 Multiline 选项。
\A 指定匹配必须出现在字符串的开头(忽略 Multiline 选项)。
\Z 指定匹配必须出现在字符串的结尾或字符串结尾处的 \n 之前(忽略 Multiline 选项)。
\z 指定匹配必须出现在字符串的结尾(忽略 Multiline 选项)。
\G 指定匹配必须出现在上一个匹配结束的地方。与Match.NextMatch()一起使用时,此断言确保所有匹配都是连续的。
\b 指定匹配必须出现在\w(字母数字)和\W(非字母数字)字符之间的边界上。匹配必须出现在单词边界上(即出现在由任何非字母数字字符分隔的单词中第一个或最后一个字符上)。匹配也可以出现在字符串结尾处的单词边界上。
\B 指定匹配不得出现在 \b 边界上。

7,限定符

限定符将可选数量的数据添加到正则表达式。限定符表达式应用于紧挨着它前面的字符、组或字符类。.NET Framework 正则表达式支持最小匹配 (lazy) 限定符。

下表描述了影响匹配的元字符。数量 n 和 m 是整数常数。

* 匹配前导元素零次或零次以上。该限定符等效于 {0,}。* 是贪婪限定符,对应的非贪婪限定符是 *?。
+ 匹配前导元素一次或多次。它等效于 {1,}。+ 是贪婪限定符,对应的非贪婪限定符是 +?。
? 匹配前导元素零次或一次。它等效于 {0,1}。? 是贪婪限定符,对应的非贪婪限定符是 ??。
{n} 匹配前导元素恰好 n 次。{n} 是贪婪限定符,对应的非贪婪限定符为 {n}?。
{n,} 匹配前导元素至少 n 次。{n,} 是贪婪限定符,对应的非贪婪限定符为 {n}?。
{n,m} 匹配前导元素至少 n 次,但不超过 m 次。{n,m} 是贪婪限定符,对应的非贪婪限定符为 {n,m}?。
*? 匹配前导元素零次或零次以上,但次数尽可能少。它是与贪婪限定符 * 相对的惰性限定符。
+? 匹配前导元素一次或多次,但次数尽可能少。它是与贪婪限定符 + 相对的惰性限定符。
?? 匹配前导元素零次或一次,但次数尽可能少。它是与贪婪限定符 ? 相对的惰性限定符。
{n}? 匹配前导元素恰好 n 次。它是与贪婪限定符 {n}+ 相对的惰性限定符。
{n,}? 匹配前导元素至少 n 次,但次数尽可能少。它是与贪婪限定符 {n,} 相对的惰性限定符。
{n,m}? 匹配前导元素 n 到 m 次,但次数尽可能少。它是与贪婪限定符 {n,m} 相对的惰性限定符。

贪婪限定符与惰性限定符的区别

一些限定符有两种版本:

贪婪版本:贪婪限定符尝试尽可能多地匹配它所应用到的元素。

非贪婪(惰性)版本:非贪婪限定符尝试尽可能少地匹配它所应用到的元素。

例如:\b.*([0-9]{4})\b 与 \b.*?([0-9]{4})\b”

在大多数情况下,带有贪婪限定符和惰性限定符的正则表达式将返回相同的匹配项。在与句点 (. ) 元字符(该元字符匹配任何字符)一起使用时,它们多数情况下会返回不同的结果。

8,分组构造

分组构造描述了正则表达式的子表达式,通常用于捕获输入字符串的子字符串。

(子表达式) 捕获匹配的子表达式(或非捕获组;有关更多信息,请参见正则表达式选项中的 ExplicitCapture 选项)。使用 () 的捕获基于左括号按顺序从 1 开始自动编号。捕获元素编号为零的第一个捕获是由整个正则表达式模式匹配的文本。

(?<name>子表达式) 将匹配的子表达式捕获到一个组名称或编号名称中。用于 name 的字符串不得包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如 (?’name’)。

(?<name1-name2>子表达式) (平衡组定义。) 删除先前定义的 name2 组的定义,并在 name1 组中存储先前定义的 name2 组和当前组之间的间隔。如果未定义 name2 组,则匹配将回溯。由于删除 name2 的最后一个定义会显示 name2 的先前定义,因此该构造允许将 name2 组的捕获堆栈用作计数器,用于跟踪嵌套构造(如括号)。在此构造中,name1 是可选的。可以使用单引号替代尖括号,例如 (?’name1-name2′)。

(?:子表达式) (非捕获组。) 不捕获由子表达式匹配的子字符串。

(?imnsx-imnsx:子表达式) 应用或禁用子表达式中指定的选项。例如,(?i-s: ) 将打开不区分大小写并禁用单行模式。有关更多信息,请参见正则表达式选项。

(?=子表达式) (零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,\w+(?=\d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。

(?!子表达式) (零宽度负预测先行断言。) 仅当子表达式不在此位置的右侧匹配时才继续匹配。例如,\b(?!un)\w+\b 与不以 un 开头的单词匹配。

(?<=子表达式) (零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。

(?<!子表达式) (零宽度负回顾后发断言。) 仅当子表达式不在此位置的左侧匹配时才继续匹配。

(?> 子表达式) (非回溯子表达式(也称为“贪婪”子表达式)。) 该子表达式仅完全匹配一次,然后就不会逐段参与回溯了。(也就是说,该子表达式仅与可由该子表达式单独匹配的字符串匹配。)

默认情况下,如果匹配未成功,回溯会搜索其他可能的匹配。如果已知无法成功回溯,可以使用非回溯子表达式避免不必要的搜索,从而提高性能。

命名捕获基于左括号按从左到右的顺序依次编号(与非命名捕获类似),但在对所有非命名捕获进行计数之后才开始对命名捕获编号。例如,模式 ((?<One>abc)\d+)?(?<Two>xyz)(.*)按编号和名称产生下列捕获组。(编号为 0 的第一个捕获总是指整个模式)。

编号 名称 模式
0 0(默认名称) ((?<One>abc)\d+)?(?<Two>xyz)(.*)

1 1(默认名称) ((?<One>abc)\d+)

2 2(默认名称) (.*)

3 1 (?<One>abc)

4 2 (?<Two>xyz)

平衡组匹配简直太强大啦!!下面是匹配尖括号<与>的的数量是否相等,详见msdn的示例

string pattern = "^[^<>]*(((?’Open’<)[^<>]*)+((?’Close-Open’>)[^<>]*)+)*(?(Open)(?!))$”;

9,后向引用构造

\数字 后向引用。例如,(\w)\1 查找双写的单词字符。

\k<name> 命名后向引用。例如,(?<char>\w)\k<char> 查找双写的单词字符。表达式 (?<43>\w)\43 执行同样的操作。可以使用单引号替代尖括号,例如 \k’char’。

using System;
using System.Text.RegularExpressions;

/// <summary>
/// 2008-10-07 copyright 可爱的猴子
/// http://www.dreamdu.com/blog/
/// </summary>
public class RegexTest
{
	public static void Main()
	{
		string str = "ab cb dbbb abab";
		string pattern = @"(?<ab>ab)\k<ab>";
		foreach (Match m in Regex.Matches(str,pattern))
		{
			Console.WriteLine("{0}--{1}",m.Value, m.Index);
		}
	}
}

10,替换构造

下表列出了用于修改正则表达式以允许进行二者之一/或匹配的特殊字符。

| 与以 |(竖线)字符分隔的术语中的任何一项匹配;例如, cat|dog|tiger。使用最左侧的成功匹配。

(?( 表达式 )yes|no) 如果表达式在此位置匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。表达式可以是任何有效的子表达式,但它将变为零宽度断言,因此该语法等效于 (?(?=expression)yes|no)。请注意,如果表达式是命名组的名称或捕获组编号,则替换构造将解释为捕获测试(在本表的下一行对此进行了描述)。若要避免在这些情况下产生混淆,则可以显式拼出内部 (?=expression)。

(?( name )yes|no) 如果命名捕获字符串有匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。如果给定的名称不与此表达式中使用的捕获组的名称或编号对应,则替换构造将解释为表达式测试(在本表的上一行进行了描述)。

C#正则表达式 — 正则表达式类

2008-10-07

又重新学了一遍C#的正则表达式,温故而知新,C#的正则就用一句话概括吧:很好很强大! 这里做个笔记,笔记的相关内容多数摘自MSDN。

Regex 类表示不可变(只读)的正则表达式。它还包含各种静态方法,允许在不显式创建其他类的实例的情况下使用其他正则表达式类。

Match 类表示正则表达式匹配操作的结果。Match 类的 Match.Success 匹配是否成功。Match.Index 返回第一个匹配的位置。

MatchCollection 类表示成功的【非重叠】匹配项的序列。MatchCollection 的实例是由 Regex.Matches 方法返回的。

GroupCollection 类表示被捕获的组的集合,并在单个匹配项中返回该捕获组的集合。GroupCollection 的实例在 Match.Groups 属性返回的集合中返回。

CaptureCollection 类表示捕获的子字符串的序列,并返回由单个捕获组所执行的捕获集。由于限定符,捕获组可以在单个匹配中捕获多个字符串。Captures 属性(CaptureCollection 类的对象)作为 Match 和 Group 类的成员提供,目的是便于对捕获的子字符串的集合进行访问。

Group 类表示来自单个捕获组的结果。因为 Group 可以在单个匹配中捕获零个、一个或更多的字符串(使用限定符),所以它包含 Capture 对象的集合。因为 Group 继承自 Capture,所以可以直接访问最后捕获的子字符串(Group 实例本身等价于 Captures 属性返回的集合的最后一项)。

Capture 类包含来自单个子表达式捕获的结果。

为了更好地理解上面类的相互关系,我把msdn的示例扩展了一下:

using System;
using System.Text.RegularExpressions;

/// <summary>
/// 2008-10-07 copyright 可爱的猴子
/// http://www.dreamdu.com/blog/
/// </summary>
public class RegexTest
{
   public static void Main()
   {
	   Regex r = new Regex("(Abc)+");
	   string str = "XYZAbcAbcAbcXYZAbcAb";
	   MatchCollection mc = r.Matches(str);
	   Console.WriteLine("---------------------Group-------MatchCollection={0}", mc.Count);
	   foreach (Match mt in mc)
	   {
		   Console.WriteLine("match = {0}", mt.Value);
		   Console.WriteLine("--------------Group={0}", mt.Groups.Count);
		   foreach (Group gc in mt.Groups)
		   {
			   Console.WriteLine("Group value is:{0}.    Index is:{1}",
gc.Value, gc.Index);
			   Console.WriteLine("-------Gapture={0}",
gc.Captures.Count);
			   foreach (Capture gg in gc.Captures)
			   {
				   Console.WriteLine("Capture value is:{0}.  Index is:{1}",
gg.Value, gg.Index);
			   }
		   }
	   }

	   Console.WriteLine("---------------------Capture-------MatchCollection={0}",
mc.Count);
	   foreach (Match mt in mc)
	   {
		   Console.WriteLine("match = {0}",
mt.Value);
		   Console.WriteLine("--------------Capture={0}",
mt.Captures.Count);
		   foreach (Capture gc in mt.Captures)
		   {
			   Console.WriteLine("Group value is:{0}.    Index is:{1}",
gc.Value, gc.Index);
		   }
	   }

	   Match m = r.Match(str);
	   GroupCollection gcc = m.Groups;
	   Console.WriteLine("--------------------------------GroupCollection={0}",
gcc.Count);
	   foreach (Group gt in gcc)
	   {
		   CaptureCollection cc = gt.Captures;

		   Console.WriteLine("--------------Captures={0}",
cc.Count);
		   foreach (Capture ccnp in gt.Captures)
		   {
			   Console.WriteLine("Capture value is:{0}.    Index is:{1}",
ccnp.Value, ccnp.Index);
		   }
	   }

	   CaptureCollection ccn = m.Captures;
	   Console.WriteLine("--------------------------------CaptureCollection={0}",
ccn.Count);
	   foreach (Capture gt in ccn)
	   {
		   Console.WriteLine("Capture value is:{0}.    Index is:{1}",
gt.Value, gt.Index);
	   }
   }
}

通过上面这个例子应该可以看出这几个类的实例的关系了,其实也很简单,就是互相包含的关系。

App_Code 文件夹

2008-07-28

在C#项目中的App_Code文件夹存放的源代码,在运行网站时将被自动编译,App_Code文件夹中代码的内容可以用在网站的所有页面中,而且App_Code文件夹中可以包含任意的文件与文件夹。App_Code文件夹中不允许使用用户控件。

还有Bin文件夹,但和App_Code不同的是此文件夹下只放编译好的文件,比如dll,exe等。

参考:ASP.NET 网站中的共享代码文件夹

asp.net服务器应用程序不可用的解决方法

2008-07-25

使用iis调试asp.net程序遇到服务器应用程序不可用怎么办?重装 aspnet_regiis.exe 程序,在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727目录下,就是在.net的目录下,我的.net版本是v2.0.50727。

重装方法,在windows的cmd下输入

aspnet_regiis.exe -i

等几分钟安装ok。重启iis,搞定。

参考:http://bbs.bitscn.com/49886