在没有 HTTPS 的情况下保护 REST API

信息安全 验证 休息 重放检测
2021-08-24 18:27:43

我正在开发 REST API,但如果不使用自签名证书,我将无法使用 HTTPS 我知道这对某些人来说可能是可以接受的,但我不希望在客户端浏览器上弹出安全消息。

在网络上传递的信息不敏感,所以我不关心窃听,但我想确保只有授权的客户端才能发出请求并防止重放攻击。这是我的想法:

用户注册到运行服务的 Web 应用程序。他们想注册一个 API 客户端,但他们不是开发人员,因此他们下载了提供的客户端应用程序(在本例中,他们将与计算机一起使用的 IC 卡读卡器,该计算机可以将从扫描卡读取的数据传送到 Web 服务通过 API)。

第一步:API 客户端应用程序生成一个密钥对(例如 OpenSSL)并将公钥发送到服务器(当用户向服务器注册客户端时,这可以是手动上传步骤)。服务器将公钥存储在与用户相关联的数据库中以及客户端的描述(例如“PC01 上的IC 卡读卡器”)中。

为什么要使用密钥对(如 SSH)?我觉得共享秘密很容易被窃听,因为它必须至少通过网络传输一次。虽然可以通过在服务器终端上手动输入密钥而不是通过网络传输密钥来防止这种情况,但系统的一个设计目标是允许我自己以外的用户注册客户端并使用 Web 界面获取 API 访问令牌应用。在这种情况下,窃听者可以截获共享密钥,而客户端生成的密钥对会阻止这种情况。

第二步:当发出请求时,客户端首先从服务器获取一个随机数(将它存储在随机数列表或数据库中并发布时间戳),然后在使用他们的私钥签署请求时发出包括随机数的请求。服务器在执行请求之前验证消息是真实的(由客户端签名,没有其他人签名)。

我使用这种方法的目标是确保只有有效的客户端才能访问 API,同时还可以防止重放攻击。攻击者应该能够看到消息的内容(不是很敏感),但应该无法修改它们。客户端并不真正关心来自服务器的数据是否真实(因为在这种情况下,客户端正在更改服务器的状态,而不是相反)。

  • 这种方法是否满足这些设计目标?
  • 它是否可以防止中间人攻击(假设客户端的原始注册是使用真实服务器而不是攻击者的假服务器)?
4个回答

我使用这种方法的目标是确保只有有效的客户端才能访问 API,同时还可以防止重放攻击。

使用不断增加的 nonce 值。

攻击者应该能够看到消息的内容(不是很敏感),但应该无法修改它们。

有一个 API 密钥和秘密。

您的请求包含明文形式的密钥。

它们还包括一个“哈希”值。此哈希值是 API 机密、不断增加的随机数值和请求本身的数据串联的哈希值(例如 sha256)。服务器生成自己的哈希,并将其与您的客户端发送的哈希进行比较。

攻击者可以读取您的 API 请求,但由于他没有您的 API 机密,因此他将无法制作新的“哈希”值并发送他自己的任意数据。由于您的 nonce 值,他也将无法重播旧的 API 调用。

请注意,HTTPS(没有客户端身份验证)实际上不会帮助您实现以下目标:

我使用这种方法的目标是确保只有有效的客户端才能访问 API,同时还可以防止重放攻击。

HTTPS(没有客户端身份验证)将阻止您的客户端与冒充您的服务器的人交谈;它不会阻止冒充客户端的人与真实服务器交谈。

因此,即使您设法获得了适当的 TLS 证书,您仍然需要采取一些安全措施来实现既定目标。

这种方法是否满足这些设计目标?

我会说确实如此,前提是:

  • 服务器正确地强制 nonce 唯一性并防止 nonce 重用;
  • 客户端在响应中包含该 nonce 并签署完整的响应,包括 URI、标头、有效负载。

您可以使用非对称签名(如您所建议的那样)或对称签名(例如 HMAC)。如果您可以保证初始交换(公钥或共享秘密上传)通过安全通道进行,这两种方法都应该有效。Amazon更喜欢 HMAC,而且这个库支持 RSA、DSA 和 HMAC。

它是否可以防止中间人攻击(假设客户端的原始注册是使用真实服务器而不是攻击者的假服务器)?

这取决于。攻击者仍然能够冒充服务器并向客户端提供恶意数据(因为客户端不会以任何方式验证服务器 - 这是 HTTP 可以提供帮助的部分)。

同时,如果您可以确保身份验证数据的初始交换(在 RSA 的情况下为公钥,在 HMAC 的情况下为共享密钥)是通过安全通道完成的,那么服务器应该能够检测并拒绝任何被篡改的响应(即具有无效随机数或签名的),这提供了对 MITM 有抵抗力的客户端身份验证。

这部分应该非常仔细地评估协议流(谁发起请求,什么是数据流等),因为客户端无法验证服务器。

不,它不能防止 MITM 攻击。如果没有服务器的身份验证,您就无法对客户端进行身份验证。客户端有保证它甚至与您的服务器交谈而您的服务器无法保证它实际上正在与客户端交谈的原因。

在不验证服务器的情况下:

客户端 <--------> 服务器

与此相同:

客户端 <-----> MITM <--------> 服务器

MITM 可以修改从服务器发送到客户端或从客户端发送到服务器的任何内容。客户端只能看到 MITM 希望它看到的内容,并且唯一发送到服务器的请求是 MITM 允许的请求。

因此,在您的示例客户端请求随机数,服务器发送随机数(MITM 制作副本)。客户端创建一个请求,MITM 拦截它并将其修改为他想要的任何内容并将其中继到服务器。服务器认为请求是真实的。

现在,如果您有某种方法可以使用带外通信向客户端提供 id(指纹或公钥),则现在“可以”使用自签名证书。通过这种方式,客户端可以通过确保服务器 hello(SSL 握手的一部分)与预期服务器证书的预共享“Id”匹配,安全地与服务器建立加密和经过身份验证的通信通道。

这可能适用于您的场景,但在大多数情况下,它会导致鸡和蛋的情况。如果我可以安全地向客户端获取服务器证书的有效 ID,则通信是安全的,但要做到这一点,我需要某种安全通信。CA 的重点是引导该过程。

不知道为什么 OP 不能允许端口访问 443 而是 80。在这种情况下,您可以为任何与 HTTPS 兼容的端口设置入站规则(任何 TCP/IP 都是 ..)。只需设置配置的端口并设置 HTTPS。

此外,对于 API,您可以查看 OWASP 的一整套 API 安全性。这是一张供您防御的备忘单:

https://www.owasp.org/index.php/REST_Security_Cheat_Sheet

您还可以通过实施以下方式选择实施现成的良好安全措施:

https://www.owasp.org/index.php/ESAPI_Secure_Coding_Guideline

所有指南均由 OWASP 社区维护。不确定您的 TLS 证书是否存在设置问题,如果是,请联系您的供应商以获得此支持。