WebMatrix - 保护您的网页网站

您的网页网站受到威胁。有人在那里想要闯入限制区域,下载他们不应该访问的文件,搞砸了你的数据库并窃取你的密码。更糟糕的是,他们仍然希望将应用程序用作网关到Web服务器,以便他们可以完全控制它。本文介绍了这些威胁以及如何保护您的申请。

打开Web应用程序安全项目 网站列出了您的网站所需的10个威胁。顶级两个威胁始终是相同的,SQL注入和跨站点脚本(XSS),尽管订单会根据在Internet上找到的实例数。这两个威胁都是非常真实的,并且可以对您的Web应用程序或其坐在的服务器具有灾难性影响。大多数人认为这些事情永远不会发生在他们的小地点。毕竟,它不是你在建造管理人员钱或信用卡细节的网站,是吗?虽然很多严重的黑客对金钱收益有兴趣,但很多人只是失败者和其他较低的生活形式,因为他们可以造成破坏。这些人中的许多人也使用自动机器人扫描安全漏洞,甚至不知道它们是您使用的网站,因为它们用作控制服务器的手段,或窃取人们的身份。

SQL注入

SQL注入是恶意用户的技术"inject"有效的SQL进入您的代码,然后由数据库执行,从而导致不需要的副作用。您可能想知道当您在网页中的SQL硬件时,您可能会如何完成,但实际上非常容易。如果您的SQL仅部分写入,并且取决于来自用户的动态值,则您有一个潜在的安全漏洞。看看以下内容:

Select Count(*) From Users Where Username = '" + @Request["username"] + "' And Password = '" + @Request["password"] + "'"

上面的SQL正在等待来自用户的一些值,该值将通过以窗体的日志提供。通常,应用一个测试以确保数据库表中的至少一行与用户提供的值匹配,因此如果数据库中的返回值大于0,则让验证用户并让它们进入ISR实例的受限区域。您希望用户只需提供用户名和密码,但没有什么可以阻止它们添加更多。例如,如果用户进入下列用户名和密码文本框:

' or ''='

从数据库执行的生成的SQL如下:

Select Count(*) From Users Where Username = '' or ''='' And Password = '' or ''=''

数据库中的每一行都匹配或条件,这意味着计数值将大于0.现在用户已通过测试,但甚至不必尝试猜测有效的用户名和PasWord组合。 SQL注入工程的此示例使撇号是有效的SQL分隔符。

但是,有更多... SQL CE,Web页面的默认数据库paltform来自一些更严重的攻击,因为它不支持批处理语句。但SQL Server Express和Full Edition Do,这意味着用户可以向现有命令添加额外的命令。考虑来自用户的以下输入:

'DROP table Users--

破折号 - 充当SQL Server评论,这意味着什么"DROP table Users"被忽略,但如果您在数据库中有一个名为用户的表,则刚刚删除。

这些是SQL注入的是相当简单的例子,但是说明它是一个真正的问题,易于实现。 SQL注入可能会导致更严重的问题,例如获取SQL Server在Web服务器上运行操作系统命令,从而有效地使黑客完全控制它。

跨站点脚本(XSS)

XSS是将恶意JavaScript注入页面并导致用户执行的实践。如果您使用过jQuery,您将现在已经让JavaScript非常强大。使用此技术,为黑客注入JavaScript,将更改表单的操作属性更改为指向其服务器上的页面,并窃取您的用户名和密码。有关如何发生这种情况的例子,以及潜在的危险是(包括饼干盗窃或篡改,窃取一个人的身份,内容注入,爆破广告的名称为几个)看 来自乔斯塔克纳的这个视频 of Microsoft.

保护

SQL注入和XSS漏洞都是由相同的源 - 用户输入产生的。进一步来说, 不合理的 用户输入。保护网站的规则编号是验证数据类型和范围的所有用户输入。但在我们看看如何做到这一点之前,我们应该定义什么"user input"实际上是。到目前为止,示例集中在表单上集中在文本框中,但还有其他途径,其中Web应用程序接受传入值,并且可能形成SQL语句的一部分。这些是URL,cookie,隐藏字段,下拉列表,复选框,单选按钮,提交按钮的查询字符串......

