"Safely" 存储(在客户端)令牌以供重用?
"Safely" store (on client side) tokens for reuse?
我正在尝试创建一个桌面应用程序,它将更新发送到我正在开发的网络服务。
理想情况下,应用程序只需配置一次并部署到网络共享。配置应用程序时,用户将输入用于在 Web 服务中进行身份验证的密码。之后,将创建一个令牌以用于将来的连接。
这将允许任何有权访问网络共享的计算机(即使它不是配置该应用程序的计算机)仅运行该应用程序(它将连接到网络服务)无需输入任何凭据(因为令牌已保存)。
问题是:我应该如何保护这个令牌?
- 我知道在客户端存储它永远不会完全安全,但我想让某人尽可能难以访问明文令牌。
- 我正在寻找一个最好不依赖于任何操作系统资源的答案(因为应用程序可以从不同的设备执行)。
- 假设我可以完全控制应用程序和网络服务器
- 我正在使用 C# 开发控制台应用程序,但我认为这更像是一个理论问题(与任何特定语言无关)
以下是我 tried/thought 关于的一些事情:
使用 C# 之类的东西序列化令牌 SecureString
并将其存储在文件中:这是我使用过的最好的方法。但很明显,如果有人获得了密钥文件的访问权限,则逆转非常简单。
This answer suggests to use the Windows Data Protection API (DPAPI) (in C#, the ProtectedData
class), 但显然,这将只允许最初保存凭据的用户访问它们,这是行不通的,因为我必须访问来自多个 users/devices.
的保护数据
将令牌作为参数传递给应用程序:这只是改变了我要存储的地方令牌(例如,在批处理文件或调用程序的 OS 任务上),但我认为它不会使它更安全。
我建议使用 JWT。
您的服务器会在认证成功后生成一个Token。令牌将被发送到客户端。对服务器的每个后续调用都会将 header 中的令牌发送到服务器。然后服务器将验证令牌。如果通过验证,则服务器知道客户端已通过身份验证。
无需在客户端存储usernames/passwords。
没有解决这个问题的万无一失的方法,客户端最终需要访问服务器,因此攻击者可以在客户端上获得足够的权限。
您描述的是 OAuth2 协议的典型场景,因为您可以控制服务器。使用 OAuth2,您无法避免在客户端存储秘密的需要,但通过将其交换为令牌,您可以保护原始密码,并且可以保证令牌非常强大(用户密码往往很弱)。
要将 token/password 存储在客户端上,可以在存储前对其进行加密,但这立即引发了将此加密的密钥存储在何处的问题。攻击者可以从客户端提取它。有一些方法可以提高密钥存储的安全性。
- 根据您的情况,您可以考虑使用 Hardware security module (HSM)。
- 您可以使用 OS 特定的密钥库,就像您在数据保护中提到的那样 - api。密钥库只能帮助保护密钥,因为它有操作系统的支持(我想这就是你所说的 SO-independend?)。使用 DPAPI,您不仅可以限制对已登录用户的访问,还可以将其限制在本地计算机上。密钥库在其他操作系统上也可用。
- 可以在客户端每次启动时向用户请求密码,而不是将密码存储在客户端上。这可以减少到设备的启动,然后密码可以单独保存在内存中。
好吧,你不能保护客户端的任何东西。一旦它在那里,任何有特权访问的人都可以看到它。
你能做的就是让这条信息在没有其他东西的情况下变得毫无用处。
最好的方法是在客户端存储一种 public 密钥 并使用此 public 密钥 创建哈希以通过网络服务对用户进行身份验证。
当用户配置应用程序时,服务器发送应用程序在本地存储的这个 public 密钥。当应用程序调用服务器时,它将使用此 public 密钥和只有应用程序和服务器知道的私钥创建一个散列。然后服务器可以使用它的私钥检查哈希值是否正确。
为了提高安全性,您也可以在哈希中使用时间戳,这样它会超时更改并防止重复使用密钥。或者它可以发送一个新的 public key 和每个网络服务的答案。
我建议您使用 IdentityServer4,因为它提供符合 RFC 的协议,并且根据 GrantType,客户端应用程序在您的桌面应用程序上下文中给出。
即使令牌是明文形式,它在 Web 服务中也受到保护,其中访问控制(令牌的颁发者)通过检查客户端请求中的来源和存储的来源来验证是否从正确的客户端接收到令牌在数据库中。
由于用户不是机器的管理员(这是一个基本假设),有很多方法可以对她隐瞒事情。
我的建议是:
- 确保主应用程序在与主应用程序不同的凭据下运行
登录用户,"special user".
- 编写另一个终端用户应用程序,仅用于设置,与此应用程序对话(使用您认为合适的任何进程间通信,TCPIP,无论如何,也许是安全的,但我不会太在意这个)。此应用仅用于收集凭据并将其发送到第一个应用
- 现在,主应用程序可以在登录用户无法访问的任何地方写入令牌,但我建议使用受保护的数据,因为它非常易于使用
这里有一些图形解释:
由于使用受保护数据 (Windows Data Protection) 加密的数据只能由加密它的 Windows 用户解密,登录用户将无法读取 "special user"数据。
我正在尝试创建一个桌面应用程序,它将更新发送到我正在开发的网络服务。
理想情况下,应用程序只需配置一次并部署到网络共享。配置应用程序时,用户将输入用于在 Web 服务中进行身份验证的密码。之后,将创建一个令牌以用于将来的连接。
这将允许任何有权访问网络共享的计算机(即使它不是配置该应用程序的计算机)仅运行该应用程序(它将连接到网络服务)无需输入任何凭据(因为令牌已保存)。
问题是:我应该如何保护这个令牌?
- 我知道在客户端存储它永远不会完全安全,但我想让某人尽可能难以访问明文令牌。
- 我正在寻找一个最好不依赖于任何操作系统资源的答案(因为应用程序可以从不同的设备执行)。
- 假设我可以完全控制应用程序和网络服务器
- 我正在使用 C# 开发控制台应用程序,但我认为这更像是一个理论问题(与任何特定语言无关)
以下是我 tried/thought 关于的一些事情:
使用 C# 之类的东西序列化令牌
SecureString
并将其存储在文件中:这是我使用过的最好的方法。但很明显,如果有人获得了密钥文件的访问权限,则逆转非常简单。This answer suggests to use the Windows Data Protection API (DPAPI) (in C#, the
ProtectedData
class), 但显然,这将只允许最初保存凭据的用户访问它们,这是行不通的,因为我必须访问来自多个 users/devices. 的保护数据
将令牌作为参数传递给应用程序:这只是改变了我要存储的地方令牌(例如,在批处理文件或调用程序的 OS 任务上),但我认为它不会使它更安全。
我建议使用 JWT。
您的服务器会在认证成功后生成一个Token。令牌将被发送到客户端。对服务器的每个后续调用都会将 header 中的令牌发送到服务器。然后服务器将验证令牌。如果通过验证,则服务器知道客户端已通过身份验证。
无需在客户端存储usernames/passwords。
没有解决这个问题的万无一失的方法,客户端最终需要访问服务器,因此攻击者可以在客户端上获得足够的权限。
您描述的是 OAuth2 协议的典型场景,因为您可以控制服务器。使用 OAuth2,您无法避免在客户端存储秘密的需要,但通过将其交换为令牌,您可以保护原始密码,并且可以保证令牌非常强大(用户密码往往很弱)。
要将 token/password 存储在客户端上,可以在存储前对其进行加密,但这立即引发了将此加密的密钥存储在何处的问题。攻击者可以从客户端提取它。有一些方法可以提高密钥存储的安全性。
- 根据您的情况,您可以考虑使用 Hardware security module (HSM)。
- 您可以使用 OS 特定的密钥库,就像您在数据保护中提到的那样 - api。密钥库只能帮助保护密钥,因为它有操作系统的支持(我想这就是你所说的 SO-independend?)。使用 DPAPI,您不仅可以限制对已登录用户的访问,还可以将其限制在本地计算机上。密钥库在其他操作系统上也可用。
- 可以在客户端每次启动时向用户请求密码,而不是将密码存储在客户端上。这可以减少到设备的启动,然后密码可以单独保存在内存中。
好吧,你不能保护客户端的任何东西。一旦它在那里,任何有特权访问的人都可以看到它。
你能做的就是让这条信息在没有其他东西的情况下变得毫无用处。
最好的方法是在客户端存储一种 public 密钥 并使用此 public 密钥 创建哈希以通过网络服务对用户进行身份验证。
当用户配置应用程序时,服务器发送应用程序在本地存储的这个 public 密钥。当应用程序调用服务器时,它将使用此 public 密钥和只有应用程序和服务器知道的私钥创建一个散列。然后服务器可以使用它的私钥检查哈希值是否正确。
为了提高安全性,您也可以在哈希中使用时间戳,这样它会超时更改并防止重复使用密钥。或者它可以发送一个新的 public key 和每个网络服务的答案。
我建议您使用 IdentityServer4,因为它提供符合 RFC 的协议,并且根据 GrantType,客户端应用程序在您的桌面应用程序上下文中给出。
即使令牌是明文形式,它在 Web 服务中也受到保护,其中访问控制(令牌的颁发者)通过检查客户端请求中的来源和存储的来源来验证是否从正确的客户端接收到令牌在数据库中。
由于用户不是机器的管理员(这是一个基本假设),有很多方法可以对她隐瞒事情。
我的建议是:
- 确保主应用程序在与主应用程序不同的凭据下运行 登录用户,"special user".
- 编写另一个终端用户应用程序,仅用于设置,与此应用程序对话(使用您认为合适的任何进程间通信,TCPIP,无论如何,也许是安全的,但我不会太在意这个)。此应用仅用于收集凭据并将其发送到第一个应用
- 现在,主应用程序可以在登录用户无法访问的任何地方写入令牌,但我建议使用受保护的数据,因为它非常易于使用
这里有一些图形解释:
由于使用受保护数据 (Windows Data Protection) 加密的数据只能由加密它的 Windows 用户解密,登录用户将无法读取 "special user"数据。