我有一个 API 密钥,用于检索浏览器上显示的信息。我遇到的问题是,使用 Chrome - Inspect Element - Source,可以查看 API 密钥。代码如下所示:
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<link href="/styles.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
//<![CDATA[
var dbtKey = "<?php echo $apiKey; ?>";
$apiKey
是一个位于我的config.php文件中的var:$apiKey = 'my key'
有什么办法可以阻止显示此内容吗?
您需要通过 PHP 处理所有请求并将结果作为 JSON 返回,您可以在客户端进行处理。
为了获得额外的安全性,请使用 SSL,这将有助于抵御外部威胁,但无助于抵御内部威胁。
混淆是 JavaScript 中唯一可以实现的事情。请注意,这并不能真正防止数据被抓取,它只会让它变得非常烦人。我使用的 .js 文件实际上是一个 PHP 脚本,步骤如下:
referer
和 sec-fetch-*
标头(并且如果标头对于任何和所有合法用途都保证)。$aesKey
) 和 IV ($aesIV
),并使用原始输出加密您的 API 密钥。将标签 ($aesTag
) 附加到密钥末尾,然后对其进行 Base64 编码 ($aesOut
)。附加标签是因为 Subtle Crypto for JS 期望它位于加密数据的末尾。$aesKey
分成小块,长度不要超过strlen($aesIV)
。$aesKey
生成小写字符的随机字符串,并为 $aesIV
生成一个随机字符串。 ($rndStr1
,2
,...,$rndStrIV
)。我在自己的实现中使用了 12 到 16 之间的随机长度。Server-Timing
标头,其中包含:
“$rndStrIV
;desc=base64_encode($aesIV)
;dur=strlen($aesTag)
,$rndStr1
;描述=base64_encode($aesKey1)
,$rndStr2
;描述=base64_encode($aesKey2)
,$rndStr3
;描述=base64_encode($aesKey3)
"ArrayBuffer
,另一个用于扫描 performance.getEntriesByType()
寻找 serverTiming
对象。然后,您只需从 serverTiming
数据中提取密钥、IV 和标签长度,使用 PHP 插入要查找的名称($rndStrIV
、$rndStr1
、2
、...)。$aesData
回显到脚本中作为某处的变量。Server-Timing
中。不过,请记住您的网络服务器和目标浏览器的标头长度限制。总的来说,这应该会使抓取过程变得更加复杂和复杂。
<?php
$addr = 'https://my.public.svc/tool/';
$secret = 'ACCESS_TOKEN';
$alg = 'aes-256-gcm';
$jalg = 'AES-GCM';
if (substr($_SERVER['HTTP_REFERER'], 0, strlen($addr)) !== $addr)
die('window.close();');
if ($_SERVER['HTTP_SEC_FETCH_DEST'] !== 'script')
die('window.close();');
if ($_SERVER['HTTP_SEC_FETCH_MODE'] !== 'no-cors')
die('window.close();');
if ($_SERVER['HTTP_SEC_FETCH_SITE'] !== 'same-origin')
die('window.close();');
function rndChrs() {
$l = random_int(12, 16);
$r = '';
for ($i = 0; $i < $l; $i++) {
$r.= chr(random_int(0x61, 0x7A));
}
return $r;
}
function splitKey($d, $l) {
$c = ceil(strlen($d) / $l);
$n = array();
$t = array();
for ($i = 0; $i < $c; $i++) {
$k = rndChrs();
$n[] = $k;
$v = substr($d, $i * $l, $l);
$t[] = $k.';desc="'.base64_encode($v).'"';
}
return [$n, $t];
}
function makeTime($t, $l) {
$w = random_int(0, count($t) - 1);
shuffle($t);
$r = '';
for ($i = 0; $i < count($t); $i++) {
if ($r !== '')
$r.= ',';
$r.=$t[$i];
if ($i === $w)
$r.= ';dur='.$l;
}
return $r;
}
function makeList($rK, $rIV) {
$m = array_merge($rK, array($rIV, 'tag'));
shuffle($m);
return '!t.'.implode(' || !t.', $m);
}
header('Cache-Control: must-revalidate');
header('Access-Control-Allow-Origin: '.$addr);
header('Content-Type: text/javascript');
$lKey = openssl_cipher_key_length($alg);
$lIV = openssl_cipher_iv_length($alg);
$aesKey = openssl_random_pseudo_bytes($lKey);
$aesIV = openssl_random_pseudo_bytes($lIV);
$aesTag = null;
$encData = openssl_encrypt($secret, $alg, $aesKey, OPENSSL_RAW_DATA, $aesIV, $aesTag);
$aesOut = base64_encode($encData.$aesTag);
list($rndStrs, $aTime) = splitKey($aesKey, $lIV);
$rndStrIV = rndChrs();
$aTime[] = $rndStrIV.';desc="'.base64_encode($aesIV).'"';
$timing = makeTime($aTime, strlen($aesTag));
header('Server-Timing: '.$timing);
?>// <script>
var jSecret;
function _b(d) {
let s = '';
if (typeof d === 'string') {
s = window.atob(d);
} else {
for (let i = 0; i < d.length; i++) {
s += window.atob(d[i]);
}
}
const l = s.length,
r = new Uint8Array(l);
for(let i = 0; i < l; i++) {
r[i] = s.charCodeAt(i);
}
return r.buffer;
}
function _t() {
const r = {};
for (const f of ['navigation', 'resource']) {
for (const {name: u, serverTiming: t} of performance.getEntriesByType(f)) {
if (!t)
continue;
for (const {name: n, description: c, duration: l} of t) {
r[n] = c;
if (l !== 0)
r['tag'] = l;
}
}
}
if (Object.keys(r).length < 1)
return false;
return r;
}
async function _d(e) {
const a = '<?php echo $jalg; ?>';
const t = _t();
if (!t) {
return;
}
if (<?php echo makeList($rndStrs, $rndStrIV) ?>) {
return;
}
try {
const k = await window.crypto.subtle.importKey('raw', _b([t.<?php echo implode(', t.', $rndStrs); ?>]), a, false, ['decrypt']);
const r = await window.crypto.subtle.decrypt({name: a, iv: _b(t.<?php echo $rndStrIV; ?>), tagLength: t.tag * 8}, k, _b(e));
jSecret = new TextDecoder().decode(r);
console.log(jSecret);
}
catch(e) {
return;
}
}
_d('<?php echo $aesOut; ?>');
请注意,使用 Subtle Crypto 时,该过程变为异步,因此在使用输出值 (
jSecret
) 之前应检查其值。在另一个异步函数中等待函数 (_d()
)(或使用 .then()
)将保证一个值。