wikimedia / security-check-plugin
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.5.28
README
这是一个用于检测安全问题的(例如 XSS)Phan 插件。它会记录用户修改变量的任何时间,并检查这些变量在作为 HTML 输出或用作 SQL 查询等之前是否已转义。
它支持通用的 PHP 项目,并且还针对 MediaWiki 代码(分析钩子、HTML 表单和数据库方法)有专门的模式。
有一个可用的 Web 示例。
使用方法
安装
$ 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
- 允许人们自定义污染类型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)
。序列化注入通常被认为是“关键”的,但当前表示为严重性为NORMAL,因为检查似乎有很高的误报率。
您可以使用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可能会产生混淆,您希望覆盖特定函数的污染计算。
您可以通过在文档注释中添加污染指令来实现这一点。例如
/** * 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()
方法。此方法返回一个关联数组,其中包含完全限定的方法名和描述函数返回值污点(taint)的数组。数值键对应于参数的编号,而 'overall' 键添加不存在于任何参数中的污点。基本上,对于每个参数,插件获取参数的污点,将其与数组中的条目进行按位与操作,然后将按位或操作应用于 'overall' 键。如果数组中的任何键具有 EXEC 标志,则在将对应的污点传递给函数(例如,输出函数)时,会立即引发问题。'overall' 键中不适用 EXEC 标志。
例如,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
很方便)