有人可以解释在子类化 Windows 控件时出现的 LinkDemand 警告吗?
我有这个 HeaderlessTabControl,它是经典 TabControl 的子类。
// From http://social.msdn.microsoft.com/forums/en-US/winforms/thread/c290832f-3b84-4200-aa4a-7a5dc4b8b5bb/
// Author: Hans Passant (nobugz)
public class HeaderlessTabControl : TabControl {
protected override void WndProc(ref Message m) {
// Hide tabs by trapping the TCM_ADJUSTRECT message
if (m.Msg == 0x1328 && !DesignMode) {
m.Result = (IntPtr)1;
} else {
base.WndProc(ref m);
}
}
}
当我在项目上运行代码分析时,我收到以下警告:
警告 1 CA2122:Microsoft.Security: “HeaderlessTabControl.WndProc(ref Message)”调用 具有 LinkDemand 的“Message.Msg.get()”。通过拨打此电话, “Message.Msg.get()”间接暴露给用户代码。回顾 以下调用堆栈可能会暴露规避安全性的方法 保护:->'HeaderlessTabControl.WndProc(ref Message)'
->'HeaderlessTabControl.WndProc(参考消息)'
...以及与 Message.Msg.set()
和 TabControl.WndProc()
相关的两个类似警告。我知道我通过这样做暴露了一些代码。有人可以解释一下我可能在这里打开了什么样的安全漏洞,以及可能的修复方法吗?
I have this HeaderlessTabControl, which subclasses the classic TabControl.
// From http://social.msdn.microsoft.com/forums/en-US/winforms/thread/c290832f-3b84-4200-aa4a-7a5dc4b8b5bb/
// Author: Hans Passant (nobugz)
public class HeaderlessTabControl : TabControl {
protected override void WndProc(ref Message m) {
// Hide tabs by trapping the TCM_ADJUSTRECT message
if (m.Msg == 0x1328 && !DesignMode) {
m.Result = (IntPtr)1;
} else {
base.WndProc(ref m);
}
}
}
When I run Code Analysis on my project, I get this warning:
Warning 1 CA2122 : Microsoft.Security :
'HeaderlessTabControl.WndProc(ref Message)' calls into
'Message.Msg.get()' which has a LinkDemand. By making this call,
'Message.Msg.get()' is indirectly exposed to user code. Review the
following call stack that might expose a way to circumvent security
protection: ->'HeaderlessTabControl.WndProc(ref Message)'
->'HeaderlessTabControl.WndProc(ref Message)'
...as well as two similar warnings relating to Message.Msg.set()
and TabControl.WndProc()
. I understand that I've exposed some code by doing this. Can someone explain what kind of security holes I might have opened up here, and possible ways to fix it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
让我用五分钟的时间概述一下“传统”.NET 代码访问安全性。 (我们有一个更新的、简化的安全模型,应该用于新代码,但了解底层安全模型很有帮助。)
这个想法是程序集提供证据 ——诸如它们所在的位置、作者是谁等等。 策略消耗证据并生成与该程序集关联的权限的授予集。
当尝试执行需要特定权限的操作(例如,创建对话框或访问打印机或写入文件)时,运行时会发出对该权限的需求。该需求检查当前“堆栈上”的代码,以确定直接或间接调用当前代码的所有代码。 (*)
该要求规定,堆栈上的每个调用者都必须被授予所需的权限。这可以防止诱骗攻击,即恶意的低信任代码调用良性的高信任攻击。信任代码并“引诱”它代表其执行一些危险操作,以伤害用户。由于完整的需求检查直接和间接调用者,因此诱骗攻击被击败。
断言允许高信任代码修改需求语义。断言说“我是良性的高信任代码,并且我断言我不会被低信任的敌对调用者引诱代表其执行危险操作。”断言通常与较弱的需求配对;也就是说,高信任代码断言“即使调用者不能,我也可以安全地调用非托管代码”,然后要求“但调用者最好有访问打印机的权限,因为这就是我要对我的非托管代码执行的操作”代码许可”。
需求的问题在于它们昂贵。您必须进行完整的堆栈遍历并查看每个人的权限集。如果操作成本较低(例如,调整位图中的像素),您就不会希望每次都执行完整的要求,因为您将花费所有时间进行冗余的安全检查。
因此链接需求。 每个受保护方法的调用者都会执行一次链接请求,这是第一次使用调用受保护方法的代码,并且它仅检查直接调用者受保护的方法,而不是进行完整的堆栈遍历。之后,将执行链接要求的代码操作,不会对该调用者进行安全检查。 (它确实应该被称为“jit 需求”,而不是“链接需求”,因为使用的机制是在调用者被 jitted 时检查需求。)
显然这是更便宜的方式 - 一个只检查一个程序集的每个调用者检查比检查堆栈上的每个程序集每次调用进行一次检查要便宜,而且更危险 >。
链接需求基本上是推卸责任。链接需求表示“呼叫者,通过我的链接需求检查,您可以从现在开始以便宜的价格给我打电话。但是我现在关闭安全系统,因此您 现在负责确保您的呼叫者无法利用我授予您将来无需安全检查的情况下呼叫我的权利这一事实来成功攻击用户。”
您正在调用具有链接需求的方法。所以你面临的问题是:你愿意承担这个责任吗?你可以便宜地调用这个方法。您是否愿意保证没有低信任的恶意调用者可以利用您在没有安全检查的情况下调用该方法的事实来伤害用户?
如果您不愿意或无法做出该保证,请发出您自己的请求以获取链接要求的权限;然后要求所有呼叫者满足要求。或者,将责任转嫁给您的调用者:向您的调用者发出链接请求并让他们完成工作。
(*) 正如我喜欢指出的那样,调用堆栈实际上并不会告诉您谁调用了您,它告诉您接下来控制将去往何处。由于这些通常是同一件事,所以一切都很顺利。最终可能会陷入“谁给你打电话?”的情况。已经脱离了“你下一步要去哪里?”;在这些环境中,您必须非常小心地使用传统风格的代码访问安全性。较新的“沙盒”安全模型更适合这些场景。
Let me give you the five minute overview of "traditional" .NET code access security. (We have a newer, simplified security model that should be used for new code, but an understanding of the underlying security model is helpful.)
The idea is that assemblies provide evidence -- things like where they are located, who wrote them, and so on. Policy consumes evidence and produces a grant set of permissions associated with that assembly.
When an action that requires a particular permission -- say, creating a dialog box or accessing a printer or writing to a file -- is attempted, the runtime issues a demand for that permission. The demand examines the code currently "on the stack" to determine all the code that called the present code, directly or indirectly. (*)
The demand says that every caller on the stack must have been granted the required permission. That prevents the luring attack, whereby hostile low-trust code calls benign high-trust code and "lures" it into doing some dangerous operation on its behalf, to harm the user. Since the full demand checks direct and indirect callers the luring attack is thereby defeated.
An assert allows high-trust code to modify the demand semantics. An assert says "I am benign high trust code and I assert that I cannot be lured by a low-trust hostile caller into performing a dangerous operation on its behalf." An assert is typically paired with a weaker demand; that is, high-trust code asserts "I can call unmanaged code safely even if the caller cannot", and then demands "but the caller had better have permission to access the printer, because that's what I'm going to do with my unmanaged code permission".
The problem with demands is that they are expensive. You have to do a full stack walk and look at everyone's permission set. If the operation is cheap -- say, tweaking a pixel in a bitmap -- you don't want to do a full demand every time because you'd spend all your time doing redundant security checks.
Thus the link demand. A link demand is performed once per caller of the protected method, the first time the code that calls the protected method is used, and it only checks the immediate caller of the protected method, rather than doing a full stack walk. After that, the link-demanded code operations are performed with no security checks for that caller. (It really should be called a "jit demand", not a "link demand" because the mechanism used is that the demand is checked when the caller is jitted.)
Obviously that is way cheaper -- one check per caller that only looks at one assembly is cheaper than one check per call that looks at every assembly on the stack -- and way more dangerous.
The link demand is basically buck-passing. The link demand says "Caller, by passing my link demand check, you get to call me for cheap from now on. But I am turning the security system off now, and therefore you are now responsible for ensuring that your caller cannot successfully attack the user by taking advantage of the fact that I am granting you the right to call me without security checks in the future."
You are calling a method with a link demand. So the question you face is: are you willing to take that responsibility? You get to call that method for cheap. Are you willing to guarantee that no low-trust hostile caller can use the fact that you get to call that method without security checks to harm the user?
If you are not willing or able to make that guarantee, then issue your own demand for the link-demanded permission; that will then require all your callers to meet the requirements. Or, pass the buck to your caller: issue a link demand to your caller and make them do the work.
(*) As I am fond of pointing out, the call stack does not actually tell you who called you, it tells you where control is going next. Since those are usually the same thing, everything works out fine. It is possible to end up in situations where "who called you?" has become divorced from "where are you going next?"; in those environments you have to be very careful using traditional-style code access security. The newer "sandboxed" security model is better suited for those scenarios.
FxCop 的警告可能有点过分热心。这里的情况确实如此,这段代码总是会满足 CAS 需求,因为它调用 base.WndProc()。最终调用 Control.WndProc() ,如下所示:
InheritanceDemand 就足够了。您可以安全地忽略此警告。
FxCop can be a bit overzealous with its warnings. That's certainly the case here, this code will always hit a CAS demand because it calls base.WndProc(). Which ultimately ends up calling Control.WndProc() which looks like this:
The InheritanceDemand is sufficient. You can safely ignore this warning.