您可能此处会想知道地球在地球上下拉列表可能是攻击的途径。毕竟,您提供了代码中的值,不是吗?你所做的只是检查哪个 你的 用户选择的值。实际上,寄回的东西可能不是你的价值之一。黑客很容易改变硬编码值。他们可以将页面本地副本保存到桌面,在记事本中编辑它,然后从那里提交它。或者他们可以使用Firebug或类似的工具在提交表单之前更改值。永远不要信任客户来自客户的任何值。

检查数据类型有许多帮助主者:

Isbool() 检查值是否可以转换为布尔值
isdatetime() 检查值是否为.NET DateTime
isdecimal() 检查值是否为小数
isfloat() 检查值是否是浮点数
isint() 检查值是否为整数

 

请注意,来自用户的所有值最初是字符串,因此这些方法检查是否可以将转换转换为相关数据类型。如果是, 真的 被退回。让我们假设您的网站显示了许多文章。每个人都有一个物质,你的主页页面列出了每个与每个链接的文章。链接URL可能会如下所示:

http://mysite.com/Article/20

在“文章”中的代码中.CSHTML中,您可能会使用值20从数据库中检索正确的项目。在您尝试之前,请确保使用ISINT()方法是整数:

@if(UrlData[0].IsInt()){
    @:It's a number <br />
}

HTML输入和XSS攻击

默认情况下,ASP.NET可以通过表单或URL阻止人员发布HTML标记。这是一个专门用于阻止人们注射的防御措施<script>标记到您的页面,他们可以从中启动XSS攻击。管理的机制被称为请求验证。我们可以通过以下练习在行动中看到这种机制。创建这样的页面:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
    <form method="post" action="">
        @HTML..textarea.("input")
        <br />
        <input type="submit" name="action" value="Submit" />
        <br />
        @Request["input"]
    </form>
    </body>
</html>

这是一种简单的表单,具有TextArea和一个用于将表格与代码一起发布的提交按钮,以呈现提交的任何代码。我用了 HTML..textarea. 帮手。如果您运行该页面并在TextBox中输入以下内容:

<strong>Hello World!</strong>

然后点击提交,你会看到一个YSOD(黄色屏幕死亡):

这很好知道,但可能有时需要允许用户将HTML提交给您的网站,例如,如果您为其提供了丰富的文本编辑器,则可以格式化其提交以进行显示。这是编辑可能提交新文章的论坛,博客评论和内容管理系统的常见要求。关于网页的伟大事物之一是您可以为各个表单字段切换请求验证,同时仍然保持对所有其他用户输入的保护,包括URL。您可以使用您不希望受保护的表单字段的名称的Request.unvalidated()方法执行此操作:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
    <form method="post" action="">
        @HTML..textarea.("input")
        <br />
        <input type="submit" name="action" value="Submit" />
        <br />
        @Request.Unvalidated("input")
    </form>
    </body>
</html>

再次运行页面,输入相同 <strong>Hello World</strong>。这次你没有错误,但结果不是你可能期望的,要么是:

这展示了网页对XSS攻击提供的另一种内置的防御层。默认情况下,要呈现给浏览器的所有输出都是HTML编码的。同样,这可能是不可取的 - 您允许用户输入HTML,所以可能是您想要在预期显示它。但是,你不希望人们逃避注射<script>标签。管理这一点的一个非常简单的方法是如果它包含,只需拒绝任何输入"<script>":


<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
    <form method="post" action="">
        @HTML..textarea.("input")
        <br />
        <input type="submit" name="action" value="Submit" />
        <br />
        @if(!Request.Unvalidated("input").IsEmpty()){
            if(!Request.Unvalidated("input").ToString().Contains("<脚本>"))
            {
                @Request.Unvalidated("input")
            }
        }
    </form>
    </body>
</html>

这是一个相当粗略的,但主要是有效的管理方式。你可能还想考虑一个"white list"方法,您确定允许的HTML标记。

