mediawiki/phan-taint-check-plugin

一个用于安全检查的 Phan 插件

6.0.0 2024-02-02 01:29 UTC

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-LikelyFalsePositiveSecurityCheck-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 - 如果仅指定了参数中的类型,则将其与输入变量的污点进行位与操作。通常您不希望这样做,但当$TYPEnone时,可以用来指定参数未用于生成返回值。在@return中,这可以用来列举返回值具有哪些污点标志,这通常仅在指定为tainted时有用,表示它具有所有标志。
  • array_ok - 用于表示忽略数组中的污点参数的特殊用途标志。
  • allow_override - 用于指定污点注释应由phan-taint-check在检测到特定污点时覆盖的特殊用途标志。

$TYPE的值可以是以下之一:htmlnoenthtmlsqlshellserializecustom1custom2codepathregexsql_numkeyescapednonetainted。其中大多数是污点类别,除了

  • 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-taintallow_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 很方便)

许可证

GNU 通用公共许可证,版本 2 或更高版本