shell中实用eval命令和安全问题

eval命令非常强大,但也非常容易被滥用。

它会导致代码被解析两次而不是一次。这意味着,如果你的代码中包含变量引用,shell解析器将评估该变量的内容。如果变量包含一个shell命令,shell可能会运行该命令,无论你是否希望运行它。这可能会导致意外的结果,特别是当变量可以从不受信任的来源(如用户或用户创建的文件)读取时。

请注意,eval命令在编程中被广泛认为是危险的。它可以执行任意的Shell代码,包括恶意代码,因此应该谨慎使用。

Bash的名称引用问题

Bash 4.3引入了declare -n("名称引用")来模仿Korn shell的nameref​​特性,允许变量保存对其他变量的引用。然而,Bash中使用的实现存在一些问题。

首先,Bash的declare -n​​实际上并没有避免名称冲突问题:

$ foo() { declare -n v=$1; }
$ bar() { declare -n v=$1; foo v; }
$ bar v
bash: warning: v: circular name reference

换句话说,我们无法给名称引用指定一个安全的名称。如果调用者的变量恰好具有相同的名称,那就麻烦了。

其次,Bash的名称引用实现仍然允许任意代码执行:

$ foo() { declare -n var=$1; echo "$var"; }
$ foo 'x[i=$(date)]'
bash: i=Thu Mar 27 16:34:09 EDT 2034: syntax error in expression (error token is "Mar 27 16:34:09 EDT 2023")

这个例子并不优雅,但你可以清楚地看到date​​命令实际上被执行了。这绝不是我们想要的结果。

尽管存在这些缺点,declare -n​​特性是朝着正确方向迈出的一步。但你必须小心选择一个调用者不会使用的名称(这意味着你需要对调用者有某种控制,即使只是告诉他们“不要使用以_my_pkg​​开头的变量”),并且必须拒绝不安全的输入。

eval的良好使用示例

eval最常见的正确使用方式是从专门设计为以这种方式使用的程序输出中读取变量。例如,

# 在旧系统上,调整窗口大小后必须运行以下命令:
eval "`resize`"# 更高级的用法:获取SSH私钥的密码短语。
# 这通常从.xsession或.profile类型的文件执行。
# ssh-agent生成的变量将被导出到用户会话中的所有进程,以便之后的ssh命令可以继承这些变量。
eval "`ssh-agent -s`"

eval还有其他用途,特别是在创建变量时(参考indirect variable references ↗)。以下是一种解析不带参数的命令行选项的示例:

# POSIX
#
# 动态创建选项变量。尝试调用:
#
#    sh -x example.sh --verbose --test --debugfor i; docase $i in--test|--verbose|--debug)shift                   # 从命令行中移除选项name=${i#--}            # 删除选项前缀eval "$name=\$name"    # 创建*新*变量;;esac
doneecho "verbose: $verbose"
echo "test: $test"
echo "debug: $debug"

那么,为什么这个版本是可接受的呢?这是因为我们限制了eval命令的使用,只有在输入是一组有限的已知值之一时才会执行。因此,用户无法滥用它以导致任意命令执行——任何包含奇怪内容的输入都不会匹配三个预定的可能输入之一。

请注意,这仍然是不推荐的:这是一条很陡峭的道路,稍后的维护很容易将这段代码变成危险的内容。例如,你想要添加一个功能,允许传递一堆不同的--test-xyz选项。你将--test更改为--test-*,而不费力地检查脚本的其他部分的实现。你测试你的用例,一切正常。不幸的是,你刚刚引入了任意命令执行:

$ ./foo --test-'; ls -l /etc/passwd;x='
-rw-r--r-- 1 root root 943 2007-03-28 12:03 /etc/passwd

再次强调:允许eval命令在未经过滤的用户输入上使用会导致任意命令执行。

尽一切可能避免将数据传递给eval,即使你的代码似乎处理了所有边界情况。

如果你经过深思熟虑并向#bash寻求了替代方法,但没有找到任何方法,请跳到"Robust eval usage"部分。

使用declare的问题

使用declare能更好地完成这个任务吗?