using System;
using System.Web;
using System.Text.RegularExpressions;

public static class HTML.Sanitizer
{
    private static Regex _tags = new Regex("<[^>]*(>|$)", 
        RegexOptions.Singleline | 
        RegexOptions.ExplicitCapture | 
        RegexOptions.Compiled);
    
    private static Regex _白名单 = new Regex(@"
        ^</?(b(lockquote)?|code|d(d|t|l|el)|em|h(1|2|3)|i|kbd|
        li|ol|p(re)?|s(ub|up|trong|trike)?|ul)>$|
        ^<(b|h)r\s?/?>$",
        RegexOptions.Singleline | 
        RegexOptions.ExplicitCapture | 
        RegexOptions.Compiled | 
        RegexOptions.IgnorePatternWhitespace);
    
    private static Regex _白名单_a = new Regex(@"
        ^<a\s
        href=""(\#\d+|(https?|ftp)://[-a-z0-9+&@#/%?=~_|!:,.;\(\)]+)""
        (\stitle=""[^""<>]+"")?\s?>$|
        ^</a>$",
        RegexOptions.Singleline | 
        RegexOptions.ExplicitCapture | 
        RegexOptions.Compiled | 
        RegexOptions.IgnorePatternWhitespace);
    
    private static Regex _白名单_img = new Regex(@"
        ^<img\s
        src=""https?://[-a-z0-9+&@#/%?=~_|!:,.;\(\)]+""
        (\swidth=""\d{1,3}"")?
        (\sheight=""\d{1,3}"")?
        (\salt=""[^""<>]*"")?
        (\stitle=""[^""<>]*"")?
        \s?/?>$",
        RegexOptions.Singleline | 
        RegexOptions.ExplicitCapture | 
        RegexOptions.Compiled | 
        RegexOptions.IgnorePatternWhitespace);


    public static string AsSafeHtml(this string html)
    {
        细绳 tagname;
        Match tag;
    
        // match every HTML tag in the input
        MatchCollection tags = _tags.Matches(html);
        for (int i = tags.Count - 1; i > -1; i--)
        {
            tag = tags[i];
            tagname = tag.Value.ToLowerInvariant();
            
            if(!(_whitelist.IsMatch(tagname) || _whitelist_a.IsMatch(tagname) || _whitelist_img.IsMatch(tagname)))
            {
                html = html.Remove(tag.Index, tag.Length);
            }
        }
        return html;
    }
}

它已放在App_code中的C#类文件中,该文件名为HTMLSanitizer.cs。这可能看起来非常复杂,但它使用正则表达式来简单地剥离未出现在白色列表中的任何标签。有3个Whilte列表 - 第一个是我将允许的一般HTML标记,然后是第二个,其次是允许的<a href>标签,第三个允许<img> tags. <script>不被允许。在该示例中,也不是H4,H5或H6标签。你可以通过改变来改变它 h(1 | 2 | 3) 阅读 H(1 | 2 | 3 | 4 | 5 | 6) 在里面 _白名单 Regex pattern.

返回类型的返回类型 assafehtml() 方法是AN 细绳。默认情况下,这仍然没有处理我们早些时候的问题,所有输出都是HTML编码的。它需要转换为HTMLString。自从此以来 assafehtml() 方法已经进行了扩展方法,使用它很简单:

@{
    var input = Request.Unvalidated("input");
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
    <form method="post" action="">
        @HTML..textarea.("input")
        <br />
        <input type="submit" name="action" value="Submit" />
        <br />
        @if(!input.IsEmpty()){
            @(new HtmlString(input.ToString().AsSafeHtml()))
        }
    </form>
    </body>
</html>

现在,当 <strong>Hello World!</strong> 输入到表单中,输出如需要:

如果有人试图输入未出现在白色列表中的HTML标记,例如<script>, they are removed:

SQL注入和参数

现在我们已经学会了如何验证输入以及如何安全地接受HTML,我们仍然需要阻止用户尝试注入SQL。第一种形式的防御已经放置在原地 - 如果预期值是弦乐的任何东西,那么 IsInt(), isdecimal() ETC测试将防止攻击者已成功附加任意字符串。尽管如此,字符串仍然脆弱。我们可以考虑采用黑名单方法来防止SQL注入。这将需要筛选针对已知的SQL关键字(丢弃,创建,执行etc)和语法(如单引号,双划线)进行筛选输入,但此问题有两个问题。首先是用户尝试提供许多SQL关键字的合法原因。其中许多人都是常见的英语用法。第二个是,将需要维护黑名单,因为介绍了新关键字。

对SQL注入的唯一可接受的保护是使用参数。幸运的是,网页使这种微不足道,所以真的没有任何原因是为了不这样做。用于对数据库执行命令的每种方法, database.query(), database.querysingle(), database.queryValue()database.execute() 拍摄表示要执行的命令的字符串和表示参数值的可选对象数组。让我们假设您有一种将新文章添加到数据库的表单。这些文章由多个元素组成 - 标题,介绍和作者和一个身体。您决定允许HTML在身体中,以便作者可以根据他或她喜欢的结果格式化结果。页面的代码应该如下所示:

@{

    if(IsPost){
        var title= Request["title"];
        var intro = Request["intro"];
        var body = Request.Unvalidated("body");
        if(!body.IsEmpty()){
            body = body.AsSafeHtml();
        }
        var author = Request["author"];
        var db = Database.Open("MyDatabase");
        var sql = "INSERT INTO Articles (Title, Intro, Body, Author) VALUES (@0, @1, @2, @3)";
        db.Execute(sql, title, intro, body, author);
    }
}
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
    <form method="post" action="">
        <div>Title</div>
        <div>@HTML..TextBox("title")</div>
        <div>Intro</div>    
        <div>@HTML..textarea.("intro", new{cols="40", rows="4"})</div>
        <div>Body</div>
        <div>@HTML..textarea.("body", new{cols="40", rows="8"})</div>
        <div>Author</div>
        <div>@HTML..TextBox("author")</div>
        <input type="submit" name="action" value="Submit" />
    </form>
      
    </body>
</html>

我们排除了从请求验证中包含文章的身体部分的TextArea。其他表单字段仍然受到保护。如果表单被提交,则每个数据字段被传输到变量,然后打开到数据库的连接。 SQL语句看起来非常正常,直到值。 @ 0,@ 1等是参数占位符,并告诉数据库期望分别传递一些值。调用参数@ 0,@ 1等,对于让它们正常工作至关重要。总是从0开始,每次递增1。当。。。的时候 database.execute() 调用方法,第一个参数是SQL,然后通过逗号分隔的每个参数的值传递,每个分隔,以及它们在SQL本身中出现的顺序。再次,位置是全部重要的。 SQL Server(完整和表达版本)在命名参数上工作,这意味着在给出的名称上完成对参数的值匹配,但Web Pages数据库方法并非如此。

这种如何保护我们免受SQL注射?如果有人补充说"DROP Table Articles"到了一个表单字段,为什么这不会对数据库执行?简单地说,如果在数据库中的基于文本的字段中定位,则参数值被视为文字字符串。任何单引号都会自动逃逸,并且整个东西由数据库提供商包裹在正确的分隔符中。对于数字类型,一旦数据库提供程序建立了数据库中的目标字段期望某种数字,就会在无法转换为数字类型的参数值中传递的任何东西会导致要提出错误消息。简单,非常安全。

结论

什么不断惊人的是,保护你的申请很容易做,但很多人都没有采取基本的预防措施。好的,它需要更多的工作,而不是这样做,我相信很多人都不采取这些预防措施,因为他们认为黑客永远不会瞄准他们的应用。这 最近在ASP.NET论坛中报告的攻击 最近表明任何人都很脆弱。不仅如此,而且越常见的攻击实际上利用了SQL注射 XSS,因为他们首先撬动SQL注入漏洞,并且一旦找到,它将使用它将脚本注入数据库表中的每一行。既然你知道威胁是什么以及如何对他们进行决定的代码,不要让它发生在你身上。