mediawiki / phan-taint-check-plugin
一个用于安全检查的 Phan 插件
Requires (Dev)
- mediawiki/mediawiki-codesniffer: 41.0.0
- mediawiki/minus-x: 1.1.1
- php-parallel-lint/php-console-highlighter: 1.0.0
- php-parallel-lint/php-parallel-lint: 1.3.2
- phpunit/phpunit: 9.6.16
README
这是一个用于Phan的插件,旨在尝试检测安全问题(如XSS)。它跟踪任何用户可以修改变量的时间,并检查这些变量在作为 HTML 输出或用作 SQL 查询等之前是否已转义。
它支持通用的 PHP 项目,并且它还为 MediaWiki 代码提供了专用模式(分析钩子、HTML 表单和数据库方法)。
有一个在线演示。
用法
安装
$ composer require --dev mediawiki/phan-taint-check-plugin
用法
该插件可以在“手动”和“独立”模式下使用。如果您的项目已经运行了 phan,则前者是最佳选择,几乎不需要配置。后者仅在您不想将 phan 添加到项目时使用,且不支持 MediaWiki 相关代码。有关 Wikimedia 使用此插件的更多信息,请参阅https://www.mediawiki.org/wiki/Phan-taint-check-plugin。
手动
您只需将 taint-check 添加到 phan 配置的 plugins 部分。假设 taint-check 在标准 vendor 位置,例如 $seccheckPath = 'vendor/mediawiki/phan-taint-check-plugin/';,对于通用项目,要包含的文件是 "$seccheckPath/GenericSecurityCheckPlugin.php",对于 MediaWiki 项目是 "$seccheckPath/MediaWikiSecurityCheckPlugin.php"。
此外,请确保已禁用快速模式,否则插件将无法正常工作
'quick_mode' => false
您还应该将 SecurityCheck-LikelyFalsePositive 和 SecurityCheck-PHPSerializeInjection 添加到 suppress_issue_types(后者有很高的误报率)。
然后像往常一样运行 phan
$ vendor/bin/phan -d . --long-progress-bar
使用 --analyze-twice 运行 phan 可以捕获在正常分析阶段可能被忽视的额外安全问题。这种方法的已知限制是,同一个问题可能会因为不同的原因被多次报告。
独立
您可以通过以下方式运行 taint-check
$ ./vendor/bin/seccheck
您可能想要为此添加一个 composer 脚本别名
"scripts": { "seccheck": "seccheck" }
请注意,默认情况下禁用了误报
插件输出
根据检测到的内容,插件将输出各种问题类型。它输出的问题类型包括
SecurityCheck-XSSSecurityCheck-SQLInjectionSecurityCheck-ShellInjectionSecurityCheck-PHPSerializeInjection- 当某人执行unserialize( $_GET['d'] );时。目前这种问题类型似乎有很高的误报率。SecurityCheck-CUSTOM1- 允许人们有自定义的 taint 类型SecurityCheck-CUSTOM2- 同上SecurityCheck-DoubleEscaped- 检测 HTML 是否被双重转义SecurityCheck-RCE- 远程代码执行,例如eval( $_GET['foo'] )SecurityCheck-PathTraversal- 路径遍历,例如require $_GET['foo']SecurityCheck-ReDoS- 正则表达式拒绝服务(ReDoS),例如preg_match( $_GET['foo'], 'foo')SecurityCheck-LikelyFalsePositive- 一个潜在的问题,但可能不是。主要发生在插件混淆时。
严重性字段通常标记为 Issue::SEVERITY_NORMAL (5)。误报通常标记为 Issue::SEVERITY_LOW (0)。可能导致服务器被入侵(而不是仅终端用户被入侵)的问题,如shell或SQL注入,标记为 Issue::SEVERITY_CRITICAL (10)。序列化注入通常被认为是“关键”的,但当前标记为正常严重性,因为检查似乎目前有很高的误报率。
您可以使用Phan的-y命令行选项按严重性过滤。
如何避免误报
如果您需要抑制误报,可以在函数/方法的docblock中放置@suppress NAME-OF-WARNING。或者,您可以使用其他类型的抑制,如@phan-suppress-next-line。有关完整列表,请参阅phan的readme。@param-taint和@return-taint(见“自定义”部分)在处理误报时也非常有用。
请注意,该插件将在CLI上下文中报告可能存在的XSS漏洞。为了避免这些问题,您可以在CLI脚本中使用@phan-file-suppress抑制SecurityCheck-XSS文件范围的警告,或者对于整个应用程序(使用suppress_issue_types配置选项),如果应用程序仅由CLI脚本组成。或者,如果所有输出都来自内部函数,您可以使用以下方法使用@param-taint
/** * @param-taint $stuffToPrint none */ public function printMyStuff( string $stuffToPrint ) { echo $stuffToPrint; }
当调试安全问题时,您可以使用
'@phan-debug-var-taintedness $varname';
这将发出一个包含$varname在注释所在行的污染性的SecurityCheckDebugTaintedness问题。请注意,您必须将注释插入字符串字面量中;注释将不起作用。有关更多信息,请参阅phan的@phan-debug-var注释。
显著的限制
一般限制
- 当问题输出时,插件会尝试包含有关最初导致问题的行的详细信息。通常这会起作用,但有时会提供误导性/错误的信息。
- 插件不会识别自定义转义的东西。如果您有自定义转义方法,必须在它的docblock中添加注释,以便插件可以识别它。请参阅自定义部分。
- Phan目前没有提供用于访问给定类的子类的API。因此,SecurityCheckPlugin无法适应某些显然应该被认为是污染的数据流。解决方法是标记任何相关的子类函数为
@return-taint html。
MediaWiki特定限制
- 使用MediaWiki钩子的引用参数传递时,有时行号是MediaWiki核心中的钩子调用,而不是导致问题的扩展中的钩子订阅者。
- 如果其直接作为数组字面量提供,或直接从
getQueryInfo()方法返回数组字面量,则插件只能验证MediaWiki的IDatabase::select()的第五个($options)和第六个($join_cond)。
自定义
该插件通过继承SecurityCheckPlugin类支持自定义。有关如何做到这一点的复杂示例,请参阅MediaWikiSecurityCheckPlugin。
有时您的代码库中有方法会改变变量的污染状态。例如,自定义html转义函数应清除html污染位。同样,有时phan-taint-check可能会迷惑,您想覆盖特定函数的污染计算。
您可以通过在docblock注释中添加污染指令来完成此操作。例如
/** * My function description * * @param string $html the text to be escaped * @param-taint $html escapes_html */ function escapeHtml( $html ) { }
方法也会从祖先接口中的抽象定义继承这些指令,但不会从祖先类中的具体实现继承。
污点指令以@param-taint $parametername或@return-taint开头。如果有多个指令,它们可以用逗号分隔。@param-taint用于标记污点如何从参数传递到方法的返回值,或者当与exec_指令一起使用时,用于标记参数输出或执行的位置。@return-taint用于调整返回值的污点,而不管输入参数如何。
指令的类型包括
exec_$TYPE- 如果一个参数被标记为exec_$TYPE,那么向该参数提供一个带有$TYPE污点的值将触发警告。通常您会在一个函数输出或执行其参数时使用此指令。escapes_$TYPE- 用于函数逃逸后返回参数的参数。因此,escapes_sql将清除sql污点位,但保留其他污点位。onlysafefor_$TYPE- 用于@return-taint,将返回类型标记为特定$TYPE是安全的,但对于其他类型是不安全的。$TYPE- 如果仅指定了参数中的类型,则将其与输入变量的污点进行位与操作。通常您不希望这样做,但当$TYPE为none时,可以用来指定参数未用于生成返回值。在@return中,这可以用来列举返回值具有哪些污点标志,这通常仅在指定为tainted时有用,表示它具有所有标志。array_ok- 用于表示忽略数组中的污点参数的特殊用途标志。allow_override- 用于指定污点注释应由phan-taint-check在检测到特定污点时覆盖的特殊用途标志。
$TYPE的值可以是以下之一:htmlnoent、html、sql、shell、serialize、custom1、custom2、code、path、regex、sql_numkey、escaped、none、tainted。其中大多数是污点类别,除了
htmlnoent- 与html类似,但禁用了与html一起使用的双重转义检测。当指定escapes_html时,自动将escaped添加到@return,并将exec_escaped添加到@param。同样,onlysafefor_html等价于onlysafefor_htmlnoent,escaped。none- 表示没有污点tainted- 表示所有污点类别(等同于SecurityCheckPlugin::YES_TAINT)escaped- 表示值已经转义(用于跟踪双重转义)sql_numkey- 对于MediaWiki来说非常特殊,它如果数组是关联键时忽略污点。
@param-taint的默认值是如果它是字符串(或其他危险类型),则为tainted,如果它是整数等,则为none。默认值@return-taint是allow_override(这等价于none,除非可以自动检测到更好的值)。
除了在代码库中注释方法外,还可以自定义phan-taint-check以具有内置的方法污点知识。此外,可以扩展插件以具有相当任意的行为。
为此,您将重写getCustomFuncTaints()方法。此方法返回一个关联数组,其中包含完全限定的方法名称和数组,该数组描述了函数返回值的污点如何根据其参数。数字键对应于参数的编号,而'overall'键添加了不在任何参数中存在的污点。基本上,对于每个参数,插件将参数的污点与数组的条目进行位与操作,然后与'overall'键进行位或操作。如果数组中的任何键具有EXEC标志,则如果向函数提供相应的污点,则立即引发问题(例如,输出函数)。EXEC标志在'overall'键中不起作用。
例如,htmlspecialchars 函数,用于去除 HTML 污染,转义其参数并返回转义后的值,其代码如下所示:
'htmlspecialchars' => [ ( self::YES_TAINT & ~self::HTML_TAINT ) | self::ESCAPED_EXEC_TAINT, 'overall' => self::ESCAPED, ];
环境变量
以下环境变量会影响插件。通常情况下,您不需要调整这些变量。
SECURITY_CHECK_EXT_PATH- 在 MediaWiki 模式下包含extension.json/skin.json文件的目录路径。如果未设置,则默认为项目根目录。SECCHECK_DEBUG- 输出额外调试信息的文件(如果从shell运行,/dev/stderr很方便)