for i in "$@"; docase "$i" in--test|--verbose|--debug)shift                   # 从命令行中移除选项name=${i#--}            # 删除选项前缀declare $name=Yes       # 设置默认值;;--test=*|--verbose=*|--debug=*)shiftname=${i#--}value=${name#*=}        # value是第一个单词后面的内容和=name=${name%%=*}        # 仅限于第一个单词(即使值中有另一个=)declare $name="$value"  # 创建*新*变量;;esac
done

请注意,--name用于默认值,--name=value是必需的格式。

以下是eval的一个良好使用示例,用于从专门设计为以这种方式使用的程序输出中读取变量:

# 在旧系统上,调整窗口大小后必须运行以下命令:eval "`resize`"# 更高级的用法:获取SSH私钥的密码短语。# 这通常从.xsession或.profile类型的文件执行。# ssh-agent生成的变量将被导出到用户会话中的所有进程,以便之后的ssh命令可以继承这些变量。eval "`ssh-agent -s`"

eval还可以用于创建变量时,尤其是在创建间接变量引用时。下面是一个解析不带参数的命令行选项的示例:

# POSIX
#
# 动态创建选项变量。尝试调用:
#
#    sh -x example.sh --verbose --test --debugfor i; docase $i in--test|--verbose|--debug)shift                   # 从命令行中移除选项name=${i#--}            # 删除选项前缀eval "$name=\$name"    # 创建*新*变量;;esac
doneecho "verbose: $verbose"
echo "test: $test"
echo "debug: $debug"

尽管这个示例中的eval使用看起来安全,但仍然不推荐广泛使用eval命令,因为它需要非常小心的输入过滤和验证,以避免任意命令执行漏洞。尽量避免将数据传递给eval,并寻找替代方案,以增加脚本的安全性。

使用declare存在的问题

难道使用declare不能更好地解决这个问题吗?

for i in "$@"; docase "$i" in--test|--verbose|--debug)shift                   # 从命令行中移除选项name=${i#--}            # 删除选项前缀declare $name=Yes       # 设置默认值;;--test=*|--verbose=*|--debug=*)shiftname=${i#--}value=${name#*=}        # 值是等号后面的内容name=${name%%=*}        # 仅限于第一个单词的名称(即使值中还有另一个等号)declare $name="$value"  # 创建*新的*变量;;esac
done

请注意,默认情况下,--name和--name=value是必需的格式。

对于某些输入,declare确实可以更好地工作:

griffon:~$ name='foo=x;date;x'
griffon:~$ declare $name=Yes
griffon:~$ echo $foo
x;date;x=Yes

但它仍然会导致数组变量中的任意代码执行:

attoparsec:~$ echo $BASH_VERSION
4.2.24(1)-release
attoparsec:~$ danger='( $(printf "%s!\n" DANGER >&2) )'
attoparsec:~$ declare safe=${danger}
attoparsec:~$ declare -a unsafe
attoparsec:~$ declare unsafe=${danger}
DANGER!

这段代码展示了使用declare可能引发的安全问题。在某些情况下,使用declare可能会导致任意代码执行,从而产生潜在的安全漏洞。在这个例子中,变量的值包含了一个命令,当使用declare声明变量时,该命令将被执行。这可能导致不受信任的代码执行,从而引发安全问题。

为了确保脚本的安全性,应该避免将不受信任的数据传递给declare命令。如果需要动态创建变量,可以考虑使用其他安全的方法或寻找替代方案,以避免潜在的安全风险。

强大的eval​​用法

几乎总是(至少在Bash中99%或更多的时间内,但也适用于更简洁的shell),正确地使用eval​​的方式是在库代码中生成隐藏在函数背后的抽象层。这允许函数具有以下功能:

  • 向函数的调用者呈现一个明确定义的接口,指定哪些输入必须由程序员严格控制,哪些可能是不可预测的,例如受用户输入影响的副作用。重要的是要记录哪些选项和参数在没有控制的情况下是不安全的。
  • 对某些类型的输入进行输入验证,如果可行,例如整数。在这种情况下,可以轻松地退出并返回一个错误状态,该错误状态可以由函数的调用者处理。
  • 创建隐藏使用eval​​的丑陋实现细节的抽象。

