【问题标题】:Ways to secure an anonymous Web API request保护匿名 Web API 请求的方法
【发布时间】:2018-06-04 17:41:26
【问题描述】:

我有一个 ASP.NET MVC(不是 ASP.NET Core)单页应用程序,前端带有 angular js

我的客户端(浏览器)通过 ASP.NET Web API 与服务器通信。现在,Web 应用程序在 https 上,但在 anonymous 上。没有登录/用户身份验证。

网络应用程序采用向导的形式,用户可以在其中来回添加或更新网页上的输入字段。然后通过 Web API 在服务器上更新表单输入值。

我正在寻找一种方法来单独保护我的 Web API 调用,尤其是 POST/PUT 请求。总之,我想阻止任何用户直接从 POSTMAN 或 Fiddler 调用我的 Web API。 Web API 虽然是匿名的,但只能从发起请求的浏览器会话中调用。

  • 我有哪些选择可以实现这一目标?

  • 我可以在此处使用 Anti-Forgery 令牌(无需身份验证)吗?

  • 一种方法,我可以想到实现这一点是为每个请求添加一个自定义标头,并在标头中存储某种会话密钥。然后,验证我从客户端收到的每个请求的自定义标头。是否有其他方法可以在不使用自定义解决方案的情况下实现这个开箱即用或一些经过验证的库?

  • 如果我必须采用上述自定义解决方案,我需要注意哪些陷阱或潜在问题?

【问题讨论】:

  • 我花了 20 分钟来构思这个问题。在问这个问题之前,我深思熟虑。我认为这个问题无论如何都不会太宽泛。它在一分钟左右的时间内被否决。是否有足够的时间来回答这个问题?反对者至少可以评论其中“过于宽泛”的内容。它可以帮助我更好地提出问题。我还需要在此处提供什么信息才能使这个问题变得更好?
  • 由于您通常无法执行此操作,因此最好添加有关您需要此操作的原因以及您尝试保护的威胁的信息,以便我们提出不同的建议。
  • 嗨@Evk,API 端点以javascript 代码提供,任何人都可以查看。我可以缩小 JavaScript,但它仍然只是混淆。由于这些端点对最终用户可见,因此任何人都可以调用端点来添加/更新请求数据。我在表格中获得了用户的一些个人信息,例如他的姓名和年龄。我想阻止用户以外的任何人(在浏览器上)更新此信息。
  • 但是如果您有个人信息 - 为什么不像往常一样使用用户名/密码实施身份验证?
  • 然后当调查开始时(我将以调查为例,但您可以将其应用于您的案例)-您接受用户识别信息(姓名,年龄),将其存储在数据库中,然后附加 cookie响应的随机字符串(并将该随机字符串与用户信息一起存储在数据库中)。然后,此调查\用户的所有后续请求都需要具有相同随机字符串值的 cookie。

标签: c# asp.net asp.net-mvc


【解决方案1】:

首先,当您删除登录并且您的应用程序中没有身份验证机制时,实际上没有办法保护任何东西,因为任何人都可以访问您的 API。我认为您想要的是确保仅从您自己的网站调用您的 API。不幸的是,您无法完全实现这一点,因为您的 Web API 是 http/https,并且任何人,来自任何地方(如邮递员、提琴手等)都可以创建 http 请求并调用您的 API。

您所能做的就是让您的 API 更难响应请求,例如您提到的使用 Anti-Forgery。

我还建议您为您的应用程序添加一个 cookie,并在每个请求中检查该 cookie,在这种情况下,使用 Fiddler 或 Postman 调用您的 API 会更加复杂(并非不可能)。

最后我建议您使用 CORS,这样浏览器将只允许您的域调用您的 API。因此没有人可以在不同域的浏览器中调用您的 API。

【讨论】:

  • 嗨@Arvin,我不确定CORS如何在这里提供帮助。根据我的理解,启用 CORS 将允许用户代理从与当前使用的站点不同的源(域)上的服务器访问选定的资源。不确定如何阻止用户通过 POSTMAN 调用我的 API。
  • 正如我所说,没有什么会阻止用户通过 POSTMAN 调用 API,因为您的 API、角度和邮递员都在使用 http 协议。关于CORS,你是对的。在您的场景中,如果您的 API 和您的 Angular 应用程序位于不同的域中,那么您需要使用 CORS 限制域
  • 感谢您的澄清。在 Anti-Forgery 令牌上,我可以在未经身份验证的网站上使用 Anti-Forgery 令牌吗?
  • 是的防伪与身份验证无关
  • 这就是我的想法。看起来防伪是唯一可能的方法。再次感谢。
