每个已发布的浏览器都有一些保存密码口令的管理系统,对于Google的。我筛选了Chrome的源代码,了解了Chrome是如。如果增加一个新的密码口令或更新修改已存在的密码,DCHECK_EQ和。当然,密码是不会储存在纯文本中,
每个已发布的浏览器都有一些保存密码口令的管理系统,对于Google的Chrome浏览器来说也不例外。我筛选了Chrome的源代码,了解了Chrome是如何保存口令的。这里,我将讲解下Google的Chrome浏览器是如何快速安全保存你的密码的。
在点击密码保存时,首先会出现下面的对话框
在Chrome里,上面的对话框称为密码管理器。当你点击Savepassword时会出现什么情形呢?
当点击保存按钮时,调用如下函数。
void PasswordManager::SavePasswordBar::OKButtonPressed() {
form_manager_->Save();
BeginClose();
}
form_manager是另外一个对象,下面代码才真正实现了保存功能。
void PasswordFormManager::Save() {
DCHECK_EQ(state_, POST_MATCHING_PHASE);
DCHECK(!profile_->IsOffTheRecord());
if (IsNewLogin())
SaveAsNewLogin();
else
UpdateLogin();
}
如果增加一个新的密码口令或更新修改已存在的密码,DCHECK_EQ和DCHECK两个对象会对此进行检查。让我们来看一下增加新密码的代码。
void PasswordFormManager::SaveAsNewLogin() {
DCHECK_EQ(state_, POST_MATCHING_PHASE);
DCHECK(IsNewLogin());
// The new_form is being used to sign in, so it is preferred.
DCHECK(pending_credentials_.preferred);
// new_form contains the same basic data as observed_form_
(because its the
// same form), but with the newly added credentials.
DCHECK(!profile_->IsOffTheRecord());
WebDataService* web_data_service =
profile_->GetWebDataService(Profile::IMPLICIT_ACCESS);
if (!web_data_service) {
NOTREACHED();
return;
}
pending_credentials_.date_created = Time::Now();
web_data_service->AddLogin(pending_credentials_);
}
大部分的函数是可调试代码。我们关心的是调用的AddLogin函数。WebDataService对象负责将meta数据和web网页进行关联。
void WebDataService::AddLogin(const PasswordForm& form) {
GenericRequest<PasswordForm>* request =
new GenericRequest<PasswordForm>
(this, GetNextRequestHandle(), NULL, form);
RegisterRequest(request);
ScheduleTask(NewRunnableMethod
(this, &WebDataService::AddLoginImpl, request));
}
到这里有一点点复杂了。新增一个密码,是通过异步和处理调度函数实现。它似乎是非常重要的,没有干扰Chrome的用户界面--仍然保持着快速反应。当执行代码时,让我们考虑一下会发生什么事。
void WebDataService::AddLoginImpl(
GenericRequest<PasswordForm>* request) {
if (db_ && !request->IsCancelled()) {
if (db_->AddLogin(request->GetArgument()))
ScheduleCommit();
}
request->RequestComplete();
}
AddLogin在这里是很重要的,所以让我们继续研究它。
bool WebDatabase::AddLogin(const PasswordForm& form) {
SQLStatement s;
std::string encrypted_password;
if (s.prepare(db_,
"INSERT OR REPLACE INTO logins "
"(origin_url, action_url, username_element, username_value, "
" password_element, password_value, submit_element, "
" signon_realm, ssl_valid, preferred, date_created, "
" blacklisted_by_user, scheme) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") !=
SQLITE_OK) {
NOTREACHED() <<"Statement prepare failed";
return false;
}
s.bind_string(0, form.origin.spec());
s.bind_string(1, form.action.spec());
s.bind_wstring(2, form.username_element);
s.bind_wstring(3, form.username_value);
s.bind_wstring(4, form.password_element);
Encryptor::EncryptWideString(form.password_value,
&encrypted_password);
s.bind_blob(5, encrypted_password.data(), static_cast<int>(encrypted_password.length()));
s.bind_wstring(6, form.submit_element);
s.bind_string(7, form.signon_realm);
s.bind_int(8, form.ssl_valid);
s.bind_int(9, form.preferred);
s.bind_int64(10, form.date_created.ToTimeT());
s.bind_int(11, form.blacklisted_by_user);
s.bind_int(12, form.scheme);
if (s.step() != SQLITE_DONE) {
NOTREACHED();
return false;
}
return true;
}
终于完成了。这个功能其实是在Chrome的SQLite数据库中,通过建立SQL语句来添加一个新的密码。当然,密码是不会储存在纯文本中,因为Chrome有一个encryptor对象,负责加密密码口令。让我们来看一下。
bool Encryptor::EncryptString(const std::string& plaintext, std::string* ciphertext)
{
DATA_BLOB input;
input.pbData = const_cast<BYTE*>( reinterpret_cast<const BYTE*>(plaintext.data()));
input.cbData = static_cast<DWORD>(plaintext.length());
DATA_BLOB output;
BOOL result = CryptProtectData(&input, L"", NULL, NULL, NULL, 0, &output);
if (!result)
return false;
// this does a copy
ciphertext->assign(reinterpret_cast<std::string::value_type*>
(output.pbData), output.cbData);
LocalFree(output.pbData);
return true;
}
重要的一块是CryptProtectData,它是一个Windows API加密函数。使用它加密后数据是相当坚固的。
通过了解,我们学习了Chrome的密码口令管理系统。Google使用SQLite作为密码和其他网页相关数据的存储机制。唯一的Windows特定代码来实现加密的功能,它可以很容易被移植,为每个操作系统创造不同的encryptor对象。