全局变量和信息安全

信息安全 访问控制 环境变量 违反
2021-09-06 14:34:41

我得到的印象是,在特定范围(如函数范围)中创建变量并避免全局范围以使事情更加模块化和更好地组织是一种编程最佳实践。但是我不确定是否还有安全问题。

这是一个在 Bash 中为我工作了一年多的全局变量示例:

cat <<-EOF >> "$HOME"/.profile
    set -x
    complete -r
    export war="/var/www/html" # Web Application Root;
    export dmp="phpminiadmin" # Database Management Program;

    export -f war
    war() {
        cd $war/
    }
EOF

source "$HOME"/.profile 2>/dev/null

在 Bash 或 JavaScript 中,我从来没有遇到过全局变量的问题,很可能是因为我只在极简环境中编写了供个人使用的小脚本。

为什么很多程序员避免使用全局变量,有没有使用全局变量导致安全漏洞的例子?

4个回答

抵制全球!

我从Steffen Ullrich 的评论中窃取信息,但全局变量的主要问题是它们难以保持代码的良好组织和可维护性。 他的链接很好,但是您可以毫不费力地在网上找到无数关于全局变量问题的文章。

当您使用全局变量时,很容易忘记变量在程序中的哪个位置被修改,特别是如果您没有简单的线性流。因此,全局变量可以在小脚本中完美运行,但随着应用程序开始扩展,可能会引起巨大的麻烦。

我避免使用全局变量的主要原因是因为它们使自动化单元/集成测试成为一场噩梦。小型应用程序可以在没有测试的情况下生存,但试图在没有良好测试的情况下管理大型应用程序只是一场噩梦(相信我,我在年轻和愚蠢的日子里尝试过)。

这可能会给您留下这样的印象,即全局变量在非常小的应用程序中很好,但由于应用程序通常只会随着时间的推移而增长,而从临时开始的东西会变成永久性的,所以使用它们真的是个坏主意。当使用适当范围的变量如此容易时,为什么要从错误的角度开始呢?

安全

使用全局变量对安全没有任何直接影响,但它们确实更容易解决安全问题,因为它将数据源与其使用断开。我什至见过以这种方式引入的实际漏洞:

想象一下,您有一个用于 SQL 查询的变量。最初,您将其设置为安全值,然后将其直接注入查询中。然而,它是一个全局的(或成为一个全局的!),后来用例发生变化,它是从用户输入中设置的。从用户输入设置它的开发人员没有意识到它是直接注入到查询中的(可能是因为它发生在完全不同的文件中,并且只发生在特定的流程中),因此不会为严格的输入检查而烦恼,也不会他们更新它用于更安全使用的查询。突然之间引入了一个非常难以发现的漏洞,因为全局变量隐藏了变量的来源和使用之间的联系!

全局变量 == 死亡

我不知道因全局变量而发生的任何违规行为,但很容易争辩说使用全局变量实际上已经杀死了人,所以我认为从不使用它们是合理的。

在某些编程环境中,您不能信任全局范围,因为其他来源可能会读取/弄乱它。

这已经导致安全敏感产品中的一些严重漏洞,例如LastPass 浏览器扩展中的远程代码执行

他们可以让代码注入更容易访问所有内容

在 PHP 中有$GLOBALS超全局;在 Python 中有globals()函数,而在 Javascript 中有window(或在 Node 中process)对象。这些都使得枚举所有全局变量变得微不足道。

一旦你这样做了,你就可以吸出它们包含的信息恶意更改它们。例如,如果敏感数据存储在全局中,攻击者可以轻松提取它。例如,如果数据库连接 URL 存储在全局中,攻击者可以将 URL 指向攻击者的服务器,然后受害者将尝试连接到该服务器并发送凭据。

此外,如果您有很多全局对象,那么您可以轻松调用它们的方法。

他们违反了“最小特权原则”

安全工程的这一原则基本上是说“不要给予某人超过他们完成工作所需的访问权限”。每个函数和方法都可以读写全局变量,即使它们与它的工作完全无关,这意味着如果那段代码即使以某种有限的方式被劫持,也会打开更大的攻击面。

(当然,大多数脚本语言的封装规则都很弱,这也违反了 POLP,但是如果对象超出范围,那么对它们做任何事情都会变得更加困难。)

它们是错误代码的温床

原因包括:

  • 程序员忘记了函数中的一个变量是全局变量,因此对其所做的更改在函数结束时持续存在,
  • 程序对函数调用顺序的非明显敏感性
  • 级联故障发生的容易程度(例如,当一个函数将全局设置为null,然后另一个函数崩溃时)
  • 容易发生且难以推理的高度复杂的相互作用

他们只是不整洁

如果您考虑一个到处都是文件的企业,事情并没有被整齐地整理和归档,而是到处乱放,如果有什么东西丢失了,谁会注意到呢?如果员工进行欺诈,没有人会发现差异,因为差异无处不在。如果一对钥匙丢失了,有人会认为它们被埋在什么东西下面,或者有人不得不借用它们。

现在你可能会说这是夸大其词,但如果是这样的话,我怀疑你是否曾经参与过大型软件项目。如果您的网站足够小,可以在几天内构建(并且您知道自己在做什么),那么全局变量是可以的。他们可以节省时间让事情顺利进行。

但它们无法扩展。

请比较以下代码:

1)

/file.php:
    $newconn = new PDO('mysql:host=localhost;charset=utf8mb4;','username','password');

2)

../secrets.php:
    $mysqlusername = 'username';
    $mysqlpassword = 'password';

/file.php:
    require_once('../secrets.php');
    $newconn = new PDO('mysql:host=localhost;charset=utf8mb4;',$mysqlusername,$mysqlpassword);

3

../secrets.php:
    function pdoconn(i) {
        $mysqlusername = ['username1','username2'];
        $mysqlpassword = ['password1','password2'];
        $conn = new PDO('mysql:host=localhost;charset=utf8mb4;',$mysqlusername[i],$mysqlpassword[i]);
        return $conn;
    }

/file.php:
    require_once('../secrets.php');
    $newconn = pdoconn(0);

示例 1 是不可能的 - 生产服务器上的错误配置最终可能会向非预期方显示敏感参数。

示例 2 更好,但这些变量在整个应用程序中都可用,并且可以修改,这可能会导致错误。

示例 3 使事情变得非常有条理和可转移。
如果../secrets.php改为使用全局变量,则可以修改为:

../secrets.php:
$mysqlusername = 'username';
$mysqlpassword = 'password';
function pdoconn()  {
    $conn = new
PDO('mysql:host=localhost;charset=utf8mb4;',$GLOBALS['mysqlusername'],$GLOBALS['mysqlpassword']);
return $conn;
}

我认为这以非常简洁的方式说明了为什么大多数时候全局没有意义。


概括:

至于使用全局变量的安全漏洞(以及为什么我在 PHP 中编写这些示例),在 PHP 4.2.0中有一个(当时)有争议的更改,其中 register_globals 被打开到关闭。我现在找不到任何文章,因为此更改是在 2002 年进行的,但我似乎确实记得它对当时的一些违规行为负责。直接从手册中复制,有一个非常清晰的易受攻击代码示例:

<?php
// define $authorized = true only if user is authenticated
if (authenticated_user()) {
    $authorized = true;
}

// Because we didn't first initialize $authorized as false, this might be
// defined through register_globals, like from GET auth.php?authorized=1
// So, anyone can be seen as authenticated!
if ($authorized) {
    include "/highly/sensitive/data.php";
}
?>