我正在用毛伊岛编写一个应用程序,其中使用数据库。要连接到数据库,我需要一个 stringConnection,它将包含秘密信息(密码、登录名、端口等)。据我所知,这种秘密信息不能简单地存储在代码文件中。
比如说我现在有的就是这个。
public static class MySqlDatabaseManager
{
private static string _server = "someServer.com";
private static string _port = "1234";
private static string _user = "user1";
private static string _password = "password123";
private static string _database = "database1";
private static string _stringConnection = $"server={_server};port={_port};user={_user};password={_password};database={_database}";
public static User Authorization(string login, string password)
{
...
}
public static bool TestConnection()
{
...
}
}
我把所有的信息都公开在教室里。
问题是——如何安全地存储这些数据?
我尝试使用secrets.json。创建一个类来获取秘密。
public class UserSecretsManager
{
private static IConfigurationRoot _configuration = new ConfigurationBuilder()
.AddUserSecrets<UserSecretsManager>()
.Build();
public static string StringConnection => _configuration["Database:ConnectionString"];
}
但这一切只在我编写代码的设备上运行。例如,如果您在 Android 模拟器上运行该应用程序,一切都会停止工作,并且 StringConnection 将返回一个空字符串。
后来我发现 - UserSecrets 旨在用于在开发过程中存储本地开发人员机密,并且它们不会随应用程序转移到其他设备。
请告诉我如何以及在哪里可以安全地存储这些数据?
只有在用户必须根据应用程序的目的输入秘密数据的情况下,在客户端存储秘密数据才有意义。例如,如果用户输入需要保存以供日后使用的密码、令牌或 API 密钥,则建议在移动设备上使用安全存储。它使用Keychain(iOS/macOS)和Keystore(Android),提供可靠的数据保护。
向安全存储添加机密的示例:
获取秘密的示例:
但是,如果数据库连接字符串需要对于应用程序的所有用户都相同,那么将其存储在客户端上绝对不是一个选择。即使它是加密的,用户仍然能够以某种方式访问它。
关键在于应用程序必须将密钥存储在某处才能解密字符串。如果它存储在代码中,则可以使用逆向工程来提取它。
即使连接字符串在代码中被加密,它也会在运行过程中在RAM中被解密。有许多工具(例如Frida、Xposed)可以让你实时读取应用程序的内存。
如果应用程序直接访问数据库,攻击者可以拦截请求并从数据包中提取登录名/密码。即使使用 TLS,也有可能使用伪造的证书执行MITM(中间人)攻击。
安全方法
建议不要将连接字符串传递给客户端,而是使用中间服务器(API 网关):
如果您想在 .NET 中实现 API 网关,我会推荐 ASP.NET Core Web API 作为最灵活的选项。但是您需要自己实现处理数据库的所有逻辑。
代码示例:
对于授权,我推荐IdentityServer4。确实,它已经过时了,因为从 5.0 版本开始它就变成了付费版本并且不再由社区开发。但它仍然是 OAuth 2.0 和 OpenID Connect 的强大解决方案。我认为它对于宠物项目或初创企业来说仍然足够好,如果有的话,迁移到Duende IdentityServer(IdentityServer5)相对容易。这实际上是 IdentityServer4(相同的开发人员),但具有支持和更新。
IdentityServer 还有一个免费的替代品——OpenIddict ,但我还没有准备好推荐它,因为我没有用过它。