【问题标题】:Is it unwise to have a static method be used for security validation in ASP.net?在 ASP.net 中使用静态方法进行安全验证是不明智的吗?
【发布时间】:2010-08-06 02:27:55
【问题描述】:

我有一个像这样的静态方法

public static string DoSomethingToString(string UntrustedString)
{
//parse format and change string here.
return newString.
}

因为我知道多次调用:

static int myInt=0;
public static int AddNumber()
{
lock(someObject)
{
myInt++;
return myInt;    
 }
}

将返回一个不断增加的数字(在页面之间共享),我不确定在 DoSomethingToString() 中如何处理局部变量;

我想了解在 ASP.net 中可以安全/不安全地使用静态方法的条件,只是为了让我的多线程大脑专注于这个主题。

更新:

大部分讨论都是围绕值类型进行的。我如何知道何时可以安全地调用更改我的引用类型的方法(显式或隐式)?查看 MSDN 文档并仅使用“threadSafe”的方法就足够了吗?

如果我称之为隐式更改的一个示例是使用 String.Split(),因为它是同一类的一部分。我认为有可能共享一些安全特性,并且不需要担心/勤奋。 (?)

我所说的显式更改(因为现在没有更好的词)是调用另一种方法来工作……它可以是静态的或实例类。我认为需要做更多的背景/研究工作来确保每个对象和方法都是线程安全的。

为了便于讨论,假设我有一个带有这个签名的方法:

ValidateStringAndContext(string untrustedString, Object myCustomUserContext)

它有一个引用以下对象的静态方法

public SecurityChecker
{
public static object CheckSecurityStatic(string DirtyData)
{
//do string.split
//maybe call a database, see if it's a token replay 
//

//OR  - alternate implementation
SecurityChecker sc = new SecurityChecker();
 if (sc.CheckSecurity(DirtyData))
  {
  myCustomUserContext.Property1 = new GUID()
  }
return myCustomUserContext;
}


public class bool CheckSecurity(string DirtyData)
{
//do string.split
//maybe call a database, see if it's a token replay 
// return true if OK return false if not
}
}

修改后的问题

如果我创建的静态“实用程序”类要创建另一个对象的实例,然后调用方法——与直接调用静态方法相比,我会遇到并发问题(变量相互覆盖)吗?

【问题讨论】:

  • 我不确定您所说的“我不确定如何处理局部变量”是什么意思。你能澄清一下吗?
  • 这里我使用“newstring”作为一个范围很大的局部变量。假设该方法的 CPU 密集度很高,需要 5 秒才能完成。如果另一个请求进来,而我的方法正在运行,是否会发生数据冲突/覆盖?为了更进一步,我的方法可能会创建对象的一些新实例(非静态)并让它们做一些事情。如果碰撞没有发生在我的方法中,它会发生在子对象中吗?
  • @MakerOfThings7:我刚刚注意到您的评论,只要在静态方法中声明 newString 和其他对象,就可以了。例如,如果您必须调用其他类来获取这些对象,并且它们是在其他地方创建的,那么肯定会出现问题。
  • @MakerOfThings7:对不起,示例中没有包含太多内容,无法真正说出一种或另一种方式。您能否制作一个完整的小型示例 - 可以编译 - 并执行您正在尝试做的事情(即使它不是线程安全的),以便其他人可以基于此提出建议?
  • 您的 AddNumber 实现已损坏。 return MyNumberlock 之外

标签: c# asp.net multithreading concurrency static


【解决方案1】:

每个调用静态方法的线程都有自己的调用堆栈。对 DoSomethingToString() 的每次调用都有自己的方法变量副本,完全独立于另一个线程上对该方法的任何其他调用(除非您将变量声明为静态,否则您可能会感到困惑 - 静态变量只有一个可供程序中多个线程访问的实例)。

与往常一样,当多个线程访问共享资源时,您需要考虑并发性。当资源仅存在于方法调用的上下文中时,并发性不是问题。

另请参阅:https://stackoverflow.com/questions/511378/net-static-methods-and-its-effects-on-concurrencyt

【讨论】:

  • 那么如果一个方法是静态的,那么该方法中的变量是隐式静态的还是非静态的?
  • 静态方法是一种无需实例化它所属的对象即可调用的方法。将方法设为静态不会使其变量变为静态,您仍需要在每个变量声明中显式使用 static 关键字来实现。
  • @MakerOfThings7:是的,在静态方法中声明的变量对于执行该代码的线程来说是本地的。这些变量可能在线程之间没有共享内存,并且它们是线程安全的。 @saille:很好的答案,回复:您的评论:静态关键字只能用于在 VB.NET 语言的方法中定义局部范围的静态变量,如果这就是您所指的。这种用法没有很多好的例子,我会警告不要这样做。这是我能找到的最好的例子:weblogs.asp.net/psteele/pages/7717.aspx
  • re:您的评论,如果您的意思是定义一个类级静态变量,然后在静态方法中访问该静态类级变量......那肯定是个问题...除非您使用锁定所有用法来管理对该静态变量的访问。例如,请参阅我的答案。
【解决方案2】:

与问题中显示的代码相比,与不可见的代码有关的一些非常重要的点...