通常,当满足以下至少全部条件时,eval​​是正确的:

  • 可能的所有eval​​参数都保证不会在任何情况下产生有害的副作用或导致任意代码的执行。这些输入是静态编码的,不与不受控制的动态代码交互,并且/或经过彻底验证。这就是为什么函数很重要,因为你不一定需要自己保证这个保证。只要您的函数记录了哪些输入可能是危险的,您就可以将这个任务委托给函数的调用者。
  • ​eval​​用法向用户或程序员呈现了一个清晰的接口。
  • ​eval​​使得原本不可能的事情成为可能,而无需编写更大、更慢、更复杂、更危险、更丑陋、更不实用的代码。

如果出于某种原因仍然需要动态构建Bash代码并评估它,请确保采取以下预防措施:

  1. 始终引用eval​​表达式:eval 'a=b'​​
  2. 始终使用单引号引用代码,并使用printf​​的%q​​将数据扩展到其中:eval "$(printf 'myvar=%q' "$value")"​​
  3. 不要使用动态变量名。即使使用了小心的%q​​用法,这也可能会被利用。

为什么要注意?如果未能遵循上述建议,以下是脚本可能会受到利用的示例:

  • 如果不对代码进行单引号引用,则存在将数据扩展到其中而没有进行%q​​处理的风险。这意味着该数据可以自由执行:
name='Bob; echo I am arbitrary code'; eval "user=$name"
  • 即使在对输入数据进行%q​​处理之后再将其视为变量名进行处理,如果赋值中存在非法变量名,Bash将会在PATH​​中搜索命令:
echo 'echo I am arbitrary code' > /usr/local/bin/a[1]=b; chmod +x /usr/local/bin/a[1]=b; var='a[1]' value=b; eval "$(printf '%q=%q' "$var" "$value")"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101908.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【经典排序算法 time: 2023-10-12】插入排序(层层优化改进)