【解决方案2】:

根据@Arvin 的回答和@Evk 的评论,我打算这样做:

  • 一旦用户启动anonymous 会话,使用常规Guid.NewGuid() 方法生成一个GUID 并将其保存在数据库中以识别请求(我现在正在这样做)。但是,正如here 所述,

GUID 可以是唯一的,但它们不受加密保护。

  • 因此,不使用纯文本 GUID,而是使用当前时间戳将其加密为 token,并附加请求查询字符串。

  • 对于每个后续的 API 请求,从查询字符串中读取 token,对其进行解密和验证,如下所示:

    • 检查时间戳。如果时间差超过预定义时间(即令牌过期),拒绝请求

    • 针对 DB 验证唯一 ID (GUID)

  • 因为我不再使用纯文本 GUID,所以 URI 不容易猜到。

此外,使用时间戳,URI 会在一段时间后失效。虽然理论上仍然可以通过 Fiddler 调用 API,但这对于攻击者来说应该是非常困难的,如果不是不可能的话。

  • 作为进一步的安全措施,我还可以在请求中添加 Anti-Forgery 令牌

据我了解,这有助于解决我的潜在问题,并且使用这种方法,我什至可能不需要添加 cookie 来保护我的匿名会话。

很高兴听到大家的意见,如果这种方法看起来不错,以及如何改进它。

【讨论】:

  • 您的整体解决方案似乎不错,我想提几点。如果您正在使用加密,那么将您的加密密钥(时间戳)发送给您的客户端并不是一个好主意。我建议您使用Microsoft.Owin.Security 包中的TicketDataFormat 为您的会话生成票证并对其进行加密,您还可以为其添加到期时间,并且您不需要将票证存储在数据库中,提高性能
  • 尽管您甚至无法实现您想要的(任何人都可以轻松地伪装成网络浏览器。)您不必提出自定义令牌验证。您可以使用经批准的方法,例如 JWT。
  • 您的回答完美地描述了我创建的middleware,请参阅my answer
【解决方案3】:

我也曾经奇怪地需要在 WebAPI 上具有会话功能,并创建了一个 OWIN 会话中间件,它完全符合您的目标。

该库名为 OwinSessionMiddleware,可在 githubNuGet 上使用。

用法

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseSessionMiddleware();

        // other middleware registrations...
        app.UseWebApi();
    }
}

您还可以传递一些 options 来进一步调整 cookie 名称,添加数据库支持的会话存储(而不是内存中),添加您自己的会话 id 生成器(而不是基于 GUID + 的默认 id 生成器)由RNGCryptoServiceProvider 生成的安全随机部分)。

唯一的会话 ID 本身存储为安全 cookie,并且会话由中间件在每次请求时自动恢复。

您可以在 API 控制器内部调用 extension methods 来获取和设置会话数据:

public SomeApiController : ApiController
{
    public IHttpActionResult MyAction()
    {
        var requestCount = Request.GetSessionProperty<int>("RequestCount");

        Request.SetSessionProperty("RequestCount", ++requestCount);
    }
}

【讨论】:

  • 不可以使用 Fiddler 发送 Http cookie 吗?
  • 嗨@Wouter,看起来和我要找的差不多。我有一个关于会话管理的问题。 ReadMe.md 文件提到这种方法使用内存中的会话存储。我还没有在我的项目中使用会话,并希望尽可能避免。我有一个负载平衡的环境,要管理服务器上的会话,我需要配置进程外机制。你知道是否可以像 Raven 这样在服务器上存储自定义数据,而不是将其存储在会话中?
  • 更新:我想我得到了我的问题的答案。看起来我可以拥有自己的ISessionStore 实现并将其存储在我想要的任何地方。再次感谢。
  • 嗨@Legend,它是。但是,在安全 (HTTPS) 连接上,拦截器无法读取它。而且由于是通过RNGCryptoServiceProvider生成的,所以不容易猜到。
  • 您可以将负载均衡器配置为根据会话 cookie 锁定会话(通常称为粘性会话)
猜你喜欢
  • 2013-08-08
  • 2013-03-20
  • 1970-01-01
  • 2015-02-03
  • 2020-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多