auth

The Auth Module

1. 概述

这是一个通用模块,它本身不提供身份验证所需的所有功能,但提供所有其他身份验证相关模块(所谓的身份验证后端)所需的功能。

我们决定将身份验证代码分成几个模块,因为现在有多个后端(目前支持数据库身份验证和 Radius)。这允许我们创建单独的包,以便用户可以仅安装和加载所需的功能。这也使我们能够避免二进制包中不必要的依赖关系。

2. 依赖关系

必须在此模块之前加载以下模块:

三、参数

3.1. auth_checks_register(旗帜)

参见参数说明auth_checks_in_dlg

3.2. auth_checks_no_dlg(旗帜)

参见参数说明auth_checks_in_dlg

3.3. auth_checks_in_dlg(旗帜)

这三个模块参数控制在 SIP MD5 摘要身份验证期间将对携带摘要响应的 SIP 消息执行哪些可选完整性检查。auth_check_register 控制对 REGISTER 消息执行的完整性检查, auth_checks_no_dlg控制对没有 To 标头字段或没有 To 标记的 SIP 请求(换句话说,建立对话或外部对话的请求)执行哪些可选完整性检查。 auth_checks_in_dlg控制对对话中的 SIP 请求执行哪些完整性检查,例如 BYE 或 re-INVITE。所有三个参数的默认值都是 0(旧行为,没有额外的检查)。可以对 REGISTER 执行的完整性检查集通常不同于可以对其他 SIP 请求类型执行的完整性检查集,因此我们具有三个独立的模块参数。

如果没有额外的检查,随机数将只能防止过期值。在过期“窗口”中仍然可能存在一些回复攻击。一种可能的解决方法是始终强制 qop 身份验证并始终从授权标头检查 uri,但如果上游代理重写 uri,则这将不起作用,并且它也不适用于许多 UA 实现。

在这种情况下,nonce 值将仅用于保存过期时间(请参阅 参考资料nonce_expire)以及其上的 MD5 和一些秘密(MD5 用于确保没有人篡改 nonce 过期时间)。

当启用额外检查时,随机数将在消息的选定部分(见下文)上包含额外的 MD5 和一些其他秘密。当 UA 尝试重用随机数时,这将用于检查消息的所选部分是否相同,从而保护或严格限制回复攻击。

所有三个参数的可能标志值为:

作为示例,设置auth_checks_register为 3 将检查 callid 或请求 uri 是否从为其生成原始随机数的 REGISTER 消息发生更改(这将允许仅在同一 UA 内且在过期时间内重用随机数)。请注意,启用额外检查将限制 UA 的随机数缓存,需要额外的挑战和往返,但会提供更好的保护。

警告

不要为 REGISTER ( auth_checks_register) 和对话外消息 ( auth_checks_no_dlg) 启用 from 标记检查 (4),除非您确定所有用户代理在受到质询时不会更改 from 标记。当质询的请求不在对话中时,某些用户代理也会更改 callid,因此请避免对不属于对话 ( auth_checks_no_dlg) 的消息启用 callid 检查 (2)。在极少数情况下,还必须对 REGISTER 执行此操作。

secret设置该参数并启用额外检查时,前半部分secret 将用于过期时间 MD5,另一半用于额外检查 MD5,因此请确保您有一个长秘密(建议 32 个字符或更长) )。

例 1.1。设置auth_checks_register模块参数

# 对于 REGISTER 请求,我们散列请求 URI、呼叫 ID 和源 IP
# 请求进入随机数字符串。这确保了生成的凭据
# 不能与另一个注册商、具有另一个源 IP 的用户代理一起使用
# 地址或呼叫 ID。请注意,每次更改 Call-ID 的用户代理
# 如果启用此功能,REGISTER 消息将无法注册。
modparam("auth", "auth_checks_register", 11)
# 用于建立对话的请求(例如原始的INVITE、OPTIONS等)
# 我们对请求 URI 和源 IP 进行哈希处理。散列 Call-ID 和 From 标签需要
# 一些额外的预防措施,因为这些检查可能会导致某些 UA 无法使用。
modparam("auth", "auth_checks_no_dlg", 9)
# 对于对话中请求,例如 re-INVITE,我们可以散列源 IP 和
# 请求 URI 就像前面的例子一样。除此之外我们还可以散列
# Call-ID 和 From 标签,因为它们固定在对话框中并且是
# 保证不改变。此设置有效地限制了使用
# 在单个对话框中为单个用户代理生成凭据。
modparam("auth", "auth_checks_in_dlg", 15)

