Chromium内核浏览器首页保护逻辑
发布于 2020-09-02
以前Chromium内核浏览器首页的设置是明文存在Preferences文件中的,及其不安全。很多三方程序通过改这个配置文件就可以篡改劫持浏览器的首页了。从某个版本开始,Chromium通过对设置首页的url进行hash,然后读取配置的时候对比hash的值,来判断设置首页的url是否被篡改。如果检测到被篡改,则首页默认打开新标签页,并打开设置页的时候提示用户“部分设置已经重置”,如下图所示:
下面介绍一下chromium内核浏览器首页保护逻辑。
Chromium内核浏览器把首页的配置保存在Preferences或者Secure Preferences文件中。一般用户的首页设置都存在Secure Preferences文件中。但是如果电脑被外部管理,比如说入了域,则保存在Preferences文件中。判断电脑是否入域是通过IsOS(OS_DOMAINMEMBER)
来判断的。
不管存在哪个文件中,在json配置文件的位置不是不变的:
- session.restore_on_startup,明文,打开首页的方式,integer值
- 值为1是恢复上次浏览器会话打开的url
- 值为4是打开session.startup_urls设置里的urls
- 值为5是打开新标签页
- session.startup_urls,是打开特定网页或一组网页的urls
- protection.macs.session.restore_on_startup,是对session.restore_on_startup值的hash结果
- protection.macs.session.startup_urls,是对session.startup_urls值的hash结果
具体的hash算法是,先取得当前电脑的sid,获得代码如下:
MachineIdStatus GetDeterministicMachineSpecificId(std::string* machine_id) {
DCHECK(machine_id);
wchar_t computer_name[MAX_COMPUTERNAME_LENGTH + 1] = {};
DWORD computer_name_size = base::size(computer_name);
if (!::GetComputerNameW(computer_name, &computer_name_size))
return MachineIdStatus::FAILURE;
DWORD sid_size = SECURITY_MAX_SID_SIZE;
char sid_buffer[SECURITY_MAX_SID_SIZE];
SID* sid = reinterpret_cast<SID*>(sid_buffer);
DWORD domain_size = 128; // Will expand below if needed.
std::unique_ptr<wchar_t[]> domain_buffer(new wchar_t[domain_size]);
SID_NAME_USE sid_name_use;
// Although the fifth argument to |LookupAccountNameW()|,
// |ReferencedDomainName|, is annotated as |_Out_opt_|, if a null
// value is passed in, zero is returned and |GetLastError()| will
// return |ERROR_INSUFFICIENT_BUFFER| (assuming that nothing else went
// wrong). In order to ensure that the call to |LookupAccountNameW()|
// has succeeded, it is necessary to include the following logic and
// obtain the domain name.
if (!::LookupAccountNameW(nullptr, computer_name, sid, &sid_size,
domain_buffer.get(), &domain_size, &sid_name_use)) {
// If the initial size of |domain_buffer| was too small, the
// required size is now found in |domain_size|. Resize and try
// again.
if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
return MachineIdStatus::FAILURE;
domain_buffer.reset(new wchar_t[domain_size]);
if (!::LookupAccountNameW(nullptr, computer_name, sid, &sid_size,
domain_buffer.get(), &domain_size,
&sid_name_use)) {
return MachineIdStatus::FAILURE;
}
}
// Ensure that the correct type of SID was obtained. The
// |LookupAccountNameW()| function seems to always return
// |SidTypeDomain| instead of |SidTypeComputer| when the computer name
// is passed in as its second argument and therefore both enum values
// will be considered acceptable. If the computer name and user name
// coincide, |LookupAccountNameW()| seems to always return the machine
// SID and set the returned enum to |SidTypeDomain|.
DCHECK(sid_name_use == SID_NAME_USE::SidTypeComputer ||
sid_name_use == SID_NAME_USE::SidTypeDomain);
char* sid_string = nullptr;
if (!::ConvertSidToStringSidA(sid, &sid_string))
return MachineIdStatus::FAILURE;
*machine_id = sid_string;
::LocalFree(sid_string);
return MachineIdStatus::SUCCESS;
}
这个sid在每台电脑上都不一样,比如我本机的sid是S-1-5-21-180806151-1063684393-2331670580
。后续会使用这个sid去拼接要保护的数据。这样即使同样的设置数据,实际hash的数据每台电脑上都不一样,提高了安全性。
比如要保护session.startup_urls
的配置的url,会被拼接成一个S-1-5-21-180806151-1063684393-2331670580session.startup_urls["https://www.baidu.com/"]
的字符串,然后对这个字符串进行hmac SHA256 哈希。
hmac需要一个key,Chromium内核这个key的值为空,Chrome浏览器这个值如下:
uint8_t k[64] = {0xe7, 0x48, 0xf3, 0x36, 0xd8, 0x5e, 0xa5, 0xf9, 0xdc, 0xdf,
0x25, 0xd8, 0xf3, 0x47, 0xa6, 0x5b, 0x4c, 0xdf, 0x66, 0x76,
0x00, 0xf0, 0x2d, 0xf6, 0x72, 0x4a, 0x2a, 0xf1, 0x8a, 0x21,
0x2d, 0x26, 0xb7, 0x88, 0xa2, 0x50, 0x86, 0x91, 0x0c, 0xf3,
0xa9, 0x03, 0x13, 0x69, 0x68, 0x71, 0xf3, 0xdc, 0x05, 0x82,
0x37, 0x30, 0xc9, 0x1d, 0xf8, 0xba, 0x5c, 0x4f, 0xd9, 0xc8,
0x84, 0xb5, 0x05, 0xa8};
得到的结果是F4471F81BC6DD2D8BC854436188A805A29FAEBF31008446FAF8C9877861BB4A5
。
同样的,session.restore_on_startup
的配置页需要去哈希,然后更新上。