问题描述
我有一个.Net Core REST API。和一个本地移动应用程序(也是SPA)。
我的应用程序没有任何个人用户身份验证。就像免费服务一样。
但是我想确保只有当最终用户试图从本机移动应用程序或SPA访问该服务时,才可以访问该服务。如果将GET / POST链接粘贴到浏览器上,则应该无法访问。
有可能吗?
(如果不是完全安全的解决方案?至少是一个简单的解决方案(无法轻易破解系统)?当前使用简单的API密钥的BTW)
解决方法
谁在请求中,正在做什么?
我的应用程序没有任何个人用户身份验证。就像免费服务一样。
用户身份验证仅标识谁正在向API服务器发出请求,而不标识正在做什么。
这是一个误解,通常我会发现具有任何资历的开发人员,因此,我建议您阅读我写的标题为“ 为什么您的移动应用需要Api密钥”的文章中的this section。 。本节详细说明了谁和什么正在访问您的API服务器之间的区别,但是我将在此处提取其中的主要内容:
什么是向API服务器发出请求的东西。它确实是您的移动应用程序的真正实例,还是机器人,自动脚本还是攻击者使用诸如Postman之类的工具手动在您的API服务器上闲逛?
谁是移动应用程序的用户,我们可以通过多种方式进行身份验证,授权和标识,例如使用OpenID Connect或OAUTH2流。
在简历中,您需要考虑谁,因为您的API服务器将能够对用户进行身份验证和授权访问数据,并考虑什么作为代表用户发出请求的软件。
有可能吗?
但是我想确保只有当最终用户试图从本机移动应用程序或SPA访问该服务时,才可以访问该服务。如果将GET / POST链接粘贴到浏览器上,则应该无法访问。
有可能吗?
“是”和“否” ...好吧,我不喜欢分类使用这些肯定中的任何一种,因为在软件中总是更像it depends!
,因此我将它们用引号引起来;)>
是针对移动应用
对于移动应用程序,您可以使用“移动应用程序证明”概念来高度确定做什么发出的请求确实是您的移动应用程序的原始且不受干扰的版本。关于它的更多信息,我建议您阅读this answer我对问题如何保护移动应用程序的API REST?的问题,特别是保护API服务器的保护和更好的解决方案。
否(针对Web Apps)
对于Web应用程序,攻击者所需要做的只是敲击F12
来检查网页并查看网页上的所有源代码,因此,这里没有直接的解决方案来解决高度了解问题自信做什么正在执行请求,但是您可以使攻击者的生活更加艰难。我建议您阅读my answer,以解决问题从应用程序外的调用中保护api数据,尤其是专门用于防御API服务器的部分。
您想加倍努力吗?
在回答安全问题时,我总是喜欢引用OWASP基金会的出色工作。
对于APIS
OWASP API安全项目旨在通过强调不安全API中的潜在风险并说明如何减轻这些风险来为软件开发人员和安全评估人员提供价值。为了实现此目标,OWASP API安全项目将创建和维护“十大API安全风险”文档,以及用于创建或评估API的最佳实践的文档门户。
对于移动应用
OWASP Mobile Security Project - Top 10 risks
OWASP移动安全项目是一个集中式资源,旨在为开发人员和安全团队提供构建和维护安全移动应用程序所需的资源。通过该项目,我们的目标是对移动安全风险进行分类并提供开发控制措施,以减少其影响或被利用的可能性。
OWASP - Mobile Security Testing Guide:
移动安全测试指南(MSTG)是用于移动应用安全开发,测试和逆向工程的综合手册。
对于Web Apps
The Web Security Testing Guide:
,OWASP Web安全测试指南包括用户可以在自己的组织中实施的“最佳实践”渗透测试框架,以及描述了用于测试最常见的Web应用程序和Web服务安全问题的技术的“低水平”渗透测试指南。
对于我的API,我刚刚进行了常规身份验证检查。我将预设的用户名/密码附加到我的请求标头中,然后对照我的API中的设置进行检查。我只真正关心密码,但是我使用用户名来标识来源。我创建了一个基本控制器,所有API控制器都继承自该基本控制器,因此我可以授权所有请求。 (记录是可选的)
public abstract class BaseController : ApiController
{
protected static readonly Logger _logger = LogManager.GetCurrentClassLogger();
protected bool IsAuthorized()
{
try
{
_logger.Debug("Checking credentials");
var (Username,Password) = Security.ParseCredentials(HttpContext.Current.Request);
if (Password != AppConfig.Instance.Settings["Password"])
{
_logger.Error($"Calling application ({Username}) was not authorized.");
return false;
}
else
{
_logger.Debug($"Calling application {Username} successfully authenticated.");
return true;
}
}
catch (Exception ex)
{
_logger.Error(ex,"An error occured checking authentication");
return false;
}
}
}
然后在调用API的代码中,我将用户名密码编码到了授权属性中。
var client = new HttpClient
{
BaseAddress = new Uri(ConfigurationManager.AppSettings["ReportApiUri"])
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",Convert.ToBase64String(Encoding.UTF8.GetBytes(
$"SourceApplication:{ConfigurationManager.AppSettings["ApiPassword"]}"
)
));
如果从SPA进行呼叫,则可以对值进行预加密并将其传递。我能够使用Postman传递它,因此必须有一种方法可以使用我认为的任何客户端传递它。 / p>
PS-我的基本控制器中的AppConfig对象只是我编写的用于检索设置的类,您可以使用配置管理器,甚至可以对其进行硬编码。.我的意思是,我不建议长期对其进行硬编码只是为了测试一下。
,这是另一个堆栈溢出问题的link(有人回答该问题),有人试图获取客户端IP。