3.4. qop

如果设置,则启用挑战的_qop_:每个挑战将包含一个_qop_参数。这是推荐的方式,但一些较旧的不符合 rfc3261 的 UA 可能会感到困惑,并且如果qop 启用,可能无法正确进行身份验证。

qop与 一起 启用nonce_count将提供额外的安全性(防止重放攻击),同时仍然允许在 UA 端缓存凭证,因此不会影响性能。

可能的值为:“auth”、“auth-int”和“”(未设置)。

默认值是未设置(“”)。

也可以看看: nonce_count

例 1.2。qop 示例

modparam("auth", "qop", "auth") # 设置 qop=auth

3.5. nonce_count(布尔值)

如果启用,则会记住收到的_nc_值并对照旧值进行检查(为了成功进行身份验证,收到的_nc_必须大于之前收到的值,请参阅 rfc2617 了解更多详细信息)。这将提供针对重放攻击的保护,同时仍然允许在 UA 端缓存凭证。

这取决于qop是否启用(如果 qop未启用,挑战将不包括 qop,因此 UA 可能不会 在其响应中 包括_qop_或_nc参数)。_

如果响应不包含_qop_或 nc(例如不支持它们的过时 UA),将根据其他启用的随机数检查来检查响应,按以下顺序:one_time_nonce 和 auth_check_*。如果响应仅包含_nc_正常 nonce_expire检查并且 nonce_count将执行检查,则将忽略所有其他检查。

检查nonce_count通过跟踪有限数量的随机数来进行。nc_array_size使用或 参数设置跟踪随机数的最大数量nc_array_order。如果超过此数量,旧条目将被覆盖。只要平均响应时间的可质疑消息的最大速率低于 nc_array_sizenonce_count 检查就应该完美地工作。为了获得最佳性能(最大程度地重用缓存凭据),nc_array_size除以 nid_pool_no应低于消息速率乘以所需的值nonce_expire

可接受的最大_nc_值为 255。如果 _nc_变得大于此值,则随机数将被视为过时,并且 UA 将被重新挑战。

注意: nonce_count仅应在有状态模式下启用(应在身份验证检查之前创建事务以吸收可能的重传,并且所有回复应使用有状态发送 t_reply())。如果nonce_count在无状态模式下使用身份验证检查,则所有重传都将受到质疑。

默认值为 0(关闭)。

也可以看看: qop, nc_array_size, nc_array_order, nid_pool_no, nonce_expire。 one_time_nonce

例 1.3。nonce_count 示例

modparam("auth", "nonce_count", 1) # enable nonce_count support
modparam("auth", "qop", "auth")    # enable qop=auth

