我做了一些涉及自动提交表单和/或从网站检索数据的项目。其中一些站点需要用户名/密码身份验证。(这些网站没有 API,所以我依靠屏幕抓取。)
我见过的大多数教程都将用户名和密码存储在源代码中,就像任何其他 POST 数据一样,例如:
string username = "someUserName";
string password = "somePassword";
// submit POST data...
但我知道以纯文本形式存储密码通常是不受欢迎的。我应该使用其他方法吗?
我做了一些涉及自动提交表单和/或从网站检索数据的项目。其中一些站点需要用户名/密码身份验证。(这些网站没有 API,所以我依靠屏幕抓取。)
我见过的大多数教程都将用户名和密码存储在源代码中,就像任何其他 POST 数据一样,例如:
string username = "someUserName";
string password = "somePassword";
// submit POST data...
但我知道以纯文本形式存储密码通常是不受欢迎的。我应该使用其他方法吗?
存储密码的常用方法是对其进行哈希处理。由于大多数哈希密码算法都是破坏性的,也就是说它们不能被逆转,这对你不起作用。
一种选择是使用可逆散列,例如对密码进行 base64 编码,但实际上并不比以纯文本形式存储更安全。
据我所知,最好的解决方案是将密码存储在数据库中。如果你真的担心有人得到用户名和密码,你可以在数据库中使用加密函数对其进行加密,或者你可以使用 SQLite 数据库,直接在磁盘上加密。
通过这种方式,您的代码和登录凭据是分开的,您可以安全地与他人共享您的代码,而无需担心安全性。
我们使用的模式是:
在您的数据库表中,您有一个加密列。此列包含使用系统范围的长(128 位)随机密钥(通常存储在配置文件中)加密的数据。此加密列中的数据包含用于每个第三方服务的单独(随机)密钥。使用此密码,我们会加密与此第三方服务相关的身份验证详细信息。
为什么要进行双重加密?
您将纯文本密码的数量减少到一个(系统范围的密码)。因此,密钥管理更容易。我们为每个第三方服务创建一个长的随机密钥,以便我们可以选择性地解密每个第三方服务的凭据,并在必要时在系统之间传输它们。将我们的密钥之一存储在数据库之外还可以降低与 SQL 注入攻击(它们“仅”获取数据库数据)和备份(配置文件不包含在常规备份数据中)相关的风险。
弱点显然是系统范围的密码。它需要在内存中的某个地方。
我不是密码学家,我很确定上述内容不是最佳的。但是,它比仅以纯文本形式存储第三方服务凭证更有效、易于管理且安全得多。
我有一个需要解决这个问题的抓取项目。我的设置包括两个独立的服务器。第一个是用户前端 Web 应用程序。第二个是处理抓取的 nodejs 服务器。
我使用 openssl 密钥对加密处理加密。我为 nodejs 机器生成一个密钥对,并将公钥提供给前端 Web 应用程序。当用户注册他们的第 3 方凭据时,这些凭据会使用公钥加密并存储在数据库中。
Web 应用程序定期选择用户的加密凭据并将其发送到节点服务器,在该节点服务器上使用私钥对其进行解密并与第三方一起用于抓取。
快速搜索后,我发现了这篇关于使用 openssl 和加密字符串的文章。
我意识到这是一个非常古老的帖子,但希望它可以帮助下一个偶然发现这个问题的人。
一种非常简单的加密和解密方法是扩展微型加密算法 (XTEA)。我在这里粘贴来自维基百科的 C++ 代码,但请记住,任何人都可以在那里更改它。
#include <stdint.h>
/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0]=v0; v[1]=v1;
}
没有办法做到这一点。它需要在某个地方作为纯文本(或“可逆加密”)提供给脚本。
许多 API(例如包括 Amazon Web Services)会建议在环境变量中设置凭据,这可能是您所希望的安全性。
把它放在你的 .bash_profile 中,仔细检查权限,至少你可以确定它不会在公共 repo 的 github 上结束。
您必须做两件事:
1. 使用 HTTPS 登录页面(如有必要)
2. 收到密码后立即使用密码加密。编码器是这样的:
private static String passwordEncryption(String oldPass){
String newPass = "";
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(oldPass.getBytes(), 0, oldPass.length());
newPass = new BigInteger(1,messageDigest.digest()).toString(16);
if (newPass.length() < 32) {
newPass = "0" + newPass;
}
return newPass;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return newPass;
}
并使用MySql 的MD5()函数将接收到的密码与存储的密码进行比较。