安全浏览(Safe Browsing)是 Google 的安全团队打造的一项服务,旨在发现网上的不安全网站并将潜在危害告知用户和网站站长,具体介绍可以参考官方网页。简单来说就是 Google 的安全浏览服务会检查数十亿个网址以及相应网页中的软件和内容,发现其中包含病毒、网站被篡改等不安全问题,并在用户访问时发出警告,这是一项很有用的服务,故考虑将其集成到网站外链处理中,当用户点击外链时实时进行检测提醒。
集成使用安全浏览服务可以有两种方式,一种是直接抓取安全浏览检测网页的内容,判断返回的状态:
这种方法可以实现目标,但效率一般,不在本文探讨中;另一种方法是使用 Google 提供的 Safe Browsing API,这种方法相比前一种更为高效,故我们接下来一起来看看如何利用 API 的方式来集成安全浏览服务。
注意,不管是哪种方法,都要求运行此服务的主机能够访问 Google 网站。
注册
注册账号
使用 Safe Browsing API 要求必须要有谷歌(Google)账号,因此在进行下一步前请先注册账号,可以使用自己的邮箱注册,这里就不详细介绍,按页面提示操作即可:
注册 API
打开 Google Developers Console API 库,从项目下拉列表中,选择一个项目或创建一个新项目,由于现在 API 已经集成到 Google Cloud Platform,创建项目前可能需要先绑定有效的付款方式(API 不收费):
在 Google API 标签中,搜索并选择 Safe Browsing API ,注意有两个 API,我们选择新版 v4 的:
然后单击启用 API:
接下来,我们启用凭据:
选择安全浏览 Safe Browsing API,创建:
凭据已创建,记下来备用:
为保证凭据安全,建议进行使用限制,比如来源页、允许 IP 等:
到这里 API 凭据 KEY 已经成功获取。
使用
CURL 及参数解析
获得 KEY 后我们可以先测试一下效果:
$ curl -X POST \
'https://safebrowsing.googleapis.com/v4/threatMatches:find?key=YOUR_KEY_HERE' \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d ' {
"client": {
"clientId": "vcloud",
"clientVersion": "1.1.1"
},
"threatInfo": {
"threatTypes": ["MALWARE", "SOCIAL_ENGINEERING"],
"platformTypes": ["WINDOWS"],
"threatEntryTypes": ["URL"],
"threatEntries": [
{"url": "https://accounts-wallets.redirectme.net/webapps/57a39/websrc"},
{"url": "http://stackoverflow.com/"}
]
}
}'
ShCopy
得到了如下响应:
{
"matches": [
{
"threatType": "SOCIAL_ENGINEERING",
"platformType": "WINDOWS",
"threat": {
"url": "https://accounts-wallets.redirectme.net/webapps/57a39/websrc"
},
"cacheDuration": "300s",
"threatEntryType": "URL"
}
]
}
ShCopy
我们提交了两条 URL 检测,但只回复了一条,说明如果网址是安全的,那么回复的内容为空,后续我们就可以以此来判断是否安全。
我们看一下请求的 JSON 参数:
{
"client": {
"clientId": "vcloud",
"clientVersion": "1.1.1"
},
"threatInfo": {
"threatTypes": ["MALWARE", "SOCIAL_ENGINEERING"],
"platformTypes": ["WINDOWS"],
"threatEntryTypes": ["URL"],
"threatEntries": [
{"url": "https://accounts-wallets.redirectme.net/webapps/57a39/websrc"},
{"url": "http://stackoverflow.com/"}
]
}
}
JSONCopy
client
表示与安全浏览 API 请求关联的客户端元数据,可以根据自己需求实际设置:
clientId
表示使用 Safe Browsing API 的客户端唯一标识 ID,clientVersion
表示客户端版本。
threatInfo
表示要进行安全检测匹配的内容:
threatTypes:安全问题类型,主要有:
THREAT_TYPE_UNSPECIFIED | 未知的威胁类型 |
MALWARE | 恶意软件威胁类型 |
SOCIAL_ENGINEERING | 社会工程威胁类型 |
UNWANTED_SOFTWARE | 不需要的软件威胁类型 |
POTENTIALLY_HARMFUL_APPLICATION | 潜在有害的应用程序威胁类型 |
这也印证刚说的,如果检测是安全的,不会直接返回安全,而是不回复任何内容,凡有回复内容皆是存在安全风险。
platformTypes:安全问题平台,主要有:
PLATFORM_TYPE_UNSPECIFIED | 未知的平台 |
WINDOWS | 对 Windows 构成威胁 |
LINUX | 对 Linux 构成威胁 |
ANDROID | 对 Android 构成威胁 |
OSX | 对 OS X 构成威胁 |
IOS | 对 iOS 构成了威胁 |
ANY_PLATFORM | 对至少一个已定义的平台构成威胁 |
ALL_PLATFORMS | 对所有已定义的平台构成威胁 |
CHROME | 对 Chrome 构成威胁 |
threatEntryType:构成威胁的类型,区别于 threatTypes 和 platformTypes,一次只能指定一个:
THREAT_ENTRY_TYPE_UNSPECIFIED | 未指定 |
URL | 一个URL |
EXECUTABLE | 可执行程序 |
threatEntries(threatEntry):需要检测的目标,可以是 URL,也可以是哈希值,但一个JSON 中只能有一个(可以有多个 JSON):
hash | 二进制格式,对于 JSON 请求,哈希值由 base64 编码 |
url | 完整的 URL(无需编码) |
digest | 二进制或十六进制,针对可执行文件,对于 JSON 请求,哈希值由 base64 编码 |
再来看一下回复的 ThreatMatch JSON 参数:
{
"matches": [
{
"threatType": "SOCIAL_ENGINEERING",
"platformType": "WINDOWS",
"threat": {
"url": "https://accounts-wallets.redirectme.net/webapps/57a39/websrc"
},
"cacheDuration": "300s",
"threatEntryType": "URL"
}
]
}
JSONCopy
其中 threatType
、platformType
在请求 JSON 已经讲过了;threat
表示匹配到的请求 JSON 中的 threatEntries
中的有安全风险的条目;cacheDuration
表示该匹配的缓存生存期,客户端若有缓存安全警报,则缓存时间不应超过此时间以避免误报;threatEntryType
则表示检测的 threat
的类型。
之所以将 JSON 参数讲解这么详细,是因为我们可以根据需要指定检测特定的安全类型、平台、URL 等等,以提高效率。
Python 示例
import requests
url = "https://safebrowsing.googleapis.com/v4/threatMatches:find"
querystring = {"key":"YOUR_KEY"}
payload = """
{
"client": {
"clientId": "yourcompanyname",
"clientVersion": "1.5.2"
},
"threatInfo": {
"threatTypes": ["MALWARE", "SOCIAL_ENGINEERING"],
"platformTypes": ["WINDOWS"],
"threatEntryTypes": ["URL"],
"threatEntries": [
{"url": "https://accounts-wallets.redirectme.net/webapps/57a39/websrc"},
{"url": "http://stackoverflow.com/"}
]
}
}
"""
headers = {
'content-type': "application/json",
'cache-control': "no-cache",
'postman-token': "460d6d44-4a55-d6ab-fb58-2ff9fb306154"
}
response = requests.request("POST", url, data=payload, headers=headers, params=querystring)
print(response.text)
PythonCopy
PHP 示例
<?php
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=YOUR_KEY",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => " {
"client": {
"clientId": "yourcompanyname",
"clientVersion": "1.5.2"
},
"threatInfo": {
"threatTypes": ["MALWARE", "SOCIAL_ENGINEERING"],
"platformTypes": ["WINDOWS"],
"threatEntryTypes": ["URL"],
"threatEntries": [
{"url": "https://accounts-wallets.redirectme.net/webapps/57a39/websrc"},
{"url": "http://stackoverflow.com/"}
]
}
}",
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache",
"content-type: application/json",
"postman-token: b05b8d34-85f2-49cf-0f8e-03686a71e4e9"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
?>
PHPCopy
Typecho
参照本站代码:
function.php(供参考,不要直接照抄):
......
function safeCheck($url){
if(!isset($url) || empty($url)){
return 'ERROR: URL is required.';
}
if(!isset(Helper::options()->safeapi) || empty(Helper::options()->safeapi)){
return 'ERROR: API Key is required.';
} else {
$apiKey = Helper::options()->safeapi;
}
if(!isset($_SERVER['HTTP_USER_AGENT']) || empty($_SERVER['HTTP_USER_AGENT'])){
return 'ERROR: Unknown platform.';
} else {
$os = getOS($_SERVER['HTTP_USER_AGENT'], 1);
if(strpos($os,'Windows') === true) {
$platform = 'WINDOWS';
} elseif(strpos($os,'Linux') === true) {
$platform = 'LINUX';
} elseif(strpos($os,'Android') === true) {
$platform = 'ANDROID';
} else {
$platform = 'IOS';
}
}
$apiUrl = 'https://safebrowsing.googleapis.com/v4/threatMatches:find?key='.$apiKey;
$params = [
'client' => [
'clientId' => 'armxmod',
'clientVersion' => '7.2.0'
],
'threatInfo' => [
'threatTypes' =>['MALWARE', 'SOCIAL_ENGINEERING'],
'platformTypes' => $platform,
'threatEntryTypes' => ['URL'],
'threatEntries' => [
[ 'url' => $url ]
]
]
];
$curl = curl_init($apiUrl);
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 3,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($params),
CURLOPT_HTTPHEADER => array(
'cache-control: no-cache',
'content-type: application/json'
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
return 'ERROR: ' . $err;
} else {
$res = json_decode($response,true);
if ($res['error']['message']){
return $res['error']['message'];
} else if($res['matches']['threatType']){
return $res['matches']['threatType'];
} else {
return 'safe';
}
}
}
......
PHPCopy
在需要检测的地方,使用如下代码即可:
......
<?php echo '安全检测结果:'.safeCheck($url);?>
......
PHPCopy
注意
Safe Broswing API 是有调用限制的,所以要注意 KEY 的调用限制:
Requests per day(每天):10,000 次
Requests per 100 seconds per user(100 秒内):3,000 次
另外,API KEY 似乎只有 24 小时有效期(测试中出现了 API key expired. Please renew the API key
错误),暂时没看到哪里可以设置永久的,知道的朋友可以告知一下,感谢。