....
route{
	# go stateful and catch retransmissions
	if (!t_newtran()) {
	    xlog("L_NOTICE", "Failed to create new transaction\n");
 	    drop;
	};
	if (method=="REGISTER"){
		if (!www_authenticate("test", "credentials")){
			# reply must be sent with t_reply because the
			# transaction is already created at this point
			# (we are in "stateful" mode)
			if ($? == -2){
				t_reply("500", "Internal Server Error");
			}else if ($? == -3){
				t_reply("400", "Bad Request");
			}else{
				if ($digest_challenge)
					append_to_reply("%$digest_challenge");
				t_reply("401", "Unauthorized");
			}
			drop;
		}
		if (!save_noreply("location")) {
			t_reply("400", "Invalid REGISTER Request");
			drop;
		}
		append_to_reply("%$contact");
		t_reply("$code", "$reason"); # no %, avps are used directly
		drop;
	}else{
		if (!proxy_authenticate("my_realm", "credentials")){
			if ($? == -2){
				t_reply("500", "Internal Server Error");
			}else if ($? == -3){
				t_reply("400", "Bad Request");
			}else{
				if ($digest_challenge)
					append_to_reply("%$digest_challenge");
				t_reply("401", "Unauthorized");
			}
			drop;
		}
	}

4. 功能

4.1. consume_credentials()

此函数从服务器正在处理的消息中删除先前授权的凭证标头。这意味着下游消息将不包含该服务器使用的凭据。这确保了代理不会泄露有关用于下游元素的凭据的信息,并且消息也会稍微短一些。www_authorize该函数必须在、 proxy_authorize或 www_authenticate后 调用proxy_authenticate

例 1.18。Consumer_credentials 示例

if (www_authenticate("realm", "subscriber")) {
    consume_credentials();
}

4.2. has_credentials(realm)

如果请求具有授权或代理授权标头以及提供的领域,则此函数返回 true。参数可以是带有伪变量的字符串。

例 1.19。Consumer_credentials 示例

if (has_credentials("myrealm")) {
    ...
}

4.3.  www_challenge(realm, flags)

该函数向用户代理发起挑战。它将生成一个包含摘要挑战的 WWW-Authorize 标头字段,并将该标头字段放入服务器正在处理的请求生成的响应中,并发送 401 回复。收到此类答复后,用户代理应计算凭据并重试请求。有关摘要身份验证的更多信息,请参阅 RFC2617。请参阅有关发送回复的模块参数force_stateless_reply。

参数含义如下:

该函数可以从 REQUEST_ROUTE 中使用。

例 1.20。www_challenge 用法

if (!www_authenticate("$td", "subscriber")) {
	www_challenge("$td", "1");
	exit;
}

4.4.  proxy_challenge(realm, flags)

该函数向用户代理发起挑战。它将生成一个包含摘要挑战的 Proxy-Authorize 标头字段,它将标头字段放入服务器正在处理的请求生成的响应中,并发送 407 回复。收到此类答复后,用户代理应计算凭据并重试请求。有关摘要身份验证的更多信息,请参阅 RFC2617。请参阅有关发送回复的模块参数force_stateless_reply。

参数的含义与函数 www_challenge(realm, flags) 相同

该函数可以从 REQUEST_ROUTE 中使用。

例 1.21。proxy_challenge 用法

if (!proxy_authenticate("$fd", "subscriber")) {
	proxy_challenge("$fd", "1");
	exit;
}

4.5.  auth_challenge(realm, flags)

该函数向用户代理发起身份验证挑战。它结合了函数 www_challenge() 和 proxy_challenge(),通过在内部调用第一个函数来处理 REGISTER 请求,并调用第二个函数来处理其余的其他请求类型。换句话说,它通过发送 REGISTER 请求的 401 回复和其他类型的 SIP 请求的 407 回复来挑战身份验证。

参数含义与函数 www_challenge(realm, flags) 相同

该函数可以从 REQUEST_ROUTE 中使用。

例 1.22。auth_challenge 用法

if (!auth_check("$fd", "subscriber", "1")) {
	auth_challenge("$fd", "1");
	exit;
}

4.6.  pv_www_authenticate(realm, passwd, flags [, method])

该函数根据 RFC2617验证凭据。如果凭证验证成功,则该函数将成功并将凭证标记为已授权(标记的凭证稍后可以由其他一些函数使用)。如果该函数由于某种原因无法验证凭据,那么它将失败,并且应该调用脚本,www_challenge这将再次询问用户。

负代码可以解释如下:

参数含义如下:

当质询头被构建并存储在 avp 中时,append_to_reply() 和 sl 回复函数可用于发送适当的 SIP 回复以进行质询以进行身份​​验证。

该函数可以从 REQUEST_ROUTE 中使用。

例 1.23。 pv_www_authenticate 用法

if (!pv_www_authenticate("$td", "123abc", "0")) {
	www_challenge("$td", "1");
	exit;
}

4.7.  pv_proxy_authenticate(realm, passwd, flags)

该函数根据 RFC2617验证凭据。如果凭证验证成功,则该函数将成功并将凭证标记为已授权(标记的凭证稍后可以由其他一些函数使用)。如果该函数由于某种原因无法验证凭据,那么它将失败,并且应该调用脚本, proxy_challenge这将再次询问用户。有关负返回代码的更多信息,请参阅上面的函数。

参数含义与 pv_www_authenticate(realm, passwd, flags) 相同

该函数可以从 REQUEST_ROUTE 中使用。

例 1.24。pv_proxy_authenticate 用法

$avp(password)="xyz";
if (!pv_proxy_authenticate("$fd", "$avp(password)", "0")) {
	proxy_challenge("$fd", "1");
	exit;
}