【问题标题】:How do I encrypt URLs in ASP.NET MVC?如何在 ASP.NET MVC 中加密 URL?
【发布时间】:2011-05-26 18:28:10
【问题描述】:

我需要加密我的 ASP.NET MVC 应用程序中的 URL。

是否需要在路由集合的全局页面中编写代码来加密所有 URL?

【问题讨论】:

  • url加密有什么用?或者您是否尝试对部分 url 进行编码?如果是这样,Base64 编码应该会有所帮助。
  • 我的意思是说最终用户不应该看到我的带有 Id 值等的 URL。
  • 如何在全局文件中声明这些东西?
  • 你想解决什么问题?

标签: asp.net asp.net-mvc asp.net-mvc-2


【解决方案1】:

加密 URL 是个坏主意。期间。

你可能想知道我为什么这么说。

我为一家加密其 URL 的公司开发了一个应用程序。这是一个网络表单应用程序。仅从 URL 来看,几乎不可能分辨出我敲击的代码的哪一部分导致了该问题。由于调用 webform 控件的动态特性,您只需要知道软件将要运行的路径即可。这很令人不安。

此外,应用程序中没有基于角色的授权。这一切都基于被加密的 URL。如果您可以解密 URL(如果可以加密,则可以解密),那么您可以想象输入另一个加密的 URL 并冒充另一个用户。我并不是说这很简单,但它可能会发生。

最后,您多久使用一次互联网并查看加密的 URL?当你这样做时,你会死在里面吗?我愿意。 URL 旨在传达公共信息。如果您不希望它这样做,请不要将其放在您的 URL 中(或要求对您网站的敏感区域进行授权)。

您在数据库中使用的 ID 应该是可供用户查看的 ID。如果您使用 SSN 作为主键,那么您应该为 Web 应用程序更改该架构。

任何可以加密的东西都可以被解密,因此容易受到攻击。

如果您希望用户仅在获得授权的情况下访问某些 URL,那么您应该使用 ASP.NET MVC 中提供的[Authorize] 属性。

【讨论】:

  • “任何可以加密的东西都可以被解密,因此容易受到攻击。”。多么令人惊讶的无用声明。我同意你的其他 cmets。
  • @George:这句话的含义是什么都不应该加密;这显然是荒谬的。
  • 那肯定不是加密的状态,否则网上银行和网上购物就会陷入困境。我们使用密码哈希是因为这意味着某些流氓员工将无法访问用户可能用于其他 50 个网站的密码。我认为 URL 加密没有意义,但 SSL 之类的东西在防止第三方看到您的数据方面做得很好。
  • @George:我不能继续和你讨论这个话题,它已经到了一个荒谬的地方。我会把它留给你。
  • 对不起,这里不得不同意Noon。有时应该对 url 中的 ID 进行某种混淆。因为即使是“经过身份验证的用户”也会尝试在路由中弄乱 ID 值以查看他们可能不应该看到的内容。我正在研究这个,会给出更好的答案。
【解决方案2】:

加密整个网址,我同意,这是个非常糟糕的主意。加密url参数?没那么多,实际上是一种有效且广泛使用的技术。

如果您真的想加密/解密 url 参数(这根本不是一个坏主意),请查看 Mads Kristensen 的文章“HttpModule for query string encryption”。

您需要修改 context_BeginRequest 以使其适用于 MVC。只需删除检查原始 url 是否包含“aspx”的 if 语句的第一部分。

话虽如此,我已经在几个项目中使用了这个模块(如果需要,可以使用转换后的 VB 版本),并且在大多数情况下,它就像一个魅力。

但是,在某些情况下,我遇到了一些 jQuery/Ajax 调用无法正常工作的问题。我确信可以修改模块以弥补这些情况。

