我正在编写一个使用Tesseract的用于字符识别的GUI应用程序。我想允许用户指定文本准备好后将使用/bin/sh -c
执行的自定义Shell命令。
问题在于,可识别的文本实际上可以包含任何内容,例如&& rm -rf some_dir
。
我的第一想法是使它像其他许多程序一样
用户可以在文本输入中键入命令,然后将命令中的特殊字符串(如printf()
)替换为适当的数据(在我的情况下,可能是%t
)。然后,整个字符串将传递到execvp()
。例如,这是qBittorrent的屏幕截图:
问题在于,即使我在替换%t
之前正确地转义了文本,也没有什么能阻止用户在说明符周围添加额外的引号:
echo '%t' >> history.txt
因此要执行的完整命令是:
echo ''&& rm -rf some_dir'' >> history.txt
显然,这是个坏主意。
第二选项仅允许用户选择一个可执行文件(带有文件选择对话框),因此我可以手动将Tesseract中的文本作为{{1}的argv[1]
}。这个想法是可执行文件可以是一个脚本,用户可以在其中放置所需的任何内容,并使用execvp()
访问文本。这样,命令注入是不可能的(我认为)。这是用户可以创建的示例脚本:
"$1"
这种方法有什么陷阱吗?也许有更好的方法可以安全地将任意文本作为参数传递给Shell脚本中的程序?
答案 0 :(得分:4)
请勿执行此操作。请参阅下面的“带外”部分。
要在严格符合POSIX的外壳中的未加引号的上下文中使用时,使任意C字符串(不包含NUL)对其自身求值,可以使用以下步骤:
'
(从所需的初始未引用上下文移至单引号上下文)。 '
替换数据中的每个文字'"'"'
。这些字符的工作方式如下:
'
关闭初始的单引号上下文。"
输入一个双引号上下文。'
是文字。"
关闭双引号上下文。'
重新输入单引号上下文。'
(返回所需的初始单引号上下文)。 这在POSIX兼容的外壳程序中可以正常工作,因为在单引号上下文中唯一不是文字的字符是'
;在这种情况下,甚至将反斜杠也解析为文字。
但是,这仅在仅在无引号的上下文中使用sigils时才有效(因此,请用户承担责任以使事情正确),并且外壳严格符合POSIX。同样,在最坏的情况下,您可以使此转换生成的字符串的长度比原始字符串长5倍;因此,需要注意用于转换的内存的分配方式。
(可能会问为什么建议使用'"'"'
而不是'\''
;这是因为反斜杠会更改其在旧版反引号命令替换语法中使用的含义,因此较长的形式会更健壮)。
数据仅应从代码中进行带外传输,以使它永远不会通过解析器运行。调用Shell时,有两种简单的方法(使用文件除外):环境变量和命令行参数。
在以下两种机制中,仅需信任user_provided_shell_script
(尽管这还要求不要引入新的或附加的漏洞;调用eval
或任何与之等效的道德使所有信任无效)保证,但这是用户的问题,而不是您的问题。
不包括错误处理(如果setenv()
返回非零结果,则应将其视为错误,并且应使用perror()
或类似内容向用户报告),如下所示:< / p>
setenv("torrent_name", torrent_name_str, 1);
setenv("torrent_category", torrent_category_str, 1);
setenv("save_path", path_str, 1);
# shell script should use "$torrent_name", etc
system(user_provided_shell_script);
一些注意事项:
execve()
系列调用失败。验证长度是否在特定于域的合理范围内是明智的。此版本需要一个显式的API,以便配置触发命令的用户知道哪个值将在$1
中传递,哪个值将在$2
中传递,等等。
/* You'll need to do the usual fork() before this, and the usual waitpid() after
* if you want to let it complete before proceeding.
* Lots of Q&A entries on the site already showing the context.
*/
execl("/bin/sh", "-c", user_provided_shell_script,
"sh", /* this is $0 in the script */
torrent_name_str, /* this is $1 in the script */
torrent_category_str, /* this is $2 in the script */
path_str, /* this is $3 in the script */
NUL);
答案 1 :(得分:0)
每次运行命令时,甚至可能会有用户输入进入命令中,都必须转义以获取shell上下文。
C中没有内置函数可以执行此操作,因此您可以自己操作,但是基本思想是将用户参数呈现为正确转义的字符串或某种执行函数的单独参数(例如{ {3}}。