原理 每次循环往后面拿出一个元素排到前面有序序列中(动态演示) 插排第一版 public class InsertSort {public static void main(String[] args) {long start System.currentTimeMillis();int[] arr {11, 23, 69, 99, 1, 3, 45, 67, 5, 234, 678, 99…

ppt录屏没有声音?超实用教程来了!

随着信息技术的发展,ppt已经成为工作中必不可少的工具。无论是工作报告、项目展示还是学术交流,都离不开ppt的辅助。屏幕录制功能是ppt的一个重要特性,可以帮助用户方便地录制幻灯片演示,但在使用过程中,有时会遇到ppt…

ubuntu配置yolov5环境

本文硬件平台为工控机,系统环境为ubuntu18 配置yolov5步骤 1.下载pytorch和torchvision软件包 由于在线安装容易出现安装失败,所以本文使用的是本地安装。本文是基于miniconda安装的,miniconda安装参考之前的博客:ubuntu中安装m…

开发者福利:免费好用的API推荐

企业基本信息(含联系方式):通过公司名称/公司ID/注册号或社会统一信用代码获取企业基本信息和企业联系方式,包括公司名称或ID、类型、成立日期、电话、邮箱、网址等字段的详细信息。企业商标信息:获取商标的有关信息,包括商标图片…

微信小程序报错request:fail -2:net::ERR_FAILED(生成中间证书)

微信小程序报错request:fail -2:net::ERR_FAILED-生成中间证书 前言一、检查网站ssl证书二、生成证书方法1.获取中间证书手动合并1.进入网站:[https://www.myssl.cn/tools/downloadchain.html](https://www.myssl.cn/tools/downloadchain.html)2.点击下一步3.手动合…

C++教程(2)

C 环境设置 本地环境设置 如果您想要设置 C 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C 编译器。 文本编辑器 这将用于输入您的程序。文本编辑器包括 Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。 文…

uniapp:幸运大转盘demo

<template><view class"index"><image src"../../static/img/158.png" mode"" class"banner"></image><view class"title">绿色积分加倍卡拿到手软</view><almost-lottery :lottery…

招投标系统简介 企业电子招投标采购系统源码之电子招投标系统 —降低企业采购成本

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

cuML机器学习GPU库

目录 一、开始安装 1、创建虚拟环境 2、激活该虚拟环境 3、安装cuML 4、安装ipykernel 5、在jupter上使用&#xff0c;所以需要配置一下新的内核 二、调试 1、原始机器学习库运行 2、cuml库运行 以下安装教程为基于Linux系统&#xff0c;cuda版本为11.3.109、驱动530.…

智能机场系统:打造出行体验的未来

随着航空业的迅猛发展&#xff0c;机场作为出行的重要枢纽&#xff0c;必须不断提升自身的服务质量和效率。智能机场系统应运而生&#xff0c;为旅客提供更加便捷、智能化的出行体验。本文将从技术应用、服务优化和安全保障三个方面&#xff0c;全面介绍智能机场系统的特点和优…

早安问候语早安心语,别把人生想太难,人生需要鼓励

1、别把人生想的太难&#xff0c;人生需要几分自我的鼓励&#xff0c;不管在什么时候&#xff0c;要有几分信念和信心&#xff0c;生活少不了哭哭笑笑。青山绿水依然在&#xff0c;来来往往人不同&#xff0c;要学会看得惯&#xff0c;还要学会看得开&#xff0c;你内心的平坦是…

正则表达式——^的两种用法

用法一: 限定开头 文档上给出了解释是匹配输入的开始&#xff0c;如果多行标示被设置成了true&#xff0c;同时会匹配后面紧跟的字符。 比如 /^A/会匹配"An e"中的A&#xff0c;但是不会匹配"ab A"中的A 用法二&#xff1a;&#xff08;否&#xff0…

C语言常见题目 过关斩将(1)C语言的那些坑题,你可知道❓

我的个人主页&#xff1a;☆光之梦☆的博客_CSDN博客-C语言基础语法&#xff08;超详细&#xff09;领域博主 欢迎各位 &#x1f44d;点赞 ⭐收藏 &#x1f4dd;评论 我的专栏&#xff1a;C语言基础语法&#xff08;超详细&#xff09;_☆光之梦☆的博客-CSDN博客&#xff08;这…

ldd--cppad--static_assert--gflags

1问题解决 ‘google’ has not been declaredbool isSmoothingMethodValid = google::RegisterFlagValidator(^ /home/ubuntu/Code_Study/path_optimizer_test-master/src/config/planning_flags.cpp:115:34: error: ‘google’ has not been declaredbool isOptimizationMetho…

学网络安全的过程 ,差点要了我的命

我真的好像感慨一下&#xff0c;这个世界真的给计算机应届生留活路了吗&#xff1f; 看着周围的同学&#xff0c;打算搞前端、JAVA、C、C的&#xff0c;一个两个去跑去应聘。你以为是00后整治职场&#xff1f; 真相是主打一个卑微&#xff1a;现阶段以学习为主&#xff08;工资…

2、服务器安装docker

# 1.卸载旧的版本 yum remove -y docker \ docker-client\ docker-client-latest\ docker-common docker-latest\ docker-latest-logrotate\ docker-logrotate docker-s…

接口与抽象类的区别

a、抽象类不能被实例化只能被继承&#xff1b;b、包含抽象方法的一定是抽象类&#xff0c;但是抽象类不一定含有抽象方法&#xff1b;c、抽象类中的抽象方法的修饰符只能为public或者protected&#xff0c;默认为public&#xff1b;d、一个子类继承一个抽象类&#xff0c;则子类…

保护互联网数据安全:关键方法与最佳实践

在当今数字化时代&#xff0c;互联网数据安全已经成为个人、企业和组织的首要任务之一。随着信息技术的迅猛发展&#xff0c;网络威胁也不断演进&#xff0c;因此保护互联网数据安全变得尤为关键。本文将介绍一些关键方法和最佳实践&#xff0c;帮助您确保互联网数据的安全性。…

input时间控件选择时禁用某个日期之前或之后

【版权所有&#xff0c;文章允许转载&#xff0c;但须以链接方式注明源地址&#xff0c;否则追究法律责任】【创作不易&#xff0c;点个赞就是对我最大的支持】 前言 仅作为学习笔记&#xff0c;供大家参考 总结的不错的话&#xff0c;记得点赞收藏关注哦&#xff01; 目录 …