利用链接URL的GET方式删除记录操作的安全隐患

最近在研究ASP.NET的MVC3,在asp.net的系列讲解中了解到一个有趣的安全问题,Stephen Walther已经在其《ASP.NET MVC Tip #46 – Don’t use Delete Links because they create Security Holes》文章中做了相关描述,基本的意思就是我们常常在设计信息系统时往往会很随意的将Delete记录这个操作设计成一个链接,通过这个链接以GET的方式传入要删除记录的ID,然后服务器处理脚本或程序就会删除我们所指定ID的记录,这听起来没有什么问题,但是有一种情况是存在的。

假设删除记录的URL是http://wangye.org/Sample/Delete/23,其中Delete后面的23表示要删除记录的ID,好,目前的状态是只要我们不去访问这个URL,那么这条ID为23的记录就是安全的,这时骇客想要删除这条记录,他们往往会发给你一封邮件,内容的源代码如下:

1
<img src="http://wangye.org/Sample/Delete/23" />

当你打开这封邮件时,虽然http://wangye.org/Sample/Delete/23不是一个图片,但是你的计算机仍然会Request这个地址,那么造成的后果是什么呢,ID=23的这条记录被删除了,很显然这个不是我们所期望的,但是骇客们往往就这样做到了,不过做到这个还要有两个前提:1.知道操作删除的URL地址和格式。2.获得访问权限。目前大多数删除操作是在访问权限控制之下的,但是如果我们在登录系统并获得访问权限后再打开那封问题邮件,那么这道防线就形同虚设了。

Stephen Walther提出的解决方案就是避免使用GET方式进行删除操作,当然通常情况下HTML只能支持GET和POST操作,所以很自然的就想到采用POST来取代GET进行相关操作,是的这不失为一个好的办法,不过HTTP除了POST和GET还有另外的操作方式,比如PUT和DELETE等,在HTTP中GET、POST、PUT、DELETE就对应着对这个资源的查、改、增、删4个操作,当然要实现DELETE操作还是要借助于JavaScript脚本,比如Ajax删除。在MVC中的Controller里的处理删除的方法前面加上[AcceptVerbs(HttpVerbs.Delete)]标识,表示仅接受DELETE操作。

然后前端脚本可以像下面这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script type="text/javascript">
  function deleteRecord(recordId)
  {
    // Perform delete
    var action = "/Home/Delete/" + recordId;
 
    var request = new Sys.Net.WebRequest();
    request.set_httpVerb("DELETE");
    request.set_url(action);
    request.add_completed(deleteCompleted);
    request.invoke();
  }
 
  function deleteCompleted()
  {
    // Reload page
    window.location.reload();
  }
</script>
<a onclick="deleteRecord(23)" href="javascript:void(0)">Delete 23</a>

通过上面的脚本就可以利用DELETE方法就可以安全的向服务器发出删除ID=23这条记录了。当然如果你不希望采用JavaScript方式的话那只有通过表单的POST形式进行删除了,至于ID,可以放在type=hidden的input控件里。

C#利用反射(Reflection)进行SHA1和MD5的哈希(Hash)加密

最近在ASP.NET项目中用到的,主要是对用户密码进行哈希加密,传统编程进行的两种哈希加密方式一般如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Text;
using System.Security.Cryptography;
 
namespace ConsoleApplication1
{
    class Program
    {
        public static string ComputeSHA1Hash(string str)
        {
            using (SHA1CryptoServiceProvider
                sha1Csp = new SHA1CryptoServiceProvider())
            {
                str = Convert.ToBase64String(
                        sha1Csp.ComputeHash(
                        Encoding.UTF8.GetBytes(str)
                        )
                    );
                sha1Csp.Clear();
            }
            return str;
        }
 
        public static string ComputeMD5Hash(string str)
        {
            using (MD5CryptoServiceProvider
                md5Csp = new MD5CryptoServiceProvider())
            {
                str = Convert.ToBase64String(
                        md5Csp.ComputeHash(
                        Encoding.UTF8.GetBytes(str)
                        )
                    );
                md5Csp.Clear();
            }
            return str;
        }
 
        static void Main(string[] args)
        {
            System.Console.WriteLine(ComputeSHA1Hash(@"http://wangye.org"));
            System.Console.WriteLine(ComputeMD5Hash(@"http://wangye.org"));
        }
    }
}

通过代码可以很明显的看出,我们主要引入System.Security.Cryptography的命名空间,然后调用其SHA1CryptoServiceProvider进行SHA1哈希或者调用MD5CryptoServiceProvider进行MD5哈希,当然由于哈希所需要的参数是字节型的,所以还需要将字符串进行一次字节编码,为了更好的兼容性,我们这里选择Encoding.UTF8.GetBytes进行UTF8编码,然后将哈希编码好的字节码再转换为Base64,至此整个哈希编码完成。

大家可能看到这两个函数比较类似,有没有什么办法将它们整合一下,变得更通用呢?对了,你可能想到了反射(Reflection),确实我们可以引入反射机制,从而让我们的代码更精简,更通用。反射功能需要引入System.Reflection命名空间,然后查找SHA1CryptoServiceProvider和MD5CryptoServiceProvider的定义,发现其共同继承了HashAlgorithm这个类,这么说这个类可以作为一个通用的类型。Assembly.GetAssembly().CreateInstance()的方式创建对象,那么GetAssembly()的参数类型该如何获得呢?我们可以通过System.Type.GetType(“命名空间名称”)这个方法得到,比如说System.Type.GetType(“System.Security.Cryptography.SHA1CryptoServiceProvider”)将得到SHA1CryptoServiceProvider的类型,那么SHA1就可以独立出来,就像System.Type.GetType(“System.Security.Cryptography.”+param+”CryptoServiceProvider”),param如果是SHA1,那么就是SHA1的Provider,如果是MD5,那么就是MD5的Provider。恩,先简单介绍到这里,具体的代码如下:

继续阅读“C#利用反射(Reflection)进行SHA1和MD5的哈希(Hash)加密”