【讨论】:

    【解决方案3】:

    根据此处的答案,顺便说一句,这对我不起作用,我发现 另一个解决方案基于我的特定 MVC 实现,而且它也可以工作,具体取决于您是否使用 II7或 II6。两种情况都需要稍作改动。

    II6

    首先,您需要将以下内容添加到您的 web.config(根目录,而不是 View 文件夹中的那个)。

     <system.web>
        <httpModules>
          <add name="URIHandler" type="URIHandler" />
        </httpModules>
    

    II7

    将其添加到您的 web.config(根目录,而不是 View 文件夹中的那个)。

      <system.webServer>
        <validation validateIntegratedModeConfiguration="false" />
        <modules runAllManagedModulesForAllRequests="true">
          <remove name="URIHandler" />
          <add name="URIHandler" type="URIHandler" />
        </modules>
    

    或者您可以同时添加两者。真的没关系。

    接下来使用这个类。正如您可能已经注意到的那样,我称之为 - URIHandler

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.IO;
    using System.Text;
    using System.Security.Cryptography;
    using System.Diagnostics.CodeAnalysis;
    
    public class URIHandler : IHttpModule
        {
            #region IHttpModule members
            public void Dispose()
            {
            }
    
            public void Init(HttpApplication context)
            {
                context.BeginRequest += new EventHandler(context_BeginRequest);
            }
            #endregion
    
            private const string PARAMETER_NAME = "enc=";
            private const string ENCRYPTION_KEY = "key";
    
            private void context_BeginRequest(object sender, EventArgs e)
            {
                HttpContext context = HttpContext.Current;
                //if (context.Request.Url.OriginalString.Contains("aspx") && context.Request.RawUrl.Contains("?"))
                if (context.Request.RawUrl.Contains("?"))
                {
                    string query = ExtractQuery(context.Request.RawUrl);
                    string path = GetVirtualPath();
    
                    if (query.StartsWith(PARAMETER_NAME, StringComparison.OrdinalIgnoreCase))
                    {
                        // Decrypts the query string and rewrites the path.
                        string rawQuery = query.Replace(PARAMETER_NAME, string.Empty);
                        string decryptedQuery = Decrypt(rawQuery);
                        context.RewritePath(path, string.Empty, decryptedQuery);
                    }
                    else if (context.Request.HttpMethod == "GET")
                    {
                        // Encrypt the query string and redirects to the encrypted URL.
                        // Remove if you don't want all query strings to be encrypted automatically.
                        string encryptedQuery = Encrypt(query);
                        context.Response.Redirect(path + encryptedQuery);
                    }
                }
            }
    
            /// <summary>
            /// Parses the current URL and extracts the virtual path without query string.
            /// </summary>
            /// <returns>The virtual path of the current URL.</returns>
            private static string GetVirtualPath()
            {
                string path = HttpContext.Current.Request.RawUrl;
                path = path.Substring(0, path.IndexOf("?"));
                path = path.Substring(path.LastIndexOf("/") + 1);
                return path;
            }
    
            /// <summary>
            /// Parses a URL and returns the query string.
            /// </summary>
            /// <param name="url">The URL to parse.</param>
            /// <returns>The query string without the question mark.</returns>
            private static string ExtractQuery(string url)
            {
                int index = url.IndexOf("?") + 1;
                return url.Substring(index);
            }
    
            #region Encryption/decryption
    
            /// <summary>
            /// The salt value used to strengthen the encryption.
            /// </summary>
            private readonly static byte[] SALT = Encoding.ASCII.GetBytes(ENCRYPTION_KEY.Length.ToString());
    
            /// <summary>
            /// Encrypts any string using the Rijndael algorithm.
            /// </summary>
            /// <param name="inputText">The string to encrypt.</param>
            /// <returns>A Base64 encrypted string.</returns>
            [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
            public static string Encrypt(string inputText)
            {
                RijndaelManaged rijndaelCipher = new RijndaelManaged();
                byte[] plainText = Encoding.Unicode.GetBytes(inputText);
                PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(ENCRYPTION_KEY, SALT);
    
                using (ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16)))
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(plainText, 0, plainText.Length);
                            cryptoStream.FlushFinalBlock();
                            return "?" + PARAMETER_NAME + Convert.ToBase64String(memoryStream.ToArray());
                        }
                    }
                }
            }
    
            /// <summary>
            /// Decrypts a previously encrypted string.
            /// </summary>
            /// <param name="inputText">The encrypted string to decrypt.</param>
            /// <returns>A decrypted string.</returns>
            [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
            public static string Decrypt(string inputText)
            {
                RijndaelManaged rijndaelCipher = new RijndaelManaged();
                byte[] encryptedData = Convert.FromBase64String(inputText);
                PasswordDeriveBytes secretKey = new PasswordDeriveBytes(ENCRYPTION_KEY, SALT);
    
                using (ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(secretKey.GetBytes(32), secretKey.GetBytes(16)))
                {
                    using (MemoryStream memoryStream = new MemoryStream(encryptedData))
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                        {
                            byte[] plainText = new byte[encryptedData.Length];
                            int decryptedCount = cryptoStream.Read(plainText, 0, plainText.Length);
                            return Encoding.Unicode.GetString(plainText, 0, decryptedCount);
                        }
                    }
                }
            }
            #endregion
        }
    

    您不需要NameSpace

    上面的类完成了加密和解密任何以'?' 字符开头的 URL 参数所需的一切。它甚至可以很好地将参数变量重命名为“enc”,这是一个奖励。

    最后,将类放在您的App_Start 文件夹中,NOT 放在App_Code 文件夹中,因为这会与“明确错误”相冲突。

    完成。

    学分:

    https://www.codeproject.com/questions/1036066/how-to-hide-url-parameter-asp-net-mvc

    https://msdn.microsoft.com/en-us/library/aa719858(v=vs.71).aspx

    HttpModule Init method were not called

    C# Please specify the assembly explicitly in the type name

    https://stackoverflow.com/questions/1391060/httpmodule-with-asp-net-mvc-not- being-called

    【讨论】:

    • 这适用于包含? 的URL。如果没有?,您将如何加密通过的 ID。例如:myDomain/myController/myAction/10 而不是:myDomain/myController/myAction?myID=10
    • 我稍微修改了您的代码,以便在使用默认 MVC 样式传递时允许对 ID 进行加密。如果需要可以添加代码
    • @DNKROZ 要回答您的第一个问题,这正是我使用它的目的,用于参数化 URL 选项,例如 ?CustID=1234
    • 我还注意到用户在使用此解决方案时仍然可以执行查询字符串操作 - 我现在正在修改代码以加密视图中的操作链接以防止这种情况发生
    • 我最初是这样做的并且效果很好(我还检查了/ 之前都是数值),但是我注意到有人可以使用 f12 开发工具来更改视图中的 ID 参数然后在 URL 被加密之前成功地操纵它。因此我不得不放弃上面的解决方案,我现在正在实施另一个涉及扩展ActionLink 的解决方案。我使用codeproject.com/Articles/130588/… 作为参考
    【解决方案4】:

    您可以创建自定义 html 帮助程序来加密查询字符串并使用自定义操作过滤器属性进行解密并取回原始值。您可以在全球范围内实施它,因此不会花费您太多时间。你可以参考这里Url Encryption In Asp.Net MVC。这将帮助您使用自定义助手和自定义操作过滤器属性。

    【讨论】:

      【解决方案5】:

      全局加密所有 url 参数(查询字符串)可能毫无意义。大多数参数是HttpGet使用的显示项。如果所有内容都被加密,那么这不会成为一个非常有用的页面。但是,如果有一些敏感参数只是客户端上的隐藏字段(密钥),最终返回到服务器以识别记录,这可能值得加密。

      考虑这个视图模型:

      public viewModel
      {
          public int key {get;set;}           // Might want to encrypt
          public string FirstName {get;set;}  // Don't want this encrypted
          public string LastName {get;set;}   // Don't want this encrypted
      }
      

      viewModel 被转换为查询字符串,类似于.... appName.com/index?Id=2;FirstName="John";LastName="Doe"

      如果这个 viewModel 作为查询字符串传递,那么加密名字和姓氏的意义何在?

      需要注意的是查询字符串是HttpGet。 HttpPost 使用会话传递值而不是查询字符串。 HttpPost 会话是加密的。但是 httpPost 有开销。因此,如果您的页面确实包含需要显示的敏感数据(可能是用户当前密码),那么请考虑改为使用 HttpPost。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-28
        • 1970-01-01
        • 1970-01-01
        • 2012-07-17
        • 2011-02-25
        • 1970-01-01
        • 2018-01-25
        • 1970-01-01
        相关资源
        最近更新 更多