wikimedia/security-check-plugin

此包已被放弃且不再维护。作者建议使用 mediawiki/phan-taint-check-plugin 包。

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

5.0.0 2023-08-29 15:58 UTC

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-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 - 允许人们自定义污染类型
  • 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 - 如果在参数中仅指定了类型,则它与输入变量的污染进行位与操作。通常您不想这样做,但可以在 $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() 方法。此方法返回一个关联数组,其中包含完全限定的方法名和描述函数返回值污点(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 很方便)

许可

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