DoSomeThingToString 方法是静态的,在该方法中声明的任何变量都将是该线程调用堆栈的本地变量。如果正在使用的变量是在函数外部定义的,那么您将在该内存上出现竞争条件。确保它看起来像这样,只使用局部变量:

public static string DoSomethingToString(string UntrustedString)
{
    var newString = UntrustedString;
    // operations on newString...
    return newString;
}

AddNumber 方法可能会遇到其他可能不明显的问题。如果这真的是你想要做的,添加一个数字,这样做:

System.Threading.Interlocked.Increment(ref myInt);

Interlocked.Increment 方法保证操作将在一个时钟周期内完成。

否则,在极少数情况下使用 lock 关键字可能会很棘手。这里有一些经验法则。始终锁定一个您创建并持有其引用的对象,甚至更好的是,您永远无法更改该对象。这意味着:只读,并在创建类时分配内存地址。看起来像这样:

static int myInt=0;
static readonly object aGoodLock = new object();
public static int MoreComplexIntStuff()
{
    lock(aGoodLock)
    {
        // Do stuff with myInt...
    }
    return myInt;
}

此外,这还不是全部。另一个问题是,只要变量 myInt 被访问,即使它在这个类的另一部分 - 或者如果它是公共的并在其他地方使用,你需要用锁来包装它。而且不仅仅是任何锁,而是您使用的同一个锁,aGoodLock。

帮助您的开发人员(可能还有您自己的长期记忆)的最佳方法是制作变量和包装它的锁private,并使用您要小心的属性公开 myInt在 get 和 set 中使用锁。

【讨论】:

  • 很好的答案!你能告诉我新建一个对象并使用它的含义吗?那个物体有什么特点?例如没有静态属性?
  • @MakerOfThings7,如果你在静态方法中新建一个对象,它就是你的,没有其他线程可以使用它。如果该对象本身具有静态属性,那也没关系。尽管没有关于手头问题的更多细节,但很难概括答案,因为有很多“变量”。 :)
【解决方案3】:

我感觉您认为变量只能是实例变量或静态变量,而忘记了局部变量与两者不同。考虑:

public class MyClass
{
    public int InstanceInt;
    public static int StaticInt;
    public int InstanceMethod()
    {
        int i = new Random().Next(1, 50);
        return i;
    }
    public static int StaticMethod()
    {
        int j = new Random().Next(1, 50);
        return j;
    }
}

这里InstanceInt是一个实例变量,StaticInt是一个静态变量。如果它们要被不同的线程访问,这两者都需要锁定,但是,InstanceInt 只有在 MyClass 的实例被不同的线程访问时才会被不同的线程访问。这可能发生在static MyClass AllThreadsSeeMe = new MyClass()、存储在静态集合中、显式地将其传递给另一个线程等情况下,否则不会发生。同时,StaticInt 本质上可以被应用程序中运行的所有线程访问,即使您注意确保线程之间没有共享 MyClass 的实例。

同时,ij 都是其函数的本地函数。每个调用该函数的线程都将获得自己的ij 副本,无论有多少线程可以调用它们。因此它们本质上是线程安全的。只有当这些方法中的任何一个更改静态或实例变量,或读取可以由不同方法更改的可变静态或实例变量时,它们才不是线程安全的(不可变的 - readonly - 变量也是线程安全的因为没有其他线程可以改变它们的值,因为根本没有线程可以改变它)。

【讨论】:

  • .. 有趣的是,在这种情况下您使用的是值类型。使用引用类型有什么不同?
  • 与引用类型有一个区别,即任何这些类型的变量(静态成员、实例成员和本地)都可以将同一个对象作为另一个变量引用。在这种情况下,通过查看代码的一部分我们认为线程安全的东西实际上可能不是,因为同一个对象保存在一个变量中,该变量将其暴露给来自其他线程的交互。它仍然认为局部变量本身总是线程安全的,但如果设置为成员(实例或静态)的值,它们可能不是。还有,分配本地->成员的操作。
  • 不过,与您的原始问题(处理字符串)特别相关的另一件事是,保存不可变对象的局部变量是线程安全的,因为没有其他线程可以更改它(对于字符串 X,X 处的字符串本身不能更改,但 X 所指的字符串可以)。不变性之所以有用的原因有很多,其中之一就是帮助提供线程安全。
  • 很好的答案,我喜欢您强调实例、静态和堆栈变量之间的区别。此外,值类型与引用类型。恕我直言,.NET 没有为程序员提供足够的信息来说明这些“不同的东西应该看起来不同”。
  • 谢谢。我同意它的探索不足。更何况不可变的优点,这似乎只是在 StringBuilder 和 UriBuilder 如何让我们解决这个问题时才提到的,而忽略字符串和 Uri 是不可变的是有原因的!其中一个原因是对线程安全的影响。该语言本身在此处具有只读性而不是 C++ 在不同上下文中以不同方式使用 const 的方式很棒,但文档不清楚(我从 C++ 的作者那里了解了不变性,而不是 C#)。
猜你喜欢
  • 1970-01-01
  • 2012-12-25
  • 2010-11-08
  • 2016-07-16
  • 1970-01-01
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多