如何隐藏对我的javascript文件的直接访问?

时间:2014-11-07 14:16:32

标签: javascript php

我正在运行LAMP网络服务器。

我想在我的页面上包含我的脚本文件:

<script src="http://domain.com/script.js"></script>

我想访问http://domain.com/script.js以显示错误或空白页。

我见过other similar questions,答案是“只是混淆它”,或“混淆的安全性很差”。

这不是出于安全考虑。我想阻止机器人自动拉我的代码。我对人类用户获取代码感到满意。我只是喜欢这个作为混淆的替代

我已经尝试使用base64_encoded $_GET$_SESSION参数。我想知道那里是否有更优雅的解决方案。


澄清:

我知道Javascript仍可供用户使用。我完全可以通过Firebug,Chrome的开发人员工具等访问代码。我只是想通过我的标签访问代码,并且无法直接访问。这对于安全性来说,而来“隐藏”我的代码。


澄清2:

我需要这个的原因是因为我们公司最近发现一个竞争对手运行脚本来从我们的网站上删除数据。我希望能够阻止数据通过他们的脚本被删除,并强制他们手动执行。

5 个答案:

答案 0 :(得分:2)

  

我非常具体地问,如何通过我的<script>标记访问该文件,并且无法直接访问该文件。

有两种选择:

“防止热链接”解决方案

