如何保护PHP脚本?

Jua*_*ano 5 php security web

如果我对php脚本进行AJAX调用,就像这样(使用jQuery)

$.ajax(url: "../myscript.php");
Run Code Online (Sandbox Code Playgroud)

并且myscript看起来是这样的:

<?php
    //code that does something to db
 ?>
Run Code Online (Sandbox Code Playgroud)

我想知道如何防止用户只是去example.com/myscript.php来执行脚本.

reg*_*ero 8

Ajax查询只是用户查询

每个XmlHTTP请求都可以重放和篡改(只需检查您最喜欢的浏览器控制台,捕获POST或GET请求并检查是否有重播选项),您还可以尝试Live HTTP Headers模块(或更多)并捕获任何内容进行重放它.

因此,如果您在应用程序中设置了一个入口点,那么任何人都可以尝试访问它并在那里注入一些不好的东西.

请注意,他们还可以更改其请求中的任何HTTP标头,以更改引用页面或主机标头等任何内容.

不安全的输入

因此,在安全性方面,每个用户输入都必须被认为是不安全的(GET参数,POST数据,使用过的url - OMG,因此很多应用程序永远不会过滤来自url路径的数据 - ,cookies,......)

过滤输出

所以你可能想知道"我怎么能用不安全的输入做些什么?",嗯......你可以.规则是过滤所有输出.获取输出管道(数据库存储,html页面,json响应,csv文件)并相应地转义数据(htmlentites用于HTML,json转义为json,sql escaper或参数化查询用于SQL查询 - 检查libs--),尤其是来自用户输入的部件,如前所述,实际上是不安全的.

访问控制

现在您的主要问题是访问控制,您有一个入口点,您可以在其中执行某些数据库操作,并且您不希望任何人访问此入口点并执行操作.

有几件事要做:

  • 确保这不是GET入口点(只有POST,PUT,DELETE HTTP操作应该对数据库执行修改),这将阻止在以后在图像标记中使用此URL,在没有用户交互的情况下加载操作.
  • 管理用户会话,使用cookie(PHP为您做到这一点)您可以在多个HTTP请求之间共享一些数据,这称为会话.用户cookie将用于加载服务器端会话存储,其中包含重要数据,例如我的用户是匿名用户还是已连接的用户?.这是识别部分.
  • 管理登录注销页面以获取身份验证部分,这些页面将以登录状态提供会话.对于一个简单的解决方案,您还可以检查HTTP基本身份验证(.htpasswd文件),它也适用于ajax,但从不使用没有SSL的HTTP基本身份验证.此Http身份验证模式将管理身份和身份验证部分.
  • 按照您希望的方式管理ACL(访问控制列表),并使用它来决定当前用户是否可以访问您的ajax页面(您从会话中获取用户).如果没有,请发送403 HTTP响应.

公共访问

现在,如果您应该运行的"数据库"内容与任何用户权限无关,但您只是想防止滥用它,例如统计ajax查询,执行计数器增量,每个用户应至少调用一次.在这种情况下,您将遇到一些问题.很难防止滥用公共入口点(只考虑保护网站免受DOS和DDOS的困扰).您必须构建一个基于应用程序的功能系统,例如在用户页面中生成唯一令牌并检查此令牌仅使用一次(但是来自代理的数千个用户可以使用匿名页面)缓存),也许你必须记录用户IP并通过IP限制令牌使用(但是一些用户可能共享相同的IP),或者你可能必须使用ajax将唯一令牌发送给用户.

我们可以谈论很多事情,但这取决于你想要做的事情.重要的是:

  • 从不信任用户输入
  • 过滤输出
  • 管理会话和ACL
  • 从不认为任何隐藏的东西,没有这样的东西.


mTo*_*res 3

这里的一些答案让您概述了您的问题背后的概念,让我给您一个更务实的方法(您至少应该阅读并理解其他人对此事的看法!)。

您只需要问自己:您的应用程序是否必须强制控制myscript.php的所有请求?

如果是这样,那么您需要使用某种令牌:您创建一个令牌并将其发送到客户端(浏览器),然后浏览器必须发回令牌并在执行某些操作之前检查它是否匹配:

<?php
// somefile.php (this file serves the page that contains your AJAX call)
session_start();
//...
$_SESSION['token'] = createNewToken(); // creates unique tokens

//add the token as a JS variable and send it back in your AJAX CALL
// some where you'll have something similar to this:
<script>
  var token = <?php echo $_SESSION['token'] ?>;
  $.ajax({
    url: "myscript.php",
    data: form_data, // include the token here!
    //...
  })
Run Code Online (Sandbox Code Playgroud)

然后在你的脚本中:

<?php
// myscript.php
session_start();

// you can check if it's an AJAX call, if the user is logged and then the token:    
if (!isset($_SESSION['token')) {
  header("HTTP/1.0 403 Forbidden");
  die("Direct access not allowed!");
}

// Assuming your AJAX is a POST though you didn't tell us
if (!isset($_POST['token'] || $_POST['token'] != $_SESSION['token']) {
  header("HTTP/1.0 400 Bad request");
  die("You didn't provide a valid token!");
}

// do something with your DB
Run Code Online (Sandbox Code Playgroud)

否则,您只需检查用户是否已登录,就像通常处理其余脚本一样:

<?php
// myscript.php
session_start();
// Check if logged in user
if (!isset($_SESSION['loggedIn']) || !$_SESSION['loggedIn']) {
  header("HTTP/1.0 403 Forbidden");
  die("You need to be logged in!");
}

// Do something with your DB
Run Code Online (Sandbox Code Playgroud)

总结

使用第一种方法可以实现更受控的访问,因为您强制用户发送普通用户不会拥有的秘密(令牌,在每个请求中都不同)(如果其他用户获得了令牌,那么您就拥有了更大的问题,例如会话劫持)。请注意,此方法可防止用户在多个选项卡/不同浏览器上打开,因为只会保存最后一个令牌。为了避免你对SO有这个很棒的答案

另一方面,第二种方法允许(已登录)用户直接请求您的myscript.php,但也许您不需要阻止这种情况(如果需要,只需使用第一种方法)。请注意,这里您不会遇到多个选项卡/不同浏览器的问题,因为您只会检查用户是否已登录。