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-XSS
SecurityCheck-SQLInjection
SecurityCheck-ShellInjection
SecurityCheck-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
很方便)