正如@MichaelBerkowski指出的那样,非常类似于不允许图像热链接的常见要求,并且同样的解决方案和陷阱也适用相同类型的解决方案。基本上,它是以下两者之一或两者兼而有之:

  1. 检查{JavaScript}文件请求的REFERER(sic)标头,如果REFERER未引用您的某个网页,则拒绝这些请求。

  2. 记住在短时间内(例如,最多一分钟)请求HTML页面的计算机的IP地址,并且仅允许这些IP地址下载JavaScript文件,拒绝来自所有其他IP地址的尝试。

  3. 第一个是微不足道的,但也很容易被绕过。第二个是不那么简单,但也很容易绕过(通过简单地发出HTML请求,然后忽略结果),但至少要求提出请求。

    将JavaScript嵌入HTML

    另一种方法是使用Apache模块缩小脚本,并在您拥有<script src="myfile.js"></script>标记的位置注入到您的HTML文件中,从而生成{{ 1}}标签代替。然后没有要求的JavaScript文件。这意味着多个页面上的相同JavaScript不会从缓存中受益,但它又具有以下优点:A)不需要单独的HTTP请求,以及B)使人们无法下载您的JavaScript文件(因为你根本就没有将它们置于外部 - 可见)。


    上述任何一种方法都意味着人们无法访问您的代码,因为从根本上说这是不可能的(您可以做的最好是混淆,并且去混淆器非常好;从根本上说,如果浏览器可以运行您的脚本,任何人都可以看到它,但从你对这个问题的评论中可以清楚地了解到这一点。

答案 1 :(得分:2)

考虑到您的澄清#2,您可以考虑使用PHP会话。

您可以先让用户点击需要验证密码才能继续的页面。提交并验证验证码后,将使用布尔$isHuman启动(或更新)PHP会话,以显示您确实在处理人类。

脚本请求将定向到仅在会话存在且$isHuman为真时才为脚本提供服务的php页面。

答案 2 :(得分:1)

有几个人试图在评论中解释,这不是真的可能,因为服务器怎么也无法知道是否正在将HTML文件作为HTML页面的一部分进行请求或者它自己的。

最接近实现这一目标的方法是创建一个随机字符串,并在生成HTML时将其附加到脚本中,并在调用JS时检查该字符串。

这就是CAPTCHA的工作方式,BTW。

以HTML格式

<?php
session_start(); //start session

// Function to generate random str, borrowed from here: http://stackoverflow.com/questions/4356289/php-random-string-generator
function randStr() {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $randomString = '';
    for ($i = 0; $i < 10; $i++) $randomString .= $characters[rand(0, strlen($characters) - 1)];
    return $randomString;
}

// set random str to session variable
$_SESSION['JS_STR'] = randStr();

// append random string to JS file, which will have to have a php extention
echo "<script src='myjavascriptfile.php?str=".$_SESSION['JS_STR']."' />";

?>

在你的JS

您必须将.js文件更改为.php

<?php
session_start();

//check make sure session variable matches the appended string
if(!isset($_SESSION['JS_STR']) || !isset($_GET['str']) || $_GET['str'] !== $_SESSION['JS_STR']) die("you don't have permission to view this");

//tell the browser your serving some JS
header('Content-Type: application/javascript');

?>
window.alert("your JS goes here...");

答案 3 :(得分:1)

我选择继续使用我在访问StackOverflow之前启动的$_SESSION / $_GET / $_POST门控脚本。

解决方案并不完美,但它符合我的需求,因为脚本可以通过我的标签访问,但直接无法访问。这是我正在做的简化版本:

文件1 是生成用户看到的HTML页面的PHP文件。此文件创建随机值,并将值设置为会话。使用此随机值作为GET参数包含脚本文件文件2

文件1:

<?php
session_start();
$gate['first_gate'] = crypt((time() * md_rand()) . 'salt');
$gate['second_gate'] = null;
$_SESSION['gate'] = json_encode($gate);
?>
<html>
    ...
    <!--this is just the HTML page including the script-->
    <script src="file_2.php?gate=<?=base64_encode(json_encode($gate))?>"></script>
    ...
</html>

文件2 是PHP文件,用作实际JavaScript代码的门。它验证随机化会话变量是否等于GET参数,然后使用POST请求从文件3 中获取代码。

文件2:

 <?php
 $session_gate = json_decode($_SESSION['gate']);
 $get_gate = json_decode(base64_decode($_GET['gate']));
 //Exit if the session value != the get value
 if($get_gate->first_gate != $session_gate->first_gate) exit;

 //Set first gate to null to prevent re-visit
 $session_gate->first_gate = null;
 $session_gate->second_gate = crypt((time() * md_rand()) . 'salt');
 $_SESSION['gate'] = json_encode($session_gate);
 header('Content-Type: application/javascript');
 ?>
 //This is visible via "view source" (then clicking on the script's URL)
 //Grab the actual JS file, hidden behind a POST "wall"
 $.post("file_3.php", { gate: '<?=base64_encode($_SESSION['gate'])?>' });
直接查看页面时无法访问

文件3 ,因为它在没有来自文件2 的POST数据的情况下退出。机器人 仍然能够通过POST请求对其进行ping操作,因此应在此处添加一些额外的安全措施。

文件3:

 <?php
 $session_gate = json_decode($_SESSION['gate']);
 $post_gate = json_decode(base64_decode($_POST['gate']));
 //Exit without a POST request. Use a more specific value, other than
 //the $_POST superglobal by itself (just using $_POST for illustrative purposes)
 if(!$_POST) exit; //or print an error message
 //Exit if the session value != the get value
 if($get_gate->second_gate != $session_gate->second_gate) exit;

 //Set both gates to null to prevent re-visit
 $session_gate->first_gate = null;
 $session_gate->second_gate = null;
 $_SESSION['gate'] = json_encode($session_gate);
 //Additional safety measures (such as IP address/HOST check) here, if desired
 header('Content-Type: application/javascript');
 ?>
 //Javascript code here

答案 4 :(得分:1)

根据您的回答,这是您的解决方案的简化版本:

<?php
//file1
session_start();
$token = uniqid();
$_SESSION['token'] = $token;
?>
<!--page html here-->
<script src="/js.php?t=<?php echo $token;?>"></script>

。     

header('Content-Type: application/javascript');
$token = isset($_GET['t'])? $_GET['t'] : null;
if(!isset($_SESSION['token']) || $_SESSION['token'] != $token){
    //lets mess with them and inject some random js, ih this case a random chunk of compressed jquery
    die('n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)}');
}
//regenerate token, this invalidates current token
$token = uniqid();
$_SESSION['token'] = $token;
?>
$.getScript('js2.php?t=<?php echo $token;?>');

<?php
//js2.php
//much the same as before
session_start();
header('Content-Type: application/javascript');
$token = isset($_GET['t'])? $_GET['t'] : null;
if(!isset($_SESSION['token']) || $_SESSION['token'] != $token){
    //lets mess with them and inject some random js, ih this case a random chunk of compressed jquery
    die('n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)}');
}
unset($_SESSION['token']);
//get actual js file, from a folder outside of webroot, so it is never directly accessable, even if the filename is known
readfile('../js/main.js');

请注意主要变化是:

  1. 简化令牌系统。由于令牌位于页面源中,因此功能所需的全部内容都是唯一的,尝试使用编码和盐等“更安全”无效。

  2. 实际的js文件保存在Web根目录之外,因此即使您知道文件名也无法直接访问

  3. 请注意,我仍然支持我关于知识产权禁止机器人的评论。这种解决方案将使拼抢变得更加困难,但并非不可能,并且可能对真正的访客产生无法预料的后果。