由于MIME包体是在”inner”消息中的,实现中通常会加密MIME指定的头域,包括:MIME-Version,Content-Type,Content-Length, Content-Language, Content-Encoding, 和Content-Disposition。”outer”消息会为S/MIME包体使用适当的MIME头域。这些头域(和他们开始的任何MIME包体)在SIP消息中,应当作为普通的MIME头域和包体
接收。
对下边这些头域的加密并不是特别有用: Min-Expires, Timestamp, Authorization, Priority, 和 WWWAuthenticate。这类头域包含了那些能够被proxy服务器所更改的头域(在前边章节有讲述)。如果这些头域不出现在”outer”消息中,那么UA应当不在”inner”消息中包含这些头域。如果UA在加密的包体中接收到这些头域,应当忽略到这些加密的值。
注意,SIP的扩展可能会定义附加的头域;那么这些扩展的作者应当描述这些头域的完整性和机密性特性。如果一个SIP UA遇到了一个不认识的头域,并且产生了一个完整性冲突,它必须忽略掉这个头域。
23.4.2 隧道的完整性和身份认证
通过S/MIME包体的SIP消息隧道可以提供SIP头域的完整性保证,只要发送方把这个包整个打包放在用CMS分离签名的”message/sip” MIME包体中。
假设那个”message/sip”包体包含了最基本的对话要素(最小集合)(To, From, Call-ID, CSEq),并且一个签名的MIME包体可以提供优先的身份认证。在这个特别的最小集合上,如果接受方不认识用于签名MIME包体的信任状,并且不能被检验,那么在同一个信任状拥有者所初始化的会话中,这个信任状拥有者可以稍后发送一个在会话中的请求,包含这个签名来进行确认。如果签名MIME包体的接受方选择信任这个信任书(他们可以检验信任书,他们已经从信任列表中检查了,或者他们经常使用这个信任书),那么这个签名就和这个信任书的主题有着相同的身份一致性。
为了排出可能的对实体包头域增加减少的相关困惑,发送方应当把请求的所有头域放在签名的包体中。任何需要完整性保护的包体都必须添加到”inner”消息中。
如果有一个签名包体的消息中包含一个Date头域,接受方应当比较它自己的内部时钟和这个头域值。如果检测到了时差(比如超过1个小时或者更多),UA应当警告使用者,并且提示这个可能是安全隐患。
如果接受方检测到消息的完整性破坏了,如果是这个消息是请求,那么应当使用403(Forbidden)来拒绝这个请求,或者终止现存的对话。UA应当提示用户这个情况,并且要求明确的操作指示。
下边是一个使用隧道”message/sip”的例子:
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Max-Forwards: 70
Date: Thu, 21 Feb 2002 13:02:03 GMT
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: multipart/signed;
protocol="application/pkcs7-signature";
micalg=sha1; boundary=boundary42
Content-Length: 568
--boundary42
Content-Type: message/sip
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
To: Bob < bob@biloxi.com>
From: Alice < alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Max-Forwards: 70
Date: Thu, 21 Feb 2002 13:02:03 GMT
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 147
v=0
o=UserA 2890844526 2890844526 IN IP4 here.com
s=Session SDP
c=IN IP4 pc33.atlanta.com
t=0 0
m=audio 49172 RTP/AVP 0
a=rtpmap:0 PCMU/8000
--boundary42
Content-Type: application/pkcs7-signature; name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7s;
handling=required
ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6
4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj
n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4
7GhIGfHfYT64VQbnj756
--boundary42-
23.4.3 隧道加密
把”message/sip”MIME包体用CMS EnvelopedData消息S/MIME包体进行加密是值得的,但是在实践中,大部分头域都是用于网络的;而通常使用S/MIME进行的加密都是加密类似SDP的消息体的,而不是消息头的。有一部分头域的信息,比如Subject或者Organization或许需要端到端的安全保证。以后的SIP应用可能会定义其他的头域,这些头域也或许需要端到端的安全保证。
另一个加密头域的可能的应用是选择性匿名。可以构造一个没有个人信息的From头域(比如sip:anonymous@anonymizer.invalid)。不过,第二个From头域包含了真实的请求者的address-of-record信息,并且加密存放在”message/sip”MIME包体中,并且只会在对话的对方节点被看到。
注意如果这个机制用于匿名,那么将接受方将不再用From头域来作为密钥组的索引,并且也不用于从密钥组查询合适的发送方的S/MIME密钥。这个消息必须首先被解密,并且”inner”From头域必须当作一个索引。
为了提供端到端的完整性,加密的”message/sip”MIME包体应当由发送方签名。这个创建了一个包含一个加密包体和一个签名的”multipart/signed” MIME包体,包体类型都是”application/pkcs7-mime”.。
在下边这个例子中,是一个加密和签名的消息,在*括起来的文字是加密的:
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
To: Bob <sip:bob@biloxi.com>
From: Anonymous <sip:anonymous@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Max-Forwards: 70
Date: Thu, 21 Feb 2002 13:02:03 GMT
Contact: <sip:pc33.atlanta.com>
Content-Type: multipart/signed;
protocol="application/pkcs7-signature";
micalg=sha1; boundary=boundary42
Content-Length: 568
--boundary42
Content-Type: application/pkcs7-mime; smime-type=enveloped-data;
name=smime.p7m
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7m
handling=required
Content-Length: 231
******************************************************************************
* Content-Type: message/sip *
* *
* INVITE sip:bob@biloxi.com SIP/2.0 *
* Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8 *
* To: Bob < bob@biloxi.com> *
* From: Alice < alice@atlanta.com>;tag=1928301774 *
* Call-ID: a84b4c76e66710 *
* CSeq: 314159 INVITE *
* Max-Forwards: 70 *
* Date: Thu, 21 Feb 2002 13:02:03 GMT *
* Contact: <sip:alice@pc33.atlanta.com> *
* *
* Content-Type: application/sdp *
* *
* v=0 *
* o=alice 53655765 2353687637 IN IP4 pc33.atlanta.com *
* s=Session SDP *
* t=0 0 *
* c=IN IP4 pc33.atlanta.com *
* m=audio 3456 RTP/AVP 0 1 3 99 *
* a=rtpmap:0 PCMU/8000 *
******************************************************************************
--boundary42
Content-Type: application/pkcs7-signature; name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7s;
handling=required
ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6
4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj
n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4
7GhIGfHfYT64VQbnj756
--boundary42-
24 例子
在下边这个例子中,由于简略介绍的关系我们经常省略消息体和对应的Content-Length和Content-Type头域。
24.1 注册
Bob在启动的时候进行注册。这个消息流在图9中展示。注意对于注册服务来说,通常需要认证,而且不像下边描述的这样简单。
图9: SIP 注册例子
F1 REGISTER Bob -> Registrar
REGISTER sip:registrar.biloxi.com SIP/2.0
Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com>
From: Bob <sip:bob@biloxi.com>;tag=456248
Call-ID: 843817637684230@998sdasdh09
CSeq: 1826 REGISTER
Contact: <sip:bob@192.0.2.4>
Expires: 7200
Content-Length: 0
注册会在2小时后超时。注册服务器回应一个200OK:
F2 200 OK Registar -> Bob
SIP/2.0 200 OK
Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7
;received=192.0.2.4
To: Bob <sip:bob@biloxi.com>;tag=2493k59kd
From: Bob <sip:bob@biloxi.com>;tag=456248
Call-ID: 843817637684230@998sdasdh09
CSeq: 1826 REGISTER
Contact: <sip:bob@192.0.2.4>
Expires: 7200
Content-Length: 0
24.2 建立会话
这个例子包括了4节描述的建立会话的细节。消息流在图1中展示了。注意这些消息流展示了头域的最小集合--一般来说还需要包含一些其他头域,比如Allow和Supported头域。
F1 INVITE Alice -> atlanta.com proxy
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142
(Alice’s SDP not shown)
F2 100 Trying atlanta.com proxy -> Alice
SIP/2.0 100 Trying
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Content-Length: 0
F3 INVITE atlanta.com proxy -> biloxi.com proxy
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
Max-Forwards: 69
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142
(Alice’s SDP not shown)
F4 100 Trying biloxi.com proxy -> atlanta.com proxy
SIP/2.0 100 Trying
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Content-Length: 0
F5 INVITE biloxi.com proxy -> Bob
INVITE sip:bob@192.0.2.4 SIP/2.0
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
Max-Forwards: 68
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142
(Alice’s SDP not shown)
F6 180 Ringing Bob -> biloxi.com proxy
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
;received=192.0.2.3
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
F7 180 Ringing biloxi.com proxy -> atlanta.com proxy
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
F8 180 Ringing atlanta.com proxy -> Alice
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
F9 200 OK Bob -> biloxi.com proxy
SIP/2.0 200 OK
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
;received=192.0.2.3
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob’s SDP not shown)
F10 200 OK biloxi.com proxy -> atlanta.com proxy
SIP/2.0 200 OK
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob’s SDP not shown)
F11 200 OK atlanta.com proxy -> Alice
SIP/2.0 200 OK
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob’s SDP not shown)
F12 ACK Alice -> Bob
ACK sip:bob@192.0.2.4 SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds9
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 ACK
Content-Length: 0
在Alice和Bob之间的媒体会话现在建立了。Bob首先挂机。注意Bob的SIP电话维持它自己的Cseq号码空间,在这里,是231开始的。由于Bob发起请求,那么To和From URI和tags交换了。
F13 BYE Bob -> Alice
BYE sip:alice@pc33.atlanta.com SIP/2.0
Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10
Max-Forwards: 70
From: Bob <sip:bob@biloxi.com>;tag=a6c85cf
To: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 231 BYE
Content-Length: 0
F14 200 OK Alice -> Bob
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10
From: Bob <sip:bob@biloxi.com>;tag=a6c85cf
To: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 231 BYE
Content-Length: 0
这个SIP呼叫流程文档[40]包含了SIP消息的更多例子。
25 SIP协议的BNF范式
本协议中定义的机制都用文本和Backus-Naur Form(BNF)范式定义(RFC2234[10])。6.1节和RFC2234定义了一个本文档使用的基本核心规则,这里就不赘述了。实现者需要熟悉RFC2234协议,这样才能理解整理定义的规范。某些基本规则是用大写字母表示的,比如SP,LWS,HTAB,CRLF,DIGIT,ALPHA,等等。尖括号定义了规则的名字。
方括号的使用是在语法上可选的。在这里用于特定参数是可选的语义提示。
25.1 基本规则
下列贯穿本规范的规则是用于描述基本的语法结构。US-ASCII码字符集是在ANSI X3.4-1986中定义的。
alphanum = ALPHA/DIGIT
部分规则是和RFC2396[5]中合并使用的,但是依据RFC2234[10]做了更新,这些包括:
reserved = ";" / "/" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","
unreserved = alphanum / mark
mark = "-" / "_" / "." / "!" / "˜" / "*" / "’" / "(" / ")"
escaped = "%" HEXDIG HEXDIG
SIP头域值可以折叠成为多行,如果每行的开始是一个空格或者一个水平制表符(就是Tab键啦)。所有的线形空白,包含折叠的空白,和SP有着同样的语义(SP就是空格啦)。一个接受方在处理头域值之前或者转发消息到下行队列之前,可以把任何线形空白当作一个单个SP处理。这个和RFC2616[8]中描述的HTTP/1.1处理方法完全一样。当线形空白是可选的时候,SWS构造就需要了,通常在两个符号和分隔符之间存在:
LWS = [*WSP CRLF] 1*WSP ; linear whitespace
SWS = [LWS] ; sep whitespace
为了把头域名和头域值分开,就需要使用冒号,根据上边的规则,允许在冒号之前有空白,但是不允许有行分隔符,并且允许在冒号之后有空白,或者行分隔符。HCOLON有如下定义:
HCOLON = *( SP / HTAB ) ":" SWS
TEXT-UTF8规则只用于描述不被消息分析所分析的域内容和域值。*TEXT-UTF8包含了UTF-8字符集的字符(RFC2279[7])。TEXT-UTF8-TRIM规则是用于描述一个n t引号引起来的字符串,其前后的空白是无意义的。在这里,SIP和HTTP不同,HTTP使用的是ISO 8859-1字符集。
TEXT-UTF8-TRIM = 1*TEXT-UTF8char *(*LWS TEXT-UTF8char)
TEXT-UTF8char = %x21-7E / UTF8-NONASCII
UTF8-NONASCII = %xC0-DF 1UTF8-CONT
/ %xE0-EF 2UTF8-CONT
/ %xF0-F7 3UTF8-CONT
/ %xF8-Fb 4UTF8-CONT
/ %xFC-FD 5UTF8-CONT
UTF8-CONT = %x80-BF
在TEXT-UTF8-TRIM的定义中,CRLF只允许作为头域的延长部分存在。当LWS(空格)折叠的时候,在分析TEXTUTF8-TRIM之前,会使用单个SP代替。
部分协议要素使用了十六进制数字字符。有一些要素(authentication)强制十六进制数字使用小写字母。
LHEX = DIGIT / %x61-66 ;lowercase a-f
许多SIP头域值都包含用LWS或者特殊符号分开的词。除非有额外的说明,这些符号是大小写不铭感的。当特殊字符作为参数值存在的时候,这些特殊字符必须通过引号引起来。Call-ID中建立的词组允许使用绝大部分分隔符。
token = 1 * ( alphanum / "-" / "." / "!" / "%" / "*"
/ "_" / "+" / "‘" / "’" / "˜" )
separators = "(" / ")" / "<" / ">" / "@" /
"," / ";" / ":" / "/" / DQUOTE /
"/" / "[" / "]" / "?" / "=" /
"{" / "}" / SP / HTAB
word = 1*(alphanum / "-" / "." / "!" / "%" / "*" /
"_" / "+" / "‘" / "’" / "˜" /
"(" / ")" / "<" / ">" /
":" / "/" / DQUOTE /
"/" / "[" / "]" / "?" /
"{" / "}" )
当标志符号或者分隔符用在要素之间是,空白通常允许在这些字符之前或者之后。
STAR = SWS "*" SWS ; asterisk
SLASH = SWS "/" SWS ; slash
EQUAL = SWS "=" SWS ; equal
LPAREN = SWS "(" SWS ; left parenthesis
RPAREN = SWS ")" SWS ; right parenthesis
RAQUOT = ">" SWS ; right angle quote
LAQUOT = SWS "<"; left angle quote
COMMA = SWS "," SWS ; comma
SEMI = SWS ";" SWS ; semicolon
COLON = SWS ":" SWS ; colon
LDQUOT = SWS DQUOTE; open double quotation mark
RDQUOT = DQUOTE SWS ; close double quotation mark
在SIP头域中可以使用注释,通过把注释放在圆括号中就可以了。只有在头域的定义中允许”comment”作为他们的头域值的一部分才可以使用注释。在其他头域中,圆括号被视同为头域值的一部分。
comment = LPAREN *(ctext / quoted-pair / comment) RPAREN
ctext = %x21-27 / %x2A-5B / %x5D-7E / UTF8-NONASCII
/ LWS
ctext包含了除了左右括号和反斜线之外的所有的字符。在双引号引起来的字符串中的字串,被视为单个词。在引起来的字串中,引号(“)和反斜线需要转码。
quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
qdtext = LWS / %x21 / %x23-5B / %x5D-7E
/ UTF8-NONASCII
反斜线(“/”)可以当作单个字符使用,转义机制只有在引号引起来的字符串中或者注释结构中有效。和HTTP/1.1不同的是,CR和LF不能通过这个机制来转义,这样可以避免同头的折叠的冲突。
quoted-pair = "/" (%x00-09 / %x0B-0C
/ %x0E-7F)
SIP-URI = "sip:" [ userinfo ] hostport
uri-parameters [ headers ]
SIPS-URI = "sips:" [ userinfo ] hostport
uri-parameters [ headers ]
userinfo = ( user / telephone-subscriber ) [ ":" password ] "@"
user = 1*( unreserved / escaped / user-unreserved )
user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
password = *( unreserved / escaped /
"&" / "=" / "+" / "$" / "," )
hostport = host [ ":" port ]
host = hostname / IPv4address / IPv6reference
hostname = *( domainlabel "." ) toplabel [ "." ]
domainlabel = alphanum
/ alphanum *( alphanum / "-" ) alphanum
toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
IPv6reference = "[" IPv6address "]"
IPv6address = hexpart [ ":" IPv4address ]
hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
hexseq = hex4 *( ":" hex4)
对下边这些头域的加密并不是特别有用: Min-Expires, Timestamp, Authorization, Priority, 和 WWWAuthenticate。这类头域包含了那些能够被proxy服务器所更改的头域(在前边章节有讲述)。如果这些头域不出现在”outer”消息中,那么UA应当不在”inner”消息中包含这些头域。如果UA在加密的包体中接收到这些头域,应当忽略到这些加密的值。
注意,SIP的扩展可能会定义附加的头域;那么这些扩展的作者应当描述这些头域的完整性和机密性特性。如果一个SIP UA遇到了一个不认识的头域,并且产生了一个完整性冲突,它必须忽略掉这个头域。
23.4.2 隧道的完整性和身份认证
通过S/MIME包体的SIP消息隧道可以提供SIP头域的完整性保证,只要发送方把这个包整个打包放在用CMS分离签名的”message/sip” MIME包体中。
假设那个”message/sip”包体包含了最基本的对话要素(最小集合)(To, From, Call-ID, CSEq),并且一个签名的MIME包体可以提供优先的身份认证。在这个特别的最小集合上,如果接受方不认识用于签名MIME包体的信任状,并且不能被检验,那么在同一个信任状拥有者所初始化的会话中,这个信任状拥有者可以稍后发送一个在会话中的请求,包含这个签名来进行确认。如果签名MIME包体的接受方选择信任这个信任书(他们可以检验信任书,他们已经从信任列表中检查了,或者他们经常使用这个信任书),那么这个签名就和这个信任书的主题有着相同的身份一致性。
为了排出可能的对实体包头域增加减少的相关困惑,发送方应当把请求的所有头域放在签名的包体中。任何需要完整性保护的包体都必须添加到”inner”消息中。
如果有一个签名包体的消息中包含一个Date头域,接受方应当比较它自己的内部时钟和这个头域值。如果检测到了时差(比如超过1个小时或者更多),UA应当警告使用者,并且提示这个可能是安全隐患。
如果接受方检测到消息的完整性破坏了,如果是这个消息是请求,那么应当使用403(Forbidden)来拒绝这个请求,或者终止现存的对话。UA应当提示用户这个情况,并且要求明确的操作指示。
下边是一个使用隧道”message/sip”的例子:
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Max-Forwards: 70
Date: Thu, 21 Feb 2002 13:02:03 GMT
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: multipart/signed;
protocol="application/pkcs7-signature";
micalg=sha1; boundary=boundary42
Content-Length: 568
--boundary42
Content-Type: message/sip
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
To: Bob < bob@biloxi.com>
From: Alice < alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Max-Forwards: 70
Date: Thu, 21 Feb 2002 13:02:03 GMT
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 147
v=0
o=UserA 2890844526 2890844526 IN IP4 here.com
s=Session SDP
c=IN IP4 pc33.atlanta.com
t=0 0
m=audio 49172 RTP/AVP 0
a=rtpmap:0 PCMU/8000
--boundary42
Content-Type: application/pkcs7-signature; name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7s;
handling=required
ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6
4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj
n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4
7GhIGfHfYT64VQbnj756
--boundary42-
23.4.3 隧道加密
把”message/sip”MIME包体用CMS EnvelopedData消息S/MIME包体进行加密是值得的,但是在实践中,大部分头域都是用于网络的;而通常使用S/MIME进行的加密都是加密类似SDP的消息体的,而不是消息头的。有一部分头域的信息,比如Subject或者Organization或许需要端到端的安全保证。以后的SIP应用可能会定义其他的头域,这些头域也或许需要端到端的安全保证。
另一个加密头域的可能的应用是选择性匿名。可以构造一个没有个人信息的From头域(比如sip:anonymous@anonymizer.invalid)。不过,第二个From头域包含了真实的请求者的address-of-record信息,并且加密存放在”message/sip”MIME包体中,并且只会在对话的对方节点被看到。
注意如果这个机制用于匿名,那么将接受方将不再用From头域来作为密钥组的索引,并且也不用于从密钥组查询合适的发送方的S/MIME密钥。这个消息必须首先被解密,并且”inner”From头域必须当作一个索引。
为了提供端到端的完整性,加密的”message/sip”MIME包体应当由发送方签名。这个创建了一个包含一个加密包体和一个签名的”multipart/signed” MIME包体,包体类型都是”application/pkcs7-mime”.。
在下边这个例子中,是一个加密和签名的消息,在*括起来的文字是加密的:
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
To: Bob <sip:bob@biloxi.com>
From: Anonymous <sip:anonymous@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Max-Forwards: 70
Date: Thu, 21 Feb 2002 13:02:03 GMT
Contact: <sip:pc33.atlanta.com>
Content-Type: multipart/signed;
protocol="application/pkcs7-signature";
micalg=sha1; boundary=boundary42
Content-Length: 568
--boundary42
Content-Type: application/pkcs7-mime; smime-type=enveloped-data;
name=smime.p7m
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7m
handling=required
Content-Length: 231
******************************************************************************
* Content-Type: message/sip *
* *
* INVITE sip:bob@biloxi.com SIP/2.0 *
* Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8 *
* To: Bob < bob@biloxi.com> *
* From: Alice < alice@atlanta.com>;tag=1928301774 *
* Call-ID: a84b4c76e66710 *
* CSeq: 314159 INVITE *
* Max-Forwards: 70 *
* Date: Thu, 21 Feb 2002 13:02:03 GMT *
* Contact: <sip:alice@pc33.atlanta.com> *
* *
* Content-Type: application/sdp *
* *
* v=0 *
* o=alice 53655765 2353687637 IN IP4 pc33.atlanta.com *
* s=Session SDP *
* t=0 0 *
* c=IN IP4 pc33.atlanta.com *
* m=audio 3456 RTP/AVP 0 1 3 99 *
* a=rtpmap:0 PCMU/8000 *
******************************************************************************
--boundary42
Content-Type: application/pkcs7-signature; name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7s;
handling=required
ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6
4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj
n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4
7GhIGfHfYT64VQbnj756
--boundary42-
24 例子
在下边这个例子中,由于简略介绍的关系我们经常省略消息体和对应的Content-Length和Content-Type头域。
24.1 注册
Bob在启动的时候进行注册。这个消息流在图9中展示。注意对于注册服务来说,通常需要认证,而且不像下边描述的这样简单。
图9: SIP 注册例子
F1 REGISTER Bob -> Registrar
REGISTER sip:registrar.biloxi.com SIP/2.0
Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com>
From: Bob <sip:bob@biloxi.com>;tag=456248
Call-ID: 843817637684230@998sdasdh09
CSeq: 1826 REGISTER
Contact: <sip:bob@192.0.2.4>
Expires: 7200
Content-Length: 0
注册会在2小时后超时。注册服务器回应一个200OK:
F2 200 OK Registar -> Bob
SIP/2.0 200 OK
Via: SIP/2.0/UDP bobspc.biloxi.com:5060;branch=z9hG4bKnashds7
;received=192.0.2.4
To: Bob <sip:bob@biloxi.com>;tag=2493k59kd
From: Bob <sip:bob@biloxi.com>;tag=456248
Call-ID: 843817637684230@998sdasdh09
CSeq: 1826 REGISTER
Contact: <sip:bob@192.0.2.4>
Expires: 7200
Content-Length: 0
24.2 建立会话
这个例子包括了4节描述的建立会话的细节。消息流在图1中展示了。注意这些消息流展示了头域的最小集合--一般来说还需要包含一些其他头域,比如Allow和Supported头域。
F1 INVITE Alice -> atlanta.com proxy
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142
(Alice’s SDP not shown)
F2 100 Trying atlanta.com proxy -> Alice
SIP/2.0 100 Trying
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Content-Length: 0
F3 INVITE atlanta.com proxy -> biloxi.com proxy
INVITE sip:bob@biloxi.com SIP/2.0
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
Max-Forwards: 69
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142
(Alice’s SDP not shown)
F4 100 Trying biloxi.com proxy -> atlanta.com proxy
SIP/2.0 100 Trying
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Content-Length: 0
F5 INVITE biloxi.com proxy -> Bob
INVITE sip:bob@192.0.2.4 SIP/2.0
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
Max-Forwards: 68
To: Bob <sip:bob@biloxi.com>
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:alice@pc33.atlanta.com>
Content-Type: application/sdp
Content-Length: 142
(Alice’s SDP not shown)
F6 180 Ringing Bob -> biloxi.com proxy
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
;received=192.0.2.3
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
F7 180 Ringing biloxi.com proxy -> atlanta.com proxy
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
F8 180 Ringing atlanta.com proxy -> Alice
SIP/2.0 180 Ringing
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
Contact: <sip:bob@192.0.2.4>
CSeq: 314159 INVITE
Content-Length: 0
F9 200 OK Bob -> biloxi.com proxy
SIP/2.0 200 OK
Via: SIP/2.0/UDP server10.biloxi.com;branch=z9hG4bK4b43c2ff8.1
;received=192.0.2.3
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob’s SDP not shown)
F10 200 OK biloxi.com proxy -> atlanta.com proxy
SIP/2.0 200 OK
Via: SIP/2.0/UDP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1
;received=192.0.2.2
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob’s SDP not shown)
F11 200 OK atlanta.com proxy -> Alice
SIP/2.0 200 OK
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8
;received=192.0.2.1
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 INVITE
Contact: <sip:bob@192.0.2.4>
Content-Type: application/sdp
Content-Length: 131
(Bob’s SDP not shown)
F12 ACK Alice -> Bob
ACK sip:bob@192.0.2.4 SIP/2.0
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds9
Max-Forwards: 70
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf
From: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 314159 ACK
Content-Length: 0
在Alice和Bob之间的媒体会话现在建立了。Bob首先挂机。注意Bob的SIP电话维持它自己的Cseq号码空间,在这里,是231开始的。由于Bob发起请求,那么To和From URI和tags交换了。
F13 BYE Bob -> Alice
BYE sip:alice@pc33.atlanta.com SIP/2.0
Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10
Max-Forwards: 70
From: Bob <sip:bob@biloxi.com>;tag=a6c85cf
To: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 231 BYE
Content-Length: 0
F14 200 OK Alice -> Bob
SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.0.2.4;branch=z9hG4bKnashds10
From: Bob <sip:bob@biloxi.com>;tag=a6c85cf
To: Alice <sip:alice@atlanta.com>;tag=1928301774
Call-ID: a84b4c76e66710
CSeq: 231 BYE
Content-Length: 0
这个SIP呼叫流程文档[40]包含了SIP消息的更多例子。
25 SIP协议的BNF范式
本协议中定义的机制都用文本和Backus-Naur Form(BNF)范式定义(RFC2234[10])。6.1节和RFC2234定义了一个本文档使用的基本核心规则,这里就不赘述了。实现者需要熟悉RFC2234协议,这样才能理解整理定义的规范。某些基本规则是用大写字母表示的,比如SP,LWS,HTAB,CRLF,DIGIT,ALPHA,等等。尖括号定义了规则的名字。
方括号的使用是在语法上可选的。在这里用于特定参数是可选的语义提示。
25.1 基本规则
下列贯穿本规范的规则是用于描述基本的语法结构。US-ASCII码字符集是在ANSI X3.4-1986中定义的。
alphanum = ALPHA/DIGIT
部分规则是和RFC2396[5]中合并使用的,但是依据RFC2234[10]做了更新,这些包括:
reserved = ";" / "/" / "?" / ":" / "@" / "&" / "=" / "+" / "$" / ","
unreserved = alphanum / mark
mark = "-" / "_" / "." / "!" / "˜" / "*" / "’" / "(" / ")"
escaped = "%" HEXDIG HEXDIG
SIP头域值可以折叠成为多行,如果每行的开始是一个空格或者一个水平制表符(就是Tab键啦)。所有的线形空白,包含折叠的空白,和SP有着同样的语义(SP就是空格啦)。一个接受方在处理头域值之前或者转发消息到下行队列之前,可以把任何线形空白当作一个单个SP处理。这个和RFC2616[8]中描述的HTTP/1.1处理方法完全一样。当线形空白是可选的时候,SWS构造就需要了,通常在两个符号和分隔符之间存在:
LWS = [*WSP CRLF] 1*WSP ; linear whitespace
SWS = [LWS] ; sep whitespace
为了把头域名和头域值分开,就需要使用冒号,根据上边的规则,允许在冒号之前有空白,但是不允许有行分隔符,并且允许在冒号之后有空白,或者行分隔符。HCOLON有如下定义:
HCOLON = *( SP / HTAB ) ":" SWS
TEXT-UTF8规则只用于描述不被消息分析所分析的域内容和域值。*TEXT-UTF8包含了UTF-8字符集的字符(RFC2279[7])。TEXT-UTF8-TRIM规则是用于描述一个n t引号引起来的字符串,其前后的空白是无意义的。在这里,SIP和HTTP不同,HTTP使用的是ISO 8859-1字符集。
TEXT-UTF8-TRIM = 1*TEXT-UTF8char *(*LWS TEXT-UTF8char)
TEXT-UTF8char = %x21-7E / UTF8-NONASCII
UTF8-NONASCII = %xC0-DF 1UTF8-CONT
/ %xE0-EF 2UTF8-CONT
/ %xF0-F7 3UTF8-CONT
/ %xF8-Fb 4UTF8-CONT
/ %xFC-FD 5UTF8-CONT
UTF8-CONT = %x80-BF
在TEXT-UTF8-TRIM的定义中,CRLF只允许作为头域的延长部分存在。当LWS(空格)折叠的时候,在分析TEXTUTF8-TRIM之前,会使用单个SP代替。
部分协议要素使用了十六进制数字字符。有一些要素(authentication)强制十六进制数字使用小写字母。
LHEX = DIGIT / %x61-66 ;lowercase a-f
许多SIP头域值都包含用LWS或者特殊符号分开的词。除非有额外的说明,这些符号是大小写不铭感的。当特殊字符作为参数值存在的时候,这些特殊字符必须通过引号引起来。Call-ID中建立的词组允许使用绝大部分分隔符。
token = 1 * ( alphanum / "-" / "." / "!" / "%" / "*"
/ "_" / "+" / "‘" / "’" / "˜" )
separators = "(" / ")" / "<" / ">" / "@" /
"," / ";" / ":" / "/" / DQUOTE /
"/" / "[" / "]" / "?" / "=" /
"{" / "}" / SP / HTAB
word = 1*(alphanum / "-" / "." / "!" / "%" / "*" /
"_" / "+" / "‘" / "’" / "˜" /
"(" / ")" / "<" / ">" /
":" / "/" / DQUOTE /
"/" / "[" / "]" / "?" /
"{" / "}" )
当标志符号或者分隔符用在要素之间是,空白通常允许在这些字符之前或者之后。
STAR = SWS "*" SWS ; asterisk
SLASH = SWS "/" SWS ; slash
EQUAL = SWS "=" SWS ; equal
LPAREN = SWS "(" SWS ; left parenthesis
RPAREN = SWS ")" SWS ; right parenthesis
RAQUOT = ">" SWS ; right angle quote
LAQUOT = SWS "<"; left angle quote
COMMA = SWS "," SWS ; comma
SEMI = SWS ";" SWS ; semicolon
COLON = SWS ":" SWS ; colon
LDQUOT = SWS DQUOTE; open double quotation mark
RDQUOT = DQUOTE SWS ; close double quotation mark
在SIP头域中可以使用注释,通过把注释放在圆括号中就可以了。只有在头域的定义中允许”comment”作为他们的头域值的一部分才可以使用注释。在其他头域中,圆括号被视同为头域值的一部分。
comment = LPAREN *(ctext / quoted-pair / comment) RPAREN
ctext = %x21-27 / %x2A-5B / %x5D-7E / UTF8-NONASCII
/ LWS
ctext包含了除了左右括号和反斜线之外的所有的字符。在双引号引起来的字符串中的字串,被视为单个词。在引起来的字串中,引号(“)和反斜线需要转码。
quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
qdtext = LWS / %x21 / %x23-5B / %x5D-7E
/ UTF8-NONASCII
反斜线(“/”)可以当作单个字符使用,转义机制只有在引号引起来的字符串中或者注释结构中有效。和HTTP/1.1不同的是,CR和LF不能通过这个机制来转义,这样可以避免同头的折叠的冲突。
quoted-pair = "/" (%x00-09 / %x0B-0C
/ %x0E-7F)
SIP-URI = "sip:" [ userinfo ] hostport
uri-parameters [ headers ]
SIPS-URI = "sips:" [ userinfo ] hostport
uri-parameters [ headers ]
userinfo = ( user / telephone-subscriber ) [ ":" password ] "@"
user = 1*( unreserved / escaped / user-unreserved )
user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
password = *( unreserved / escaped /
"&" / "=" / "+" / "$" / "," )
hostport = host [ ":" port ]
host = hostname / IPv4address / IPv6reference
hostname = *( domainlabel "." ) toplabel [ "." ]
domainlabel = alphanum
/ alphanum *( alphanum / "-" ) alphanum
toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
IPv6reference = "[" IPv6address "]"
IPv6address = hexpart [ ":" IPv4address ]
hexpart = hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
hexseq = hex4 *( ":" hex4)
hex4 = 1*4HEXDIG
port = 1*DIGIT
对于电话描述(telephone-subscriber)的BNF说明在RFC2806[9]中。注意,无论如何,如果在这里允许的字符,如果在SIP URI中的user部分不许可,那么就一定要用转义。
uri-parameters = *( ";" uri-parameter)
uri-parameter = transport-param / user-param / method-param
/ ttl-param / maddr-param / lr-param / other-param
transport-param = "transport="
( "udp" / "tcp" / "sctp" / "tls"
/ other-transport)
other-transport = token
user-param = "user=" ( "phone" / "ip" / other-user)
other-user = token
method-param = "method=" Method
ttl-param = "ttl=" ttl
maddr-param = "maddr=" host
lr-param = "lr"
other-param = pname [ "=" pvalue ]
pname = 1*paramchar
pvalue = 1*paramchar
paramchar = param-unreserved / unreserved / escaped
param-unreserved = "[" / "]" / "/" / ":" / "&" / "+" / "$"
headers = "?" header *( "&" header )
header = hname "=" hvalue
hname = 1*( hnv-unreserved / unreserved / escaped )
hvalue = *( hnv-unreserved / unreserved / escaped )
hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$"
SIP-message = Request / Response
Request = Request-Line
*( message-header )
CRLF
[ message-body ]
Request-Line = Method SP Request-URI SP SIP-Version CRLF
Request-URI = SIP-URI / SIPS-URI / absoluteURI
absoluteURI = scheme ":" ( hier-part / opaque-part )
hier-part = ( net-path / abs-path ) [ "?" query ]
net-path = "//" authority [ abs-path ]
abs-path = "/" path-segments
opaque-part = uric-no-slash *uric
uric = reserved / unreserved / escaped
uric-no-slash = unreserved / escaped / ";" / "?" / ":" / "@"
/ "&" / "=" / "+" / "$" / ","
path-segments = segment *( "/" segment )
segment = *pchar *( ";" param )
param = *pchar
pchar = unreserved / escaped /
":" / "@" / "&" / "=" / "+" / "$" / ","
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
authority = srvr / reg-name
srvr = [ [ userinfo "@" ] hostport ]
reg-name = 1*( unreserved / escaped / "$" / ","
/ ";" / ":" / "@" / "&" / "=" / "+" )
query = *uric
SIP-Version = "SIP" "/" 1*DIGIT "." 1*DIGIT
message-header = (Accept
/ Accept-Encoding
/ Accept-Language
/ Alert-Info
/ Allow
/ Authentication-Info
/ Authorization
/ Call-ID
/ Call-Info
/ Contact
/ Content-Disposition
/ Content-Encoding
/ Content-Language
/ Content-Length
/ Content-Type
/ CSeq
/ Date
/ Error-Info
/ Expires
/ From
/ In-Reply-To
/ Max-Forwards
/ MIME-Version
/ Min-Expires
/ Organization
/ Priority
/ Proxy-Authenticate
/ Proxy-Authorization
/ Proxy-Require
/ Record-Route
/ Reply-To
/ Require
/ Retry-After
/ Route
/ Server
/ Subject
/ Supported
/ Timestamp
/ To
/ Unsupported
/ User-Agent
/ Via
/ Warning
/ WWW-Authenticate
/ extension-header) CRLF
INVITEm = %x49.4E.56.49.54.45 ; INVITE in caps
ACKm = %x41.43.4B ; ACK in caps
OPTIONSm = %x4F.50.54.49.4F.4E.53 ; OPTIONS in caps
BYEm = %x42.59.45 ; BYE in caps
CANCELm = %x43.41.4E.43.45.4C ; CANCEL in caps
REGISTERm = %x52.45.47.49.53.54.45.52 ; REGISTER in caps
Method = INVITEm / ACKm / OPTIONSm / BYEm
/ CANCELm / REGISTERm
/ extension-method
extension-method = token
Response = Status-Line
*( message-header )
CRLF
[ message-body ]
Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF
Status-Code = Informational
/ Redirection
/ Success
/ Client-Error
/ Server-Error
/ Global-Failure
/ extension-code
extension-code = 3DIGIT
Reason-Phrase = *(reserved / unreserved / escaped
/ UTF8-NONASCII / UTF8-CONT / SP / HTAB)
Informational = "100" ; Trying
/ "180" ; Ringing
/ "181" ; Call Is Being Forwarded
/ "182" ; Queued
/ "183" ; Session Progress
Success = "200" ; OK
Redirection = "300" ; Multiple Choices
/ "301" ; Moved Permanently
/ "302" ; Moved Temporarily
/ "305" ; Use Proxy
/ "380" ; Alternative Service
Client-Error = "400" ; Bad Request
/ "401" ; Unauthorized
/ "402" ; Payment Required
/ "403" ; Forbidden
/ "404" ; Not Found
/ "405" ; Method Not Allowed
/ "406" ; Not Acceptable
/ "407" ; Proxy Authentication Required
/ "408" ; Request Timeout
/ "410" ; Gone
/ "413" ; Request Entity Too Large
/ "414" ; Request-URI Too Large
/ "415" ; Unsupported Media Type
/ "416" ; Unsupported URI Scheme
/ "420" ; Bad Extension
/ "421" ; Extension Required
/ "423" ; Interval Too Brief
/ "480" ; Temporarily not available
/ "481" ; Call Leg/Transaction Does Not Exist
/ "482" ; Loop Detected
/ "483" ; Too Many Hops
/ "484" ; Address Incomplete
/ "485" ; Ambiguous
/ "486" ; Busy Here
/ "487" ; Request Terminated
/ "488" ; Not Acceptable Here
/ "491" ; Request Pending
/ "493" ; Undecipherable
Server-Error = "500" ; Internal Server Error
/ "501" ; Not Implemented
/ "502" ; Bad Gateway
/ "503" ; Service Unavailable
/ "504" ; Server Time-out
/ "505" ; SIP Version not supported
/ "513" ; Message Too Large
Global-Failure = "600" ; Busy Everywhere
/ "603" ; Decline
/ "604" ; Does not exist anywhere
/ "606" ; Not Acceptable
Accept = "Accept" HCOLON
[ accept-range *(COMMA accept-range) ]
accept-range = media-range *(SEMI accept-param)
media-range = ( "*/*"
/ ( m-type SLASH "*" )
/ ( m-type SLASH m-subtype )
) *( SEMI m-parameter )
accept-param = ("q" EQUAL qvalue) / generic-param
qvalue = ( "0" [ "." 0*3DIGIT ] )
/ ( "1" [ "." 0*3("0") ] )
generic-param = token [ EQUAL gen-value ]
gen-value = token / host / quoted-string
Accept-Encoding = "Accept-Encoding" HCOLON
[ encoding *(COMMA encoding) ]
encoding = codings *(SEMI accept-param)
codings = content-coding / "*"
content-coding = token
Accept-Language = "Accept-Language" HCOLON
[ language *(COMMA language) ]
language = language-range *(SEMI accept-param)
language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) / "*" )
Alert-Info = "Alert-Info" HCOLON alert-param *(COMMA alert-param)
alert-param = LAQUOT absoluteURI RAQUOT *( SEMI generic-param )
Allow = "Allow" HCOLON [Method *(COMMA Method)]
Authorization = "Authorization" HCOLON credentials
credentials = ("Digest" LWS digest-response)
/ other-response
digest-response = dig-resp *(COMMA dig-resp)
dig-resp = username / realm / nonce / digest-uri
/ dresponse / algorithm / cnonce
/ opaque / message-qop
/ nonce-count / auth-param
username = "username" EQUAL username-value
username-value = quoted-string
digest-uri = "uri" EQUAL LDQUOT digest-uri-value RDQUOT
digest-uri-value = rquest-uri ; Equal to request-uri as specified
by HTTP/1.1
message-qop = "qop" EQUAL qop-value
cnonce = "cnonce" EQUAL cnonce-value
cnonce-value = nonce-value
nonce-count = "nc" EQUAL nc-value
nc-value = 8LHEX
dresponse = "response" EQUAL request-digest
request-digest = LDQUOT 32LHEX RDQUOT
auth-param = auth-param-name EQUAL
( token / quoted-string )
auth-param-name = token
other-response = auth-scheme LWS auth-param
*(COMMA auth-param)
auth-scheme = token
Authentication-Info = "Authentication-Info" HCOLON ainfo
*(COMMA ainfo)
ainfo = nextnonce / message-qop
/ response-auth / cnonce
/ nonce-count
nextnonce = "nextnonce" EQUAL nonce-value
response-auth = "rspauth" EQUAL response-digest
response-digest = LDQUOT *LHEX RDQUOT
Call-ID = ( "Call-ID" / "i" ) HCOLON callid
callid = word [ "@" word ]
Call-Info = "Call-Info" HCOLON info *(COMMA info)
info = LAQUOT absoluteURI RAQUOT *( SEMI info-param)
info-param = ( "purpose" EQUAL ( "icon" / "info"
/ "card" / token ) ) / generic-param
Contact = ("Contact" / "m" ) HCOLON
( STAR / (contact-param *(COMMA contact-param)))
contact-param = (name-addr / addr-spec) *(SEMI contact-params)
name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
addr-spec = SIP-URI / SIPS-URI / absoluteURI
display-name = *(token LWS)/ quoted-string
contact-params = c-p-q / c-p-expires
/ contact-extension
c-p-q = "q" EQUAL qvalue
c-p-expires = "expires" EQUAL delta-seconds
contact-extension = generic-param
delta-seconds = 1*DIGIT
Content-Disposition = "Content-Disposition" HCOLON
disp-type *( SEMI disp-param )
disp-type = "render" / "session" / "icon" / "alert"
/ disp-extension-token
disp-param = handling-param / generic-param
handling-param = "handling" EQUAL
( "optional" / "required"
/ other-handling )
other-handling = token
disp-extension-token = token
Content-Encoding = ( "Content-Encoding" / "e" ) HCOLON
content-coding *(COMMA content-coding)
Content-Language = "Content-Language" HCOLON
language-tag *(COMMA language-tag)
language-tag = primary-tag *( "-" subtag )
primary-tag = 1*8ALPHA
subtag = 1*8ALPHA
Content-Length = ( "Content-Length" / "l" ) HCOLON 1*DIGIT
Content-Type = ( "Content-Type" / "c" ) HCOLON media-type
media-type = m-type SLASH m-subtype *(SEMI m-parameter)
m-type = discrete-type / composite-type
discrete-type = "text" / "image" / "audio" / "video"
/ "application" / extension-token
composite-type = "message" / "multipart" / extension-token
extension-token = ietf-token / x-token
ietf-token = token
x-token = "x-" token
m-subtype = extension-token / iana-token
iana-token = token
m-parameter = m-attribute EQUAL m-value
m-attribute = token
m-value = token / quoted-string
CSeq = "CSeq" HCOLON 1*DIGIT LWS Method
Date = "Date" HCOLON SIP-date
SIP-date = rfc1123-date
rfc1123-date = wkday "," SP date1 SP time SP "GMT"
date1 = 2DIGIT SP month SP 4DIGIT
; day month year (e.g., 02 Jun 1982)
time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
; 00:00:00 - 23:59:59
wkday = "Mon" / "Tue" / "Wed"
/ "Thu" / "Fri" / "Sat" / "Sun"
month = "Jan" / "Feb" / "Mar" / "Apr"
/ "May" / "Jun" / "Jul" / "Aug"
/ "Sep" / "Oct" / "Nov" / "Dec"
Error-Info = "Error-Info" HCOLON error-uri *(COMMA error-uri)
error-uri = LAQUOT absoluteURI RAQUOT *( SEMI generic-param )
Expires = "Expires" HCOLON delta-seconds
From = ( "From" / "f" ) HCOLON from-spec
from-spec = ( name-addr / addr-spec )
*( SEMI from-param )
from-param = tag-param / generic-param
tag-param = "tag" EQUAL token
In-Reply-To = "In-Reply-To" HCOLON callid *(COMMA callid)
Max-Forwards = "Max-Forwards" HCOLON 1*DIGIT
MIME-Version = "MIME-Version" HCOLON 1*DIGIT "." 1*DIGIT
Min-Expires = "Min-Expires" HCOLON delta-seconds
Organization = "Organization" HCOLON [TEXT-UTF8-TRIM]
Priority = "Priority" HCOLON priority-value
priority-value = "emergency" / "urgent" / "normal"
/ "non-urgent" / other-priority
other-priority = token
Proxy-Authenticate = "Proxy-Authenticate" HCOLON challenge
challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
/ other-challenge
other-challenge = auth-scheme LWS auth-param
*(COMMA auth-param)
digest-cln = realm / domain / nonce
/ opaque / stale / algorithm
/ qop-options / auth-param
realm = "realm" EQUAL realm-value
realm-value = quoted-string
domain = "domain" EQUAL LDQUOT URI
*( 1*SP URI ) RDQUOT
URI = absoluteURI / abs-path
nonce = "nonce" EQUAL nonce-value
nonce-value = quoted-string
opaque = "opaque" EQUAL quoted-string
stale = "stale" EQUAL ( "true" / "false" )
algorithm = "algorithm" EQUAL ( "MD5" / "MD5-sess"
/ token )
qop-options = "qop" EQUAL LDQUOT qop-value
*("," qop-value) RDQUOT
qop-value = "auth" / "auth-int" / token
Proxy-Authorization = "Proxy-Authorization" HCOLON credentials
Proxy-Require = "Proxy-Require" HCOLON option-tag
*(COMMA option-tag)
option-tag = token
Record-Route = "Record-Route" HCOLON rec-route *(COMMA rec-route)
rec-route = name-addr *( SEMI rr-param )
rr-param = generic-param
Reply-To = "Reply-To" HCOLON rplyto-spec
rplyto-spec = ( name-addr / addr-spec )
*( SEMI rplyto-param )
rplyto-param = generic-param
Require = "Require" HCOLON option-tag *(COMMA option-tag)
Retry-After = "Retry-After" HCOLON delta-seconds
[ comment ] *( SEMI retry-param )
retry-param = ("duration" EQUAL delta-seconds)
/ generic-param
Route = "Route" HCOLON route-param *(COMMA route-param)
route-param = name-addr *( SEMI rr-param )
Server = "Server" HCOLON server-val *(LWS server-val)
server-val = product / comment
product = token [SLASH product-version]
product-version = token
Subject = ( "Subject" / "s" ) HCOLON [TEXT-UTF8-TRIM]
Supported = ( "Supported" / "k" ) HCOLON
[option-tag *(COMMA option-tag)]
Timestamp = "Timestamp" HCOLON 1*(DIGIT)
[ "." *(DIGIT) ] [ LWS delay ]
delay = *(DIGIT) [ "." *(DIGIT) ]
To = ( "To" / "t" ) HCOLON ( name-addr
/ addr-spec ) *( SEMI to-param )
to-param = tag-param / generic-param
Unsupported = "Unsupported" HCOLON option-tag *(COMMA option-tag)
User-Agent = "User-Agent" HCOLON server-val *(LWS server-val)
Via = ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm)
via-parm = sent-protocol LWS sent-by *( SEMI via-params )
via-params = via-ttl / via-maddr
/ via-received / via-branch
/ via-extension
via-ttl = "ttl" EQUAL ttl
via-maddr = "maddr" EQUAL host
via-received = "received" EQUAL (IPv4address / IPv6address)
via-branch = "branch" EQUAL token
via-extension = generic-param
sent-protocol = protocol-name SLASH protocol-version
SLASH transport
protocol-name = "SIP" / token
protocol-version = token
transport = "UDP" / "TCP" / "TLS" / "SCTP"
/ other-transport
sent-by = host [ COLON port ]
ttl = 1*3DIGIT ; 0 to 255
Warning = "Warning" HCOLON warning-value *(COMMA warning-value)
warning-value = warn-code SP warn-agent SP warn-text
warn-code = 3DIGIT
warn-agent = hostport / pseudonym
; the name or pseudonym of the server adding
; the Warning header, for use in debugging
warn-text = quoted-string
pseudonym = token
WWW-Authenticate = "WWW-Authenticate" HCOLON challenge
extension-header = header-name HCOLON header-value
header-name = token
header-value = *(TEXT-UTF8char / UTF8-CONT / LWS)
message-body = *OCTET
26 安全考虑:威胁模式和安全应用建议。
SIP不是一个容易进行安全保护的协议。它使用的中间媒体,以及它的多面信任关系,它希望的节点之间交互基于互不信任的关系,它的用户到用户的操作使得安全保证非常重要。今天,我们需要找到基于广泛环境和使用方法的很好的安全解决方案。为了达到这一目标,我们需要建立对SIP的不同使用的几种安全机制。
注意SIP的安全性本身同SIP使用的传输协议比如RTP的安全性或者和SIP包体的实现的安全性本身没有继承关系(虽然MIME安全体系是SIP安全体系的一个基石)。任何和一个会话相关的媒介都可以被端到端的加密,并且这个和相关的SIP信令无关。媒体加密是在本文档讨论范畴之外的。
首先对一系列的经典的威胁模式的分析可以在很大程度上描绘了SIP所需要的安全需要。这些威胁模式所针对的地址需要一些安全保护,我们接下来通过对集中安全集中的详细分析,来讲述如何这些安全机制能够提供对这个地址的安全保护。接着我们就可以定义SIP实现者的需求,并且通过提供一个安全配置样例来描述应用于提高SIP安全性的这些安全机制。本节也标注了一些隐私相关的注解。
26.1 攻击和威胁模式
本节讲述了对SIP部属来说常见的威胁模式。这些威胁模式是经过特别筛选的,用来体现各个SIP所需要的安全保卫服务。接下来的例子并不是完整的针对SIP的威胁模式列表;不过他们是”经典”的例子,用来代表各类对SIP的攻击。
这些攻击假设攻击者可以从网络上读取任何报文-这是因为SIP会通常基于公共网络Internet。在网络上的攻击者通常可以更改报文内容(可能基于中间某个节点来更改)。攻击者可以希望盗取服务,窃听通讯,或者干扰会话。
26.1.1 注册服务 Hijacking。
SIP注册机制是提供一个用户UA把自己的信息到一个注册服务器上,在这个信息中,可以用address-of-record找到这个用户的地址。注册服务器会检查在REGISTER消息中的From头域所提供的身份说明,来决定是否这个请求可以修改由To头域所包含的address-of-record的相关联系地址。这两个头域通常是相同的,也会有很多第三方代替用户注册联系地址的情况。
SIP请求的From头域,可以被一个UA的拥有者任意修改,这就给恶意注册信息打开了方便之门。一个成功模拟一个UA,通过检查来修改一个address-of-record的相关联系地址,可以,例如先注销某个URI的所有联系地址,然后把自己的设备地址注册上去,这样所有对原来URI的地址的请求将发往攻击者的设备。
这种攻击属于很常见的对没有请求发前方数字签名的攻击。任何有意义的SIP UAS(比如对传统电话呼叫的SIP 网关等等),也许希望通过对收到的请求做认证来控制对自己资源的访问。就算是最终终端UA,例如SIP电话,也会有对原始请求身份的验证要求。这个威胁表明了SIP实体需要对原是请求做安全认证的安全服务需要。
26.1.2 模仿一个服务器
对于请求发送到的区域,一般是用Request-URI来标志的。UA通常直接联系这个区域中的服务器来发送请求。不过总会存在一个可能,就是攻击者把自己模仿成为远端的服务器,这样UA的请求可能会被其他人中间截获。
例如,我们考虑这样一个情况:一个转发服务器在一个区域:chicago.com,它模拟的转发服务器在另外一个区域: biloxi.com。用户UA要发送一个请求到biloxi.com,但是chicago.com的转发服务器回答了一个伪造的应答,并且有伪造的头域就好像应答是从biloxi.com回来的一样。在转发应答中的伪造的联系地址可以引导原始UA到一个不合适的或者不安全的资源,或者简单的阻止发送请求到biloxi.com。
这个常见的威胁有着许多的成员,并且相当严重。作为和注册服务hijacking相反的威胁,我们考虑这个情况: 当注册服务信息发送给biloxi.com的被chicago.com截获,并且回应给注册者一个伪造的301(Moved Permanently)应答。这个应答可能看起来是从biloxi.com来得,并且指明了chicago.com作为新的注册服务。那么这个原始UA的所有REGISTER请求就会转发到chicago.com了。
要想防止这个威胁,那么就需要UA能够对接收他们请求的服务器进行身份鉴定。
26.1.3 修改消息包体
当然,SIP UA路由请求通过信任的proxy服务器是一件必然的事情。不管这个信任关系是如何建立的(在本节的其他地方有讲proxy的认证),UA可以信任proxy来转发请求,而不是检查怀疑请求中的包体被修改了。
考虑UA使用SIP消息体来进行媒体会话的会话密钥通讯的情况。虽然它信任本域的proxy服务器,但是它也不希望域的管理者能够解密后续的媒体通讯(就是不希望proxy得到这个会话密钥)。更糟糕的是,如果这个proxy服务器是有恶意的,他可以修改这个会话密钥,就像中间人一样,或者改变原始请求UA的安全特性。
这个类型的威胁不仅仅是对会话密钥,对所有SIP端到端的内容都有威胁。这可能包含对需要展示给用户的MIME包体,SDP或者电话信令等内容的威胁。攻击者可能试图修改SDP包体,例如,给RTP媒体流增加一个窃听设备来偷听语音通话信息。
同样需要注意的是,有一些SIP头域是对端到端有一定含义的,比如Subject。UA可以决定保护这些头域和包体(比如中间恶意的攻击者可以把Subject头域更改成为看起来好像是一个恶意邮件)。不过,因为很多头域都是proxy服务器在处理请求转发的时候需要合法检查或者更改的,所以不是所有的头域都需要端到端的保护。
基于这些原因,UA可以加密SIP包体,并且对端到端的头域做一定的限制。对包体的安全服务要求包含了机密性,完整性和身份认证。这些端到端的安全服务应当与用于和中间节点交互的安全机制无关或者不依赖。
26.1.4 破坏会话
当对话被初始消息所建立,后续的请求可以用于修改对话并且/或者会话的状态。对于会话的负责者来说,非常重要的事情是确定请求不是由攻击者伪造的。
我们考虑这样一个情况,一个第三者攻击者截获了一些初始信息,这些初始信息是对话的双方在建立会话是交换的参数等等(To tag,From tag,等等),并且这个攻击者在会话中插入了一个BYE请求。攻击者可以选择假造一个从会话的任意方的请求。当一个BYE被对方接收到,会话就会被提前终止。
类似会话中的威胁,有伪造re-INVITE修改会话(可能减少会话的安全性,或者作为窃听攻击转发媒体流)。
对于这种威胁,最有效的对策就是对BYE的发送方做身份认证。在这个例子中,接受方只需要确认BYE是从建立对话的对方发起的就可以了。同样的,如果攻击者不能够知道会话的参数,他也没有办法伪造BYE。但是,有些中间节点(比如proxy服务器)需要这些参数来判定是否会话已经建立连接。
26.1.5 拒绝服务和扩展。
DOS(拒绝服务)攻击主要是使得一个特定网络节点无法工作,通常是通过转发超大量的网络通讯阻塞它的网络接口。分布式的拒绝服务攻击允许一个网络用户导致大量的网络服务器来对一个目标做洪水攻击。
在很多架构下,SIP proxy服务器是基于公网的,因为它需要处理全球的IP终端的请求。这样SIP就给这些分布式拒绝服务攻击者提供了很多机会,这样就必须要求SIP系统的设计者和管理者能够识别这样的攻击并且定位这样的攻击者。
攻击者可以发出包含假IP地址及其相关的Via头域的请求,这个Via头域标志了被攻击的主机地址,就像这个请求是从这个主机地址来的一样。然后把这个请求发给大量的SIP节点,这样不幸的SIP UA或者proxy就会给被攻击的主机产生大量的垃圾应答,从而形成拒绝服务攻击。
类似的,攻击者可以用在请求中伪造的Route头域值来标志被攻击的目的主机,并且把这个消息发送到分支proxy,这些分支proxy会放大请求数量发送给目标主机。
Record-Route头域也可以产生类似的效果。当一个攻击者确定一个被请求初始化的SIP对话会向回产生很多事务的时候,那么Record-Route头域也可以被用于攻击。
如果REGISTER请求没有经过注册服务器进行适当的认证,那么就会有很多拒绝服务的攻击的机会。攻击者可以在一个域中,首先把一些或者全部的用户都注销,从而防止这些用户被加入新的会话。接着一个攻击者可以在注册服务器上注册大量的联系地址,这些联系地址都指向同一个被攻击的服务器,这样可以使得这个注册服务器和其他相关的proxy服务器对分布式DOS攻击进行放大。攻击者也会尝试通过注册大量的垃圾来耗尽注册服务器可能的内存或者硬盘。
多点传送的SIP请求可以非常明显的增大拒绝服务攻击的可能性。
port = 1*DIGIT
对于电话描述(telephone-subscriber)的BNF说明在RFC2806[9]中。注意,无论如何,如果在这里允许的字符,如果在SIP URI中的user部分不许可,那么就一定要用转义。
uri-parameters = *( ";" uri-parameter)
uri-parameter = transport-param / user-param / method-param
/ ttl-param / maddr-param / lr-param / other-param
transport-param = "transport="
( "udp" / "tcp" / "sctp" / "tls"
/ other-transport)
other-transport = token
user-param = "user=" ( "phone" / "ip" / other-user)
other-user = token
method-param = "method=" Method
ttl-param = "ttl=" ttl
maddr-param = "maddr=" host
lr-param = "lr"
other-param = pname [ "=" pvalue ]
pname = 1*paramchar
pvalue = 1*paramchar
paramchar = param-unreserved / unreserved / escaped
param-unreserved = "[" / "]" / "/" / ":" / "&" / "+" / "$"
headers = "?" header *( "&" header )
header = hname "=" hvalue
hname = 1*( hnv-unreserved / unreserved / escaped )
hvalue = *( hnv-unreserved / unreserved / escaped )
hnv-unreserved = "[" / "]" / "/" / "?" / ":" / "+" / "$"
SIP-message = Request / Response
Request = Request-Line
*( message-header )
CRLF
[ message-body ]
Request-Line = Method SP Request-URI SP SIP-Version CRLF
Request-URI = SIP-URI / SIPS-URI / absoluteURI
absoluteURI = scheme ":" ( hier-part / opaque-part )
hier-part = ( net-path / abs-path ) [ "?" query ]
net-path = "//" authority [ abs-path ]
abs-path = "/" path-segments
opaque-part = uric-no-slash *uric
uric = reserved / unreserved / escaped
uric-no-slash = unreserved / escaped / ";" / "?" / ":" / "@"
/ "&" / "=" / "+" / "$" / ","
path-segments = segment *( "/" segment )
segment = *pchar *( ";" param )
param = *pchar
pchar = unreserved / escaped /
":" / "@" / "&" / "=" / "+" / "$" / ","
scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
authority = srvr / reg-name
srvr = [ [ userinfo "@" ] hostport ]
reg-name = 1*( unreserved / escaped / "$" / ","
/ ";" / ":" / "@" / "&" / "=" / "+" )
query = *uric
SIP-Version = "SIP" "/" 1*DIGIT "." 1*DIGIT
message-header = (Accept
/ Accept-Encoding
/ Accept-Language
/ Alert-Info
/ Allow
/ Authentication-Info
/ Authorization
/ Call-ID
/ Call-Info
/ Contact
/ Content-Disposition
/ Content-Encoding
/ Content-Language
/ Content-Length
/ Content-Type
/ CSeq
/ Date
/ Error-Info
/ Expires
/ From
/ In-Reply-To
/ Max-Forwards
/ MIME-Version
/ Min-Expires
/ Organization
/ Priority
/ Proxy-Authenticate
/ Proxy-Authorization
/ Proxy-Require
/ Record-Route
/ Reply-To
/ Require
/ Retry-After
/ Route
/ Server
/ Subject
/ Supported
/ Timestamp
/ To
/ Unsupported
/ User-Agent
/ Via
/ Warning
/ WWW-Authenticate
/ extension-header) CRLF
INVITEm = %x49.4E.56.49.54.45 ; INVITE in caps
ACKm = %x41.43.4B ; ACK in caps
OPTIONSm = %x4F.50.54.49.4F.4E.53 ; OPTIONS in caps
BYEm = %x42.59.45 ; BYE in caps
CANCELm = %x43.41.4E.43.45.4C ; CANCEL in caps
REGISTERm = %x52.45.47.49.53.54.45.52 ; REGISTER in caps
Method = INVITEm / ACKm / OPTIONSm / BYEm
/ CANCELm / REGISTERm
/ extension-method
extension-method = token
Response = Status-Line
*( message-header )
CRLF
[ message-body ]
Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF
Status-Code = Informational
/ Redirection
/ Success
/ Client-Error
/ Server-Error
/ Global-Failure
/ extension-code
extension-code = 3DIGIT
Reason-Phrase = *(reserved / unreserved / escaped
/ UTF8-NONASCII / UTF8-CONT / SP / HTAB)
Informational = "100" ; Trying
/ "180" ; Ringing
/ "181" ; Call Is Being Forwarded
/ "182" ; Queued
/ "183" ; Session Progress
Success = "200" ; OK
Redirection = "300" ; Multiple Choices
/ "301" ; Moved Permanently
/ "302" ; Moved Temporarily
/ "305" ; Use Proxy
/ "380" ; Alternative Service
Client-Error = "400" ; Bad Request
/ "401" ; Unauthorized
/ "402" ; Payment Required
/ "403" ; Forbidden
/ "404" ; Not Found
/ "405" ; Method Not Allowed
/ "406" ; Not Acceptable
/ "407" ; Proxy Authentication Required
/ "408" ; Request Timeout
/ "410" ; Gone
/ "413" ; Request Entity Too Large
/ "414" ; Request-URI Too Large
/ "415" ; Unsupported Media Type
/ "416" ; Unsupported URI Scheme
/ "420" ; Bad Extension
/ "421" ; Extension Required
/ "423" ; Interval Too Brief
/ "480" ; Temporarily not available
/ "481" ; Call Leg/Transaction Does Not Exist
/ "482" ; Loop Detected
/ "483" ; Too Many Hops
/ "484" ; Address Incomplete
/ "485" ; Ambiguous
/ "486" ; Busy Here
/ "487" ; Request Terminated
/ "488" ; Not Acceptable Here
/ "491" ; Request Pending
/ "493" ; Undecipherable
Server-Error = "500" ; Internal Server Error
/ "501" ; Not Implemented
/ "502" ; Bad Gateway
/ "503" ; Service Unavailable
/ "504" ; Server Time-out
/ "505" ; SIP Version not supported
/ "513" ; Message Too Large
Global-Failure = "600" ; Busy Everywhere
/ "603" ; Decline
/ "604" ; Does not exist anywhere
/ "606" ; Not Acceptable
Accept = "Accept" HCOLON
[ accept-range *(COMMA accept-range) ]
accept-range = media-range *(SEMI accept-param)
media-range = ( "*/*"
/ ( m-type SLASH "*" )
/ ( m-type SLASH m-subtype )
) *( SEMI m-parameter )
accept-param = ("q" EQUAL qvalue) / generic-param
qvalue = ( "0" [ "." 0*3DIGIT ] )
/ ( "1" [ "." 0*3("0") ] )
generic-param = token [ EQUAL gen-value ]
gen-value = token / host / quoted-string
Accept-Encoding = "Accept-Encoding" HCOLON
[ encoding *(COMMA encoding) ]
encoding = codings *(SEMI accept-param)
codings = content-coding / "*"
content-coding = token
Accept-Language = "Accept-Language" HCOLON
[ language *(COMMA language) ]
language = language-range *(SEMI accept-param)
language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) / "*" )
Alert-Info = "Alert-Info" HCOLON alert-param *(COMMA alert-param)
alert-param = LAQUOT absoluteURI RAQUOT *( SEMI generic-param )
Allow = "Allow" HCOLON [Method *(COMMA Method)]
Authorization = "Authorization" HCOLON credentials
credentials = ("Digest" LWS digest-response)
/ other-response
digest-response = dig-resp *(COMMA dig-resp)
dig-resp = username / realm / nonce / digest-uri
/ dresponse / algorithm / cnonce
/ opaque / message-qop
/ nonce-count / auth-param
username = "username" EQUAL username-value
username-value = quoted-string
digest-uri = "uri" EQUAL LDQUOT digest-uri-value RDQUOT
digest-uri-value = rquest-uri ; Equal to request-uri as specified
by HTTP/1.1
message-qop = "qop" EQUAL qop-value
cnonce = "cnonce" EQUAL cnonce-value
cnonce-value = nonce-value
nonce-count = "nc" EQUAL nc-value
nc-value = 8LHEX
dresponse = "response" EQUAL request-digest
request-digest = LDQUOT 32LHEX RDQUOT
auth-param = auth-param-name EQUAL
( token / quoted-string )
auth-param-name = token
other-response = auth-scheme LWS auth-param
*(COMMA auth-param)
auth-scheme = token
Authentication-Info = "Authentication-Info" HCOLON ainfo
*(COMMA ainfo)
ainfo = nextnonce / message-qop
/ response-auth / cnonce
/ nonce-count
nextnonce = "nextnonce" EQUAL nonce-value
response-auth = "rspauth" EQUAL response-digest
response-digest = LDQUOT *LHEX RDQUOT
Call-ID = ( "Call-ID" / "i" ) HCOLON callid
callid = word [ "@" word ]
Call-Info = "Call-Info" HCOLON info *(COMMA info)
info = LAQUOT absoluteURI RAQUOT *( SEMI info-param)
info-param = ( "purpose" EQUAL ( "icon" / "info"
/ "card" / token ) ) / generic-param
Contact = ("Contact" / "m" ) HCOLON
( STAR / (contact-param *(COMMA contact-param)))
contact-param = (name-addr / addr-spec) *(SEMI contact-params)
name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
addr-spec = SIP-URI / SIPS-URI / absoluteURI
display-name = *(token LWS)/ quoted-string
contact-params = c-p-q / c-p-expires
/ contact-extension
c-p-q = "q" EQUAL qvalue
c-p-expires = "expires" EQUAL delta-seconds
contact-extension = generic-param
delta-seconds = 1*DIGIT
Content-Disposition = "Content-Disposition" HCOLON
disp-type *( SEMI disp-param )
disp-type = "render" / "session" / "icon" / "alert"
/ disp-extension-token
disp-param = handling-param / generic-param
handling-param = "handling" EQUAL
( "optional" / "required"
/ other-handling )
other-handling = token
disp-extension-token = token
Content-Encoding = ( "Content-Encoding" / "e" ) HCOLON
content-coding *(COMMA content-coding)
Content-Language = "Content-Language" HCOLON
language-tag *(COMMA language-tag)
language-tag = primary-tag *( "-" subtag )
primary-tag = 1*8ALPHA
subtag = 1*8ALPHA
Content-Length = ( "Content-Length" / "l" ) HCOLON 1*DIGIT
Content-Type = ( "Content-Type" / "c" ) HCOLON media-type
media-type = m-type SLASH m-subtype *(SEMI m-parameter)
m-type = discrete-type / composite-type
discrete-type = "text" / "image" / "audio" / "video"
/ "application" / extension-token
composite-type = "message" / "multipart" / extension-token
extension-token = ietf-token / x-token
ietf-token = token
x-token = "x-" token
m-subtype = extension-token / iana-token
iana-token = token
m-parameter = m-attribute EQUAL m-value
m-attribute = token
m-value = token / quoted-string
CSeq = "CSeq" HCOLON 1*DIGIT LWS Method
Date = "Date" HCOLON SIP-date
SIP-date = rfc1123-date
rfc1123-date = wkday "," SP date1 SP time SP "GMT"
date1 = 2DIGIT SP month SP 4DIGIT
; day month year (e.g., 02 Jun 1982)
time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
; 00:00:00 - 23:59:59
wkday = "Mon" / "Tue" / "Wed"
/ "Thu" / "Fri" / "Sat" / "Sun"
month = "Jan" / "Feb" / "Mar" / "Apr"
/ "May" / "Jun" / "Jul" / "Aug"
/ "Sep" / "Oct" / "Nov" / "Dec"
Error-Info = "Error-Info" HCOLON error-uri *(COMMA error-uri)
error-uri = LAQUOT absoluteURI RAQUOT *( SEMI generic-param )
Expires = "Expires" HCOLON delta-seconds
From = ( "From" / "f" ) HCOLON from-spec
from-spec = ( name-addr / addr-spec )
*( SEMI from-param )
from-param = tag-param / generic-param
tag-param = "tag" EQUAL token
In-Reply-To = "In-Reply-To" HCOLON callid *(COMMA callid)
Max-Forwards = "Max-Forwards" HCOLON 1*DIGIT
MIME-Version = "MIME-Version" HCOLON 1*DIGIT "." 1*DIGIT
Min-Expires = "Min-Expires" HCOLON delta-seconds
Organization = "Organization" HCOLON [TEXT-UTF8-TRIM]
Priority = "Priority" HCOLON priority-value
priority-value = "emergency" / "urgent" / "normal"
/ "non-urgent" / other-priority
other-priority = token
Proxy-Authenticate = "Proxy-Authenticate" HCOLON challenge
challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
/ other-challenge
other-challenge = auth-scheme LWS auth-param
*(COMMA auth-param)
digest-cln = realm / domain / nonce
/ opaque / stale / algorithm
/ qop-options / auth-param
realm = "realm" EQUAL realm-value
realm-value = quoted-string
domain = "domain" EQUAL LDQUOT URI
*( 1*SP URI ) RDQUOT
URI = absoluteURI / abs-path
nonce = "nonce" EQUAL nonce-value
nonce-value = quoted-string
opaque = "opaque" EQUAL quoted-string
stale = "stale" EQUAL ( "true" / "false" )
algorithm = "algorithm" EQUAL ( "MD5" / "MD5-sess"
/ token )
qop-options = "qop" EQUAL LDQUOT qop-value
*("," qop-value) RDQUOT
qop-value = "auth" / "auth-int" / token
Proxy-Authorization = "Proxy-Authorization" HCOLON credentials
Proxy-Require = "Proxy-Require" HCOLON option-tag
*(COMMA option-tag)
option-tag = token
Record-Route = "Record-Route" HCOLON rec-route *(COMMA rec-route)
rec-route = name-addr *( SEMI rr-param )
rr-param = generic-param
Reply-To = "Reply-To" HCOLON rplyto-spec
rplyto-spec = ( name-addr / addr-spec )
*( SEMI rplyto-param )
rplyto-param = generic-param
Require = "Require" HCOLON option-tag *(COMMA option-tag)
Retry-After = "Retry-After" HCOLON delta-seconds
[ comment ] *( SEMI retry-param )
retry-param = ("duration" EQUAL delta-seconds)
/ generic-param
Route = "Route" HCOLON route-param *(COMMA route-param)
route-param = name-addr *( SEMI rr-param )
Server = "Server" HCOLON server-val *(LWS server-val)
server-val = product / comment
product = token [SLASH product-version]
product-version = token
Subject = ( "Subject" / "s" ) HCOLON [TEXT-UTF8-TRIM]
Supported = ( "Supported" / "k" ) HCOLON
[option-tag *(COMMA option-tag)]
Timestamp = "Timestamp" HCOLON 1*(DIGIT)
[ "." *(DIGIT) ] [ LWS delay ]
delay = *(DIGIT) [ "." *(DIGIT) ]
To = ( "To" / "t" ) HCOLON ( name-addr
/ addr-spec ) *( SEMI to-param )
to-param = tag-param / generic-param
Unsupported = "Unsupported" HCOLON option-tag *(COMMA option-tag)
User-Agent = "User-Agent" HCOLON server-val *(LWS server-val)
Via = ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm)
via-parm = sent-protocol LWS sent-by *( SEMI via-params )
via-params = via-ttl / via-maddr
/ via-received / via-branch
/ via-extension
via-ttl = "ttl" EQUAL ttl
via-maddr = "maddr" EQUAL host
via-received = "received" EQUAL (IPv4address / IPv6address)
via-branch = "branch" EQUAL token
via-extension = generic-param
sent-protocol = protocol-name SLASH protocol-version
SLASH transport
protocol-name = "SIP" / token
protocol-version = token
transport = "UDP" / "TCP" / "TLS" / "SCTP"
/ other-transport
sent-by = host [ COLON port ]
ttl = 1*3DIGIT ; 0 to 255
Warning = "Warning" HCOLON warning-value *(COMMA warning-value)
warning-value = warn-code SP warn-agent SP warn-text
warn-code = 3DIGIT
warn-agent = hostport / pseudonym
; the name or pseudonym of the server adding
; the Warning header, for use in debugging
warn-text = quoted-string
pseudonym = token
WWW-Authenticate = "WWW-Authenticate" HCOLON challenge
extension-header = header-name HCOLON header-value
header-name = token
header-value = *(TEXT-UTF8char / UTF8-CONT / LWS)
message-body = *OCTET
26 安全考虑:威胁模式和安全应用建议。
SIP不是一个容易进行安全保护的协议。它使用的中间媒体,以及它的多面信任关系,它希望的节点之间交互基于互不信任的关系,它的用户到用户的操作使得安全保证非常重要。今天,我们需要找到基于广泛环境和使用方法的很好的安全解决方案。为了达到这一目标,我们需要建立对SIP的不同使用的几种安全机制。
注意SIP的安全性本身同SIP使用的传输协议比如RTP的安全性或者和SIP包体的实现的安全性本身没有继承关系(虽然MIME安全体系是SIP安全体系的一个基石)。任何和一个会话相关的媒介都可以被端到端的加密,并且这个和相关的SIP信令无关。媒体加密是在本文档讨论范畴之外的。
首先对一系列的经典的威胁模式的分析可以在很大程度上描绘了SIP所需要的安全需要。这些威胁模式所针对的地址需要一些安全保护,我们接下来通过对集中安全集中的详细分析,来讲述如何这些安全机制能够提供对这个地址的安全保护。接着我们就可以定义SIP实现者的需求,并且通过提供一个安全配置样例来描述应用于提高SIP安全性的这些安全机制。本节也标注了一些隐私相关的注解。
26.1 攻击和威胁模式
本节讲述了对SIP部属来说常见的威胁模式。这些威胁模式是经过特别筛选的,用来体现各个SIP所需要的安全保卫服务。接下来的例子并不是完整的针对SIP的威胁模式列表;不过他们是”经典”的例子,用来代表各类对SIP的攻击。
这些攻击假设攻击者可以从网络上读取任何报文-这是因为SIP会通常基于公共网络Internet。在网络上的攻击者通常可以更改报文内容(可能基于中间某个节点来更改)。攻击者可以希望盗取服务,窃听通讯,或者干扰会话。
26.1.1 注册服务 Hijacking。
SIP注册机制是提供一个用户UA把自己的信息到一个注册服务器上,在这个信息中,可以用address-of-record找到这个用户的地址。注册服务器会检查在REGISTER消息中的From头域所提供的身份说明,来决定是否这个请求可以修改由To头域所包含的address-of-record的相关联系地址。这两个头域通常是相同的,也会有很多第三方代替用户注册联系地址的情况。
SIP请求的From头域,可以被一个UA的拥有者任意修改,这就给恶意注册信息打开了方便之门。一个成功模拟一个UA,通过检查来修改一个address-of-record的相关联系地址,可以,例如先注销某个URI的所有联系地址,然后把自己的设备地址注册上去,这样所有对原来URI的地址的请求将发往攻击者的设备。
这种攻击属于很常见的对没有请求发前方数字签名的攻击。任何有意义的SIP UAS(比如对传统电话呼叫的SIP 网关等等),也许希望通过对收到的请求做认证来控制对自己资源的访问。就算是最终终端UA,例如SIP电话,也会有对原始请求身份的验证要求。这个威胁表明了SIP实体需要对原是请求做安全认证的安全服务需要。
26.1.2 模仿一个服务器
对于请求发送到的区域,一般是用Request-URI来标志的。UA通常直接联系这个区域中的服务器来发送请求。不过总会存在一个可能,就是攻击者把自己模仿成为远端的服务器,这样UA的请求可能会被其他人中间截获。
例如,我们考虑这样一个情况:一个转发服务器在一个区域:chicago.com,它模拟的转发服务器在另外一个区域: biloxi.com。用户UA要发送一个请求到biloxi.com,但是chicago.com的转发服务器回答了一个伪造的应答,并且有伪造的头域就好像应答是从biloxi.com回来的一样。在转发应答中的伪造的联系地址可以引导原始UA到一个不合适的或者不安全的资源,或者简单的阻止发送请求到biloxi.com。
这个常见的威胁有着许多的成员,并且相当严重。作为和注册服务hijacking相反的威胁,我们考虑这个情况: 当注册服务信息发送给biloxi.com的被chicago.com截获,并且回应给注册者一个伪造的301(Moved Permanently)应答。这个应答可能看起来是从biloxi.com来得,并且指明了chicago.com作为新的注册服务。那么这个原始UA的所有REGISTER请求就会转发到chicago.com了。
要想防止这个威胁,那么就需要UA能够对接收他们请求的服务器进行身份鉴定。
26.1.3 修改消息包体
当然,SIP UA路由请求通过信任的proxy服务器是一件必然的事情。不管这个信任关系是如何建立的(在本节的其他地方有讲proxy的认证),UA可以信任proxy来转发请求,而不是检查怀疑请求中的包体被修改了。
考虑UA使用SIP消息体来进行媒体会话的会话密钥通讯的情况。虽然它信任本域的proxy服务器,但是它也不希望域的管理者能够解密后续的媒体通讯(就是不希望proxy得到这个会话密钥)。更糟糕的是,如果这个proxy服务器是有恶意的,他可以修改这个会话密钥,就像中间人一样,或者改变原始请求UA的安全特性。
这个类型的威胁不仅仅是对会话密钥,对所有SIP端到端的内容都有威胁。这可能包含对需要展示给用户的MIME包体,SDP或者电话信令等内容的威胁。攻击者可能试图修改SDP包体,例如,给RTP媒体流增加一个窃听设备来偷听语音通话信息。
同样需要注意的是,有一些SIP头域是对端到端有一定含义的,比如Subject。UA可以决定保护这些头域和包体(比如中间恶意的攻击者可以把Subject头域更改成为看起来好像是一个恶意邮件)。不过,因为很多头域都是proxy服务器在处理请求转发的时候需要合法检查或者更改的,所以不是所有的头域都需要端到端的保护。
基于这些原因,UA可以加密SIP包体,并且对端到端的头域做一定的限制。对包体的安全服务要求包含了机密性,完整性和身份认证。这些端到端的安全服务应当与用于和中间节点交互的安全机制无关或者不依赖。
26.1.4 破坏会话
当对话被初始消息所建立,后续的请求可以用于修改对话并且/或者会话的状态。对于会话的负责者来说,非常重要的事情是确定请求不是由攻击者伪造的。
我们考虑这样一个情况,一个第三者攻击者截获了一些初始信息,这些初始信息是对话的双方在建立会话是交换的参数等等(To tag,From tag,等等),并且这个攻击者在会话中插入了一个BYE请求。攻击者可以选择假造一个从会话的任意方的请求。当一个BYE被对方接收到,会话就会被提前终止。
类似会话中的威胁,有伪造re-INVITE修改会话(可能减少会话的安全性,或者作为窃听攻击转发媒体流)。
对于这种威胁,最有效的对策就是对BYE的发送方做身份认证。在这个例子中,接受方只需要确认BYE是从建立对话的对方发起的就可以了。同样的,如果攻击者不能够知道会话的参数,他也没有办法伪造BYE。但是,有些中间节点(比如proxy服务器)需要这些参数来判定是否会话已经建立连接。
26.1.5 拒绝服务和扩展。
DOS(拒绝服务)攻击主要是使得一个特定网络节点无法工作,通常是通过转发超大量的网络通讯阻塞它的网络接口。分布式的拒绝服务攻击允许一个网络用户导致大量的网络服务器来对一个目标做洪水攻击。
在很多架构下,SIP proxy服务器是基于公网的,因为它需要处理全球的IP终端的请求。这样SIP就给这些分布式拒绝服务攻击者提供了很多机会,这样就必须要求SIP系统的设计者和管理者能够识别这样的攻击并且定位这样的攻击者。
攻击者可以发出包含假IP地址及其相关的Via头域的请求,这个Via头域标志了被攻击的主机地址,就像这个请求是从这个主机地址来的一样。然后把这个请求发给大量的SIP节点,这样不幸的SIP UA或者proxy就会给被攻击的主机产生大量的垃圾应答,从而形成拒绝服务攻击。
类似的,攻击者可以用在请求中伪造的Route头域值来标志被攻击的目的主机,并且把这个消息发送到分支proxy,这些分支proxy会放大请求数量发送给目标主机。
Record-Route头域也可以产生类似的效果。当一个攻击者确定一个被请求初始化的SIP对话会向回产生很多事务的时候,那么Record-Route头域也可以被用于攻击。
如果REGISTER请求没有经过注册服务器进行适当的认证,那么就会有很多拒绝服务的攻击的机会。攻击者可以在一个域中,首先把一些或者全部的用户都注销,从而防止这些用户被加入新的会话。接着一个攻击者可以在注册服务器上注册大量的联系地址,这些联系地址都指向同一个被攻击的服务器,这样可以使得这个注册服务器和其他相关的proxy服务器对分布式DOS攻击进行放大。攻击者也会尝试通过注册大量的垃圾来耗尽注册服务器可能的内存或者硬盘。
多点传送的SIP请求可以非常明显的增大拒绝服务攻击的可能性。
这些展示的问题需要定义一个架构来把拒绝服务攻击造成的影响最小化,并且需要在安全机制中对这类攻击特别留意。
26.2 安全机制
从上边讲述的威胁来看,我们得到了SIP协议所需要的基本安全服务,他们是:保护消息的隐私性和完整性,保护重现(replay)攻击或者消息欺骗,提供会话参与者的身份认证和隐私保护,保护拒绝服务攻击。SIP消息中的包体分别要求机密性,完整性和身份认证。
比为SIP重新定义新的安全机制更好的是,SIP可以重用已经存在的HTTP或者SMTP的安全机制。
全加密的消息提供了最好的机密保护-它也可以保证消息不被恶心的中间节点更改。不过SIP请求和应答不能简单的进行端到端的加密,因为在大多数网络架构下,消息的头域比如Request-URI,Route,Via在中间经过的proxy中需要可见,这样SIP请求才能被正确路由。注意,proxy服务器需要修改一些消息的特定属性(比如增加Via头域),这样才能保证SIP正常工作。那么proxy服务器就必须被UA信任,至少在某种程度上信任。为了这个目标,我们建议为SIP提供低层次的安全机制,他们是基于节点到节点的整个SIP请求和应答的在线加密,并且语序终端节点校验他们发出请求的proxy服务器的身份。
SIP实体也可以为安全保证,需要验证对方的身份。当SIP终端向对方UA或者proxy服务器,声明它的用户的身份时,这个身份应当是可以通过某种方法验证的。SIP中的数字签名机制就是为了这个需要的。
SIP消息体的一个独立的安全(加密)机制提供了另一个端到端的相互认证方式,也降低了UA必须信任中间节点的程度。
26.2.1 通讯和网络层的安全
通讯或者网络层的安全机制是加密信号通讯,保证消息机密的和完整的传送。
很多情况下,低层次的安全是通过证书实现的,这些证书可以在很多架构下用于提供身份认证使用。
两个通常使用的方法,提供了通讯层和网络层的安全,他们是TLS[25]和IPSec[26]。
IPSec是一组网络层的协议工具,他们可以一起使用来作为传统IP通讯的安全替代。IPSec最常用于一组主机或者管理的域有一个现存的互相信任关系的架构下。Ipsec通常由主机的操作系统级别实现,或者在一个提供机密通讯和完整性保证的通讯安全网关上实现(比如VPN结构),IPSec也可以用于点到点的结构。
在很多结构下,IPSec并不要求和SIP应用一起使用;IPSec可能是最适合于部属在那种难于直接在SIP服务器上增加安全性的情况。具有预先共享密钥关系的UA和他们的第一个节点的proxy服务器很适合使用IPSec。为SIP部属的IPSec要求一个IPSec 描述了协议工具的profile。这个profile在本文档中没有提供。
TLS提供了通讯层的安全性,基于连接相关的协议(TCP)。可以通过在Via头域或者在一个SIP URI中列明”tls” (表示基于TCP的TLS)指定通讯协议为TLS。TLS最适合没有事先定义的信任关系的点到点的结构。例如Alice 信任她的本地proxy服务器,这个服务器在进行证书交换后信任Bob的本地proxy服务器,这个Bob的本地proxy服务器是Bob信任的,因此Bob可以和Alice安全的通讯。
TLS必须和SIP应用紧紧联系在一起。注意在SIP中的通讯机制是点到点的,因此一个基于TLS发送请求到proxy服务器的UA并不能保证这个TLS会在端到端的应用。
当实现者在SIP应用中使用TLS的时候,实现者必须支持最小集合的TLS_RSA_WITH_AES_128_CBC_SHA密码套件[6]。并且为了向后兼容,proxy服务器,重定向服务器和注册服务器应当支持TLS_RSA_WITH3DES_EDE_CBC_SHA。实现者也可以支持其他密码套件。
26.2.2 SIPS URI方案
SIPS URI方案是SIP URI(19节)语法的一个附加,虽然这个方案串是”sips”不同于”sip”。SIPS的语义和SIP URI的语义十分不同。SIPS 允许指定希望通过安全访问的资源。SIPS URI可以当作一个特定用户的address-of-record使用-这个用户是已知的(根据他们的名片,在他盟请求的From头域,在REGISTER请求的To头域)。当在请求中使用Request-URI,SIPS 方案指出请求经过的每一个节点,知道请求到达目的这个Request-URI指明的SIP元素,必须通过TLS进行加密;当请求抵达目标的域,他会根据目标域的本地安全策略和转发策略,很有可能最后一部也是用TLS到达UAS。当用在请求的发起方(就像这种情况,当他们使用SIPS URI当作目标的address-of-record一样),SIPS只是这个实体请求,到目的主机的所有路径都应当加密。
SIPS方案适用于很多在SIP中应用的SIP URI,比如附加域Request-Uri,包含在address-of-record,联系地址(Contact的内容,包含REGISTER方法的头域等等),还有Route头域等等。在每个用法中,SIPS URI方案允许这些存在的URI来指明需要安全访问的资源。这些由SIPS URI所替换的东西,有他们自己的安全属性([4]中详细介绍)。
对SIPS的使用在细节上要求必须具备TLS互相的认证,并且要求支持密码套件TLS_RSA_WITH_AES_128_CBC_SHA。在认证过程中接收到的信任书应当从客户端持有的信任书跟节点开始验证;对信任书验证失败应当导致请求的失败。
注意在SIPS URI方案中,通讯层是和TLS没有依赖关系的,并且因此”sips:alice@atlanta.com;transport=tcp”和”sips:alice@atlanta.com;transport=sctp”都是合法的(虽然注意到UDP不能用于SIPS的传送)。我们不建议使用类似”transport=tls”的方式,部分原因是因为这是用于请求的单个节点之间的通讯。这是从RFC2543的一个变化。
将address-of-record用SIPS URI发出的用户,如果在非可靠通讯协议上收到的请求,可以操作设备来拒绝这个请求。
26.2.3 HTTP Authentication
SIP提供了认证机制,基于HTTP认证的身份认证机制,他们依赖于401倒407应答码和相关头域来提供拒绝不信任的信任书。对于SIP使用的HTTP Digest认证机制,并没有做重大的修改,它提供了replay攻击的保护和单向认证关系。
对SIP的Digest 认证使用在22节有描述。
26.2.4 S/MIME
就像上边讲述的,在端到端的过程中加密整个SIP消息体,可以提供机密性的保护,但是并非所有的字段都能使用这个机制进行保护,因为中间的网络节点(比如proxy服务器),需要根据读取适当的头域然后决定这个消息应当转发倒哪里,并且如果这些中间节点由于安全原因被排出在外,那么SIP消息从本质上就是不能路由的。
不过,S/MIME允许SIP 的UA在SIP中加密MIME包体,在不影响消息头的情况下,在端到端的通讯中加密这些MIME包体。S/MIME可以提供消息体的端到端的完整性和机密性,同样也提供了双向的认证机制。使用S/MIME也可以通过SIP消息隧道,为SIP头域提供一个完整性和机密性的方案。
对SIP的S/MIME使用在23节讲述。
26.3 安全机制的实现
26.3.1 对SIP实现者的要求
proxy服务器,重定向服务器,和注册服务器必须实现TLS,并且必须支持双向的和单向的认证关系。强烈建议UA可以初始化TLS;UA同样可以作为一个TLS服务器。proxy服务器,重定向服务器和注册服务器应当有一个站点信任书,这个信任书的主题和他们的规范主机名相关。对于TLS的双向认证,UA可以有他们自己的信任书,但是本文档中,没有规定他们的具体用法。所有的支持TLS的SIP元素必须具备在TLS协商中,验证信任书的机制;这个使得证书机关(可能是有名的类似web浏览器证书发行机构的发行机构)发布的一个或者多个根信任书成为必须。所有支持TLS的SIP元素必须同样支持SIPS URI方案。
Proxy服务器,重定向服务器,注册服务器,和UA可以实现IPSec或者其他底层的安全协议。
当UA试图联系一个proxy服务器,重定向服务器或者主阿服务器,UAC应当初始化一个TLS连接,在这个连接上发起SIP消息。在某些结构吓,UAS可以同样在这些TLS接收请求
Proxy服务器,重定向服务器,注册服务器,和UA必须实现Digest身份认证,包括所有的22节要求的要点。Proxy服务器,重定向服务器,注册服务器应当配置成为至少有一个Digest realm,并且对于给定服务器来说,必须支持至少有一个”realm”字符串和这个服务器的主机名或者hostname相关联。
UA可以支持MIME包体的加密,并且通过23节描述的那样使用S/MIME传送信任书。如果UA具有一个或者多个根身份认证的信任书,用来鉴定TLS或者IPSec的信任书,它应当适当的可以用这些来鉴定S/MIME的信任书。UA可以为S/MIME身份认证而具有特定的根信任书。
注意,随着S/MIME实现,将来会有安全扩展,来提高S/MIME的强度。
26.3.2 安全解决方案
这些安全机制的操作,可以在某种程度上和现存的WEB和EMAIL安全模式一致。在高一点的级别来看,UA通过Digest 用户名和口令把他们自己的身份向服务器(proxy服务器,重定向服务器,注册服务器)认证;服务器把他们自己向UA单节点认证,或者向另外一个服务器进行单节点(one hop)认证(反之亦然),并且是通过TLS来传送服务器节点信任书。
在点对点的级别,UA一般信任网络来进行对方身份的鉴别;不过,如果网络不能够鉴定对方身份,或者网络本身不被信任的情况下,也可以使用S/MIME来提供直接的身份认证。
接下来是一个例子,在这个例子中,不同的UA和服务器使用这些安全机制防止26.1节描述的攻击威胁。实现者和网络管理员可以遵循本节末尾给出的指示来防止攻击威胁,这些指示是作为实现例子提供的。
26.3.2.1 注册
当UA上线,并且注册到它自己的域上,它应当和它的注册服务器建立一个TLS连接(10节描述了UA怎样找到它的注册服务器)。注册服务器应当提供一个信任书给UA,并且这个信任书的节点必须是这个UA想要注册的域相关的信任书节点;例如,如果UA向注册 alice@atlanta.com这个address-of-record,这个信任书节点必须是一个atlanta.com域的主机(比如sip.atlanta.com)。如果它收到了TLS信任书消息,UA应当校验这个信任书,并且检查这个信任书的节点。如果信任书是非法的,作废的,或者它和持有者不符,UA必须不能发送REGISTER消息和进行注册处理。
当UA收到注册服务器提供的一个有效的信任书,UA知道注册服务器并非一个攻击者(可能重定向、窃取口令、或者试图做类似攻击的攻击者)。
于是UA创建了一个REGISTER请求,并且Request-URI应当指向从注册服务器所接收到的信任书站点。当UA通过刚才建立的TLS连接发送REGISTER请求,注册服务器应当给出一个401(Proxy Authentication Required)应答。在这个应答中,Proxy-Authenticate头域的”realm”参数,应当和前边给出的信任书节点的域相同。当UAC收到这个拒绝,它应当提示给用户要求信任书,或者根据应答中的”realm”参数,从现有的密钥组中查找对应的信任书。这个信任书的用户名应当和REGISTER请求的To头域的URI的”userinfo”部分相关。当在一个合适的Proxy-Authorization头域中插入和这个信任书,REGISTER应当重新发送到注册服务器。
由于注册服务器要求UA认证它自己,对于攻击者来说,伪造一个用户的address-of-record的REGISTER请求是很困难的。同样注意到由于REGISTER是通过机密的TLS连接发送的,攻击者不能通过截取REGISTER来记录信任书来进行重放攻击。
当注册请求被注册服务器接收,UA应当继续保持TLS连接,这样使得注册服务器可以既当作proxy服务器,这个proxy服务器可以作为管理这个域的proxy服务器。刚完成注册的TLS连接会继续保留用于接收UA后续发起的请求。
由于UA已经通过在TLS连接的对方的服务器的认证,所有在这个连接上的请求都是经过proxy 服务器的(由于保留了TLS连接,也就是说,刚才的注册服务器更换了角色,变成一个proxy服务器)--攻击者不能伪造好像是刚才从这个proxy服务器发送的请求。
26.3.2.2 在域之间的请求
现在我们说,Alice的UA希望和远端管理的域的一个用户,这个用户叫做” bob@biloxi.com”,初始化一个会话。我们讲会说本地管理的域(atlanta.com)有一个本地外发proxy。
对于一个管理域的Proxy服务器处理那发请求,可以同样作为本地的外发proxy;基于简单的原则,我们假定这就是atlanta.com(否则这时候UA将要初始化一个新的TLS连接到一个独立的服务器)。假定这时候,这个客户端已经完成了注册(参见前边的步骤),当它发出INVITE请求邀请另外一个用户的时候,它将重用这个TLS连接到本地proxy服务器(刚才的注册服务器)。这个UA在INVITE请求中,将会重用刚才cache的信任书,这样可以避免不必要的要求用户输入信任书。
当本地的发外服务器验证了这个UA在INVITE请求中的信任书,它应当检查Request-URI来决定这个消息应当如何路由(参见[4])。如果这个Request-URI的”domainname”部分和一个本地域相关联(atlanta.com)而不是和biloxi.com相关,那么proxy服务器会向本地服务查询来决定怎样最好的访问到被呼叫的用户。
“ alice@atlanta.com”正在尝试联系呼叫” alex@atlanta.com”,本地proxy将会转发这个请求到Alex和它的注册服务器所建立的TLS连接上。由于Alex将会通过它的已经通过认证的连接上收到这个请求,它就确定这个Alice的请求是通过了本地管理域的proxy的身份验证的。
不过,在这个例子中,Request-URI指向的是一个远程域。在atlanta.com的本地外发服务器应当因此而建立一个和在biloxi.com的远程proxy服务器的TLS连接。由于这个TLS连接的两端都是服务器,并且都有服务器的信任书,那么应当使用双向的TLS身份认证。连接的双方应当验证和检查对方的信任书,把在信任书中的域名同SIP消息中的头域做比较。例如,atlanta.com 这个proxy服务器,在这步,应当检查从对方接收到的关于biloxi.com域的信任书。当检查完毕,并且TLS协商完成,就建立了基于两个proxy的安全通道,atlanta.com proxy于是可以把INVITE请求转发给biloxi.com了。
在biloxi.com的proxy服务器应当检查atlanta.com的信任书,并且比较信任书提供的域名和INVITE请求的From头域的”domainname”部分。这个biloxi proxy可以执行严格的安全机制,拒绝那些他们被转发的域和本proxy所管理的域不匹配的请求(这个不太明白)。
这些安全机制可以用来防止SIP和SMTP ‘open relays’一样经常被用于产生垃圾邮件一样的信息。
这个政策,只是保证了请求声明的来源确实是它自己;他并不允许biloxi.com确知如何atlanta.com认证的Alice。只有当biloxi.com由其他方法知道atlanta.com的身份认证机制,他才可能确知Alice如何证明她的身份的。biloxi.com可以接着使用更严格的方法,禁止来自未确定和biloxi.com相同的认证策略的域的请求(就是说所有biloxi.com接受的请求,都必须来自biloxi.com所知道身份认证方式的域)。
当INVITE请求被biloxi.com核准,proxy服务器应当鉴别现存的TLS通道,如果存在现存的TLS通道,并且是和这个请求中的被叫用户(在这个例子中是 bob@biloxi.com)相关联的。那么这个INVITE请求应当通过这个TLS通道发送给Bob。由于通过这个TLS连接收到的请求,这个TLS连接是刚才已经在biloxi proxy上通过了身份认证,Bob于是知道From头域没有被篡改,并且atlanta.com已经认证了Alice,所以就没有必要犹豫是否信任Alice的身份。
在他们转发请求钱,两个proxy服务器应当在请求中增加Record-Route头域,这样所有在这个对话中的后续的请求讲过通过这两个proxy服务器。proxy服务器因此可以在对话生存周期中,继续提供安全服务。如果proxy服务器并不在Record-Route头域增加他们自己,以后的消息将会直接端到端的在Alice和Bob中发送,而没有任何安全措施(除非两个端点使用了某种独立的端到端的安全措施,比如S/MIME)。在这个考虑上,SIP梯形模式可以提供一个精美的结构来在proxy服务器节点之间进行磋商,以提供一个Alice和Bob之间的有道理的安全通道。
例如,一个攻击这个结构的攻击者将会不能伪造BYE请求并且把他插入Bob和Alice的信令流,因为攻击者无从探听会话的参数,这也是由于通讯的完整性机制保证了Alice和Bob之间的通讯是机密的完整的。
26.3.2.3 点对点请求
另外,考虑这样一个情况,UA声称 carol@chicago.com没有一个本地外发proxy。当Carol希望发送INVITE到 bob@biloxi.com,她的UA应当直接初始化一个TLS连接到biloxi proxy(使用附件[4]中描述的机制来检查怎样到达指定的Request-URI)。当她的UA收到一个biloxi proxy发送的信任书,她应当在通过TLS连接发送她的INVITE请求前,正常校验这个信任书。不过,Carol并没有义务相biloxi proxy提供她自己的身份,但是她在INVITE请求的”message/sip”包体中,有一个CMS-detached(分离的)签名。如果Carol在biloxi.com realm有其他信任书的话,就不一定提供这个签名,但是她在biloxi.com上没有正式关系,所以她没有信任书,也就必须提供这个签名。biloxi proxy可以有严格的机制直接把这个请求踢掉,甚至不用麻烦被叫方来验证这个请求,因为在From头域的”domainname”部分,并没有biloxi.com-它可以被当作这个用户是未认证的。
biloxi proxy对于Bob用户有一个政策,就是所有未认证的请求都应当转发到一个适当的地址,对于 bob@biloxic.om,就是<sipo:bob@192.0.2.4>。Carol在和biloxi proxy建立的TLS连接上,收到这个转发请求的应答,于是它信任这个转发地址的真实性。
Carol应当和目标地址建立一个TCP连接,并且发送一个新的INVITE请求,在这个请求中,Request-URI包含刚才接收到的联系地址(需要重新计算包体中的签名,因为请求重新构造了)。Bob从不安全的界面上接收到这个INVITE请求,但是这个UA是特意预留一个不安全的界面,在这个情况下,认可请求中的From头域并且随后把INVITE包体中的签名和一个本地cache的信任书进行匹配。Bob用类似的方法应答,把他自己相Carol进行认证,这样一个安全的对话就开始了。
某些情况下,在一个域的NAT或者防火墙会阻止UA之间直接建立TCP连接。在这个情况下,如果本地策略许可,proxy服务器可以隐含的做UA之间的请求转发,并且这个转发是基于没有信任关系的(比如不用现存的TLS连接,而是通过TCP明码的请求转发)
26.3.2.4 DoS 防护
基于这些安全解决方案,为了使得拒绝服务(DoS)攻击造成的影响最小,实现者应当注意如下的指引:
当SIP proxy服务器所在的主机是基于公共internte做路由的,他应当部署在一个具有防护操作策略的管理域中(比如block源路由,过滤ping包等等)。TLS和IPSec都可以在管理域的边界的防护主机,一起参与安全系统的构家,来提高安全性。这些防护主机可以防护拒绝服务攻击,确保在管理域中的SIP主机不会被大量的消息阻塞。
不管使用什么样的安全措施,对proxy服务器的洪水消息攻击可以耗尽proxy服务器的资源,并且阻止发送到目的地的正确的请求。对于proxy服务器来说,每一个SIP事务都会好用一定的资源,对于有状态的服务器来说这个消耗会更大。因此,有状态的proxy比无状态的proxy更容易受到洪水攻击的影响。
UA和proxy服务器应当用一个401(Unauthorized)或者407(Proxy Authentication Required)拒绝有问题的请求,并且对这些有问题的请求不使用正常的应答重发机制,并且对这些未认证的请求把自己当作无状态的服务器使用。如果攻击者使用假的头域值(例如Via)指向一个第三方的被攻击的服务器,那么对于401(Unauthorized)或者407(Proxy Authentication Required)应答的重发可能正中攻击者的下怀。
总得来说,基于TLS签名的proxy服务器之间的双向认证机制,降低了中间节点潜在的风险,并且减少了可以用作拒绝服务攻击的伪造的请求或者应答。这也同样提高了攻击者利用无知的SIP节点进行放大攻击的难度。
26.4 限制
虽然有这些安全机制,在正确应用的时候,可以阻止很多攻击,但是对于网络管理者和开发者来说,也必须明白他们也会有很多限制的地方。
26.4.1 HTTP Digest
在SIP中对于HTTP Digest一个限制是Digest的完整性机制在SIP下运作的不是很完美。尤其是,他们提供了对消息中的Request-URI和方法的保护,但是并没有对UA希望提供加密的所有头域进行了保护。
RFC2617所提供的回放攻击保护对于SIP来说也有一些限制。例如,next-nonce机制,并不支持通过管道传送的请求。防止回放攻击应当使用nonce-count机制。
另一个HTTP Digest限制是realm的范围。当用户希望把他们自己的身份向一个资源(这个资源和他们有着预先存在的关系)进行认证的时候,Digest是有用的,它就像一个服务提供者,用户是一个客户端(这是十分常见的场景,因此Digest提供了一个十分有用的功能)。与之对应的,TLS的范围是基于域之间的,或者multirealm的,因为信任书通常是全局可验证的,所以UA可以在不需要预先存在关系的服务器上进行身份验证。
26.4.2 S/MIME
对于S/MIME的一个最大的限制是对终端用户来说,缺少广泛的公共密钥机构。如果使用的是自签名(selfsigned)信任书(或者信任书不能被对话的对方所验证),23.2节描述的基于SIP的密钥交换机制就容易遭受中间人攻击(man-in-the-middle),在这个中间人攻击中,攻击者可以悄悄检查和修改S/MIME包体。攻击者需要截取对话中,双方的第一个密钥交换,在请求和应答中移去现有的CMS-detached签名,并且插入另外的包含攻击者提供的信任书(但是看起来像是正确地address-of-record的信任书)的CMS-detached 签名。通讯的双方都回以为他们和对方交换了密钥,实际上他们互相有的只是攻击者的公钥。
必须明确注意到攻击者只能攻击双方的第一次的密钥交换-在后续的情况,密钥的变更对于UA来说就是不可见的了。同样使得攻击者难以窃听对话双方的以后的对话中(比如一天内的对话,周内的,或者一年的对话)。
SSH在第一次密钥交换的时候,也同样容易受到这个中间人攻击;但是,虽然众所周知SSH不完美,但是它确实提高了连接的安全性。对于SIP来说,使用密钥的指纹可以有些帮助,就像在SSH中一样。例如,如果SIP的双方建立了一个语音通讯会话,每一个都可以读取对方传送的密钥指纹,并且可以根据这个指纹和原始指纹做比较。这使得中间人更加难以在语音中模拟通话双方的密钥指纹(实际上这个在Clipper 基于芯片的保密电话中使用)。
在S/MIME机制下,如果UA在他们的密钥组中持有对方address-of-record的信任书,允许UA不用导言来发送加密的请求。不过,存在这个样的情况,如果某个设备注册了这个address-of-record,并且没有持有持有设备当前用户先前使用的信任书。并且它将因此不能正常处理加密的请求,于是可能会导致某些原本可以避免的错误信号。这很像加密的请求被分支的情况。
S/MIME相关的密钥通常用于和特定用户(一个address-of-record)关联起来使用,而不是和一个设备(UA)一起使用。当用户在设备之间移动,很难把私钥安全的在UA之间传递;一个设备如何获得这些密钥不在本文讨论。
另外,使用S/MIME更常见的困难是,它可以导致很大的消息,特别是当采用23.4节的SIP隧道戒指的时候。基于这个原因,当使用S/MIME隧道机制的时候,一定要使用TCP通讯协议。
26.4.3 TLS
TLS最大的问题是,它不能基于UDP;TLS要求面向连接的底层通讯协议,对于本文来说,就是TCP。
对于TLS来说,在本地外发proxy服务器和/或者主车服务器上和大量UA,维持很多并发TLS长连接,是一件费力的事情。这导致某些容量问题,特别是在使用加强密钥套件的时候更容易出现容量问题。维持冗余的TLS长连接,特别是当UA独立负责他们的连接,会很耗资源。
TLS只允许SIP实体到他们临近的认证服务器的连接;TLS提供了严格的节点到节点的安全性(hop-by-hop)。无论TLS,还是其他本文档规定的安全机制,当不能直接建立TCP连接的情况下,都允许客户通过验证自己到proxy服务器来到达目的地。
26.4.4 SIPS URI
实际上在请求经过的每一段都使用TLS,要求了最终的UAS必须能够通过TLS到达(也许是通过SIPS URI作为一个联系地址注册的)。通常这个目的地是用SIPS描述的。在很多结构下,使用TLS保护一部分请求路径,最后一部确实依赖于其他的安全机制到UAS。因此SIPS不能保证TLS是真正的端到端的使用。注意,这是由于许多UA不接受进来的TLS连接,甚至那些支持TLS的UA也可能要求要维持一个永久的TLS连接(就像前边的TLS限制节讲述的一样),目的是为了作为UAS从TLS上接收请求。
位置服务,对于SIPS Request-URI来说,是不要求提供SIPS绑定的。虽然位置服务通常由用户注册(10.2.1节描述)组成,但是对于一个address-of-record来说,可以由不同的协议和界面都可以提供联系地址,并且这些工具都可以用来映射SIPS URI到适当的SIP URI。当对绑定信息查询的时候,位置服务返回它的联系地址,而不关心这个是否是从一个SIPS的Request-URI上收到的请求。如果是转发服务器访问位置服务,那就是说取决于处理转发的Contact头域的SIP实体来决定联系地址的属性。
如果要求请求经过的全部路径都使用TLS通讯,那么对目的域来说稍稍有点麻烦。如果实现困难,也允许在请求传送节点中的基于密码认证的proxy服务器,不兼容或者选择一个折中方案来略过SIPS的转发机制(在16.6节定义的通常转发规则)。但是,恶意的中间节点就有可能把一个基于SIPS URI的请求,重新降级成为SIP URI。
另外,中间节点也可以正常的把一个基于SIP的请求更改成基于SIPS URI的请求。请求的接受方发现请求的Request-URI是基于SIPS URI方案的并不能假设原始请求的Request-URI也是基于SIPS通过中间节点传送的(从客户端往后)。
为了解决这个问题,我们建议请求的收件人,在请求的Request-URI包含一个SIP或者SIPS URI的情况下,检查To头域值,看看是否包含一个SIPS URI(虽然注意到Request-URI可以和To头域URI有相同的URI方案,但是他们不相等并不意味者是一个安全隐患)。虽然客户端可以在一个请求中把Request-URI和To头域做成不一样的,但是当两个字段的SIPS不一样的话,那就是可能的安全隐患,并且请求因此应当被接受方拒绝。接受方也可以检查Via头域链来双重检查是否TLS在整个请求路径中被使用,一直到最近的本地管理域。源UAC也可以用S/MIME来帮助确保原始格式的To头域被端到端的发送。
如果UAS有原因来相信Request-URI的URI方案在通讯中被非法修改,UA应当提示用户这个潜在的安全隐患。
作为更深远的考量,为了防止底层的攻击,如果SIP实体只接受SIPS请求,那么可以拒绝在非安全端口的连接。
终端用户应当完全清楚SIPS和SIP URI的区别,他们可以在应答中手工更改这个URI方案。这可以增加或者降低安全性。例如,如果一个攻击者攻陷了一个DNS的cache,插入了一个假的记录集,这个记录集有效删去了一个proxy服务器的所有SIPS记录,接着经过这个proxy服务器的SIPS请求就会失效。这时候,一个用户,看见这个SIPS address-of-record总是失败,他可以手工改掉URI从SIPS改成SIP,并且重试。当然,这也有一些安全机制防止这类事情发生(如果目标UA真是有神经病拒绝所有非SIPS请求的话)。但是这个限制毫无价值。往好了想,用户也可以把SIP URI变成SIPS URI。
26.5 Privacy(隐私)
SIP消息经常包含发送者的敏感信息-不只是他们将说些什么,也包括了他们和谁在通讯,以及他们通讯了多久,以及从那里到哪里的通讯等等。很多应用和他们的用户都要求这类隐私信息对于没有必要知道的方面来说都必须保持隐秘。
注意,也有隐私信息也有少数直接泄漏的方式。如果用户或者服务,位于一个容易猜到的地址,比如通过用户的名字或者机构的联系(这是构成Address-of-record的最经常的情形),传统保证隐私性的方式是通过一个未列出的”电话号码表”来实现的。在呼叫方发起的会话邀请中,用户位置服务可以提供精确的被叫方的位置从而破坏了隐私;在实现上因此应当更严格,基于每用户的原则,根据不同的呼叫方给出不同的位置信息和可用性的信息。这是一个SIP需要解决的完整的问题。
在某些情况下,用户可能希望在通过身份验证时,在头域中隐藏个人信息。这可以应用于不仅是From和相关表现请求发起方信息的头域,也应用于To头域-他可能不能由快速拨号的nick-name转换成为最终的目的地信息,或者一个为扩展的一组目标的标志,当请求被路由的时候,每一个都可以从Request-URI上被移去,但是如果和To头域初始值相等的时候,不能更改To头域。因此,他可能由于隐私的原因创建和Request-URI不同的To头域。
27 IANA 认证
SIP应用中的所有的方法名字,头域名字,状态码,和option tags,都是通过RFC中关于IANA认证节的说明在IANA注册的。
本规范指示IANA创建了4个新的子注册项目在: http://www.iana.org/assignments/sip-parameters: Option Tags,Warning Codes(warn-codes),Methods和Response Codes,头域的子项也在那里。
27.1 Option Tags
这个规范在 http://www.iana.org/assignments/sip-parameters建立了Option Tags注册项.
Option tags用于类似Require,Supported,Proxy-Require,Unsupoprted这类的头域,用来支持SIP的扩展兼容性机制(19.2节)。Option tag自身时一个字符串,和特定的SIP选项相关(也就是和特定的SIP扩展相关)。它为SIP终端确定这个选项。Option tags当被标准的RFC轨迹扩展,它就在IANA注册了。RFC的IANA认证节必须包含如下内容,这些内容与RFC出版号码一起在IANA注册表登记。
o option tag的名字。名字可以是任意长度的,但是应当不要超过20个字母。丙子必须是alphanum(25节)字符。
o 描述扩展的描述信息
27.2 Warn-Codes
本规范在 http://www.iana.org/assignments/sip-parameters建立了Warn-Codes注册项。并且初始发布的warn-codes值在20.43节列出。附加的warn-codes通过RFC出版物注册发表。
对于warn-codes表的描述信息如下:
当一个会话描述协议(SDP)(RFC 2327[1])出现问题导致事务失败的时候,Warning codes在SIP应答信息中提供了对状态码的补充信息。
“warn-code”包含了3位数字。第一个数字”3”表示warning是SIP的警告信息。除非以后另有描述,只有3xx警告信息可以被注册。
300到329的警告信息为会话描述保留字错误保留,330到339为会话要求基本网络服务错误保留,370到379是为会话描述要求的QoS参数数量错误保留,390到399是无法归类的杂项警告信息。
27.3 头域名
本规范在 http://www.iana.org/assignments/sip-parameters建立了头域的注册项。
为了注册一个新的头域名,下列信息需要在RFC出版物中提供,
o 头域注册的RFC号码
o 注册的头域名
o 如果有缩写,头域的缩写版本。
部分常用的头域可以缩写成1个字母的简写形式(7.3.3节)。简写形式只能在SIP工作组复查的时候被登记在RFC出版物上。
27.4 方法和应答码
本规范在 http://www.iana.org/assignments/sip-parameters建立了Method和Response-code的注册项。并且下边列出了他们的初始值。初始的方法表如下:
INVITE [RFC3261]
ACK [RFC3261]
BYE [RFC3261]
CANCEL [RFC3261]
REGISTER [RFC3261]
OPTIONS [RFC3261]
INFO [RFC2976]
应答码的初始值在21节列出,分为信息部分(Informational),成功(Success),转发(Redirection),客户端错误(Client-Error),服务端错误(Server-Error),全局错误(Global-Failure)几个部分。这个表格有如下格式:
类型(Type,就是Informational), Number, Default Reason Phrase [RFC3261]
如下信息需要提供给RFC出版机构来注册新的应答码或者方法:
o 应答码和方法需要注册的RFC号码
o 应答码号码或者方法名
o 缺省应答码的原因说明
27.5 “message/sip” MIME类型
本文档注册了”message/sip”MIME媒体类型,目的是允许SIP可以通过SIP包体隧道,首要用于端到端的安全保障。这个媒体类型由如下部分组成:
媒体类型名字: message
媒体子类型: sip
要求的参数: none
可选的参数: version/Encoding scheme/Security considerations
version: 这个打包的消息的SIP-version号码(比如”2.0”)。如果没有提供,version的缺省值就是”2.0”。
Encoding scheme: SIP消息包含一个8位的头,在二进制的MIME数据对象后边可选。同样的,SIP消息必须把这个当作一个二进制值。在通常情况下,SIP消息通过二进制兼容的通讯协议传送,不需要特别的编码方式。
Security considerations: 本参数用于同23.4给出的S/MIME安全机制一致,请参见下边的例子和说明。
27.6 新Content-Disposition 参数注册
本文档也注册了4个新的Content-Disposition头”disposition-types”: altert,icon,session和render。作者要求这些值在IANA中为Content-Disposition登记。
对于”disposition-types”的描述,包括例子和说明,在20.11中说明。
在IANA注册中的简单描述是:
alter 包体是一个客户定义的铃声用于提醒用户。
icon 包体是一个显示给用户的icon
render 包体应当显示给用户
session 包体描述了一个通讯会话,例如RFC2327 SDP包体
28 同RFC 2543的改变
这个RFC修订了RFC 2543。它主要和RFC2543向后兼容。这里描述的变更主要修订了RFC2543中发现的错误,并且提供了在RFC2543中没有提及的相关情节信息。在本文中,协议以更清晰的层次结构描述。
我们把和RFC2543的永久改变,分成按功能行为划分这些区别。比如在互操作上不兼容,或者修正了某些情况下的错误操作,以及和RFC2543功能行为不同但是不是一个潜在的兼容性问题。以及有数不清楚的澄清,在本文中没有提及。
28.1 主要的功能改变
o 在UAC还没有给出应答之前,UAC希望终止一个会话,它发送CANCEL来终止。如果原始INVITE依旧给出2xx应答,那么UAC接着发送BYE。BYE只能发给现存的呼叫对话(leg)中(在这个RFC中成为对话(dialog)),但是在RFC2543中可以在任何时间发送BYE。
o SIP的BNF改成RFC2234兼容的了。
o SIP URL的BNF更改的更加通用,在user部分,允许一个很大的字符集。此外,比较规则也简化为首先大小写不敏感,接着详细比较各个参数。最主要的变化是带参数的URI和不带这个参数(但是缺省值相等)的URI 判定是不相等的。
o 删除了隐藏的Via。这要求发出绝对的信任,由于它依赖于下一个节点来执行这个不明确的操作。作为替代,Via隐藏可以通过在proxy服务器上的本地实现选择完成,于是在这里不在说明。
o 在RFC2543,CANCEL和INVITE请求是混和的。他们现在分开了。当用户发出一个INVITE请求接着CANCEL掉,INVITE事务将正常终结。UAS应当对原始INVITE请求给出一个487应答。
o类似的,CANCEL和BYE事务也是混和的;RFC2543允许UAS在收到BYE的时候,不给INVTE请求发送应答。本文档不允许这种情况出现。原始的INVITE请求需要一个应答。
o 在RFC2543中,UA需要支持只有UDP的情况。在这个RFC中,UA应当支持UDP和TCP。
o 在RFC2543中,forking 分支proxy在收到多个拒绝应答的时候,只向下行元素发出一个拒绝身份认证应答,在这个RFC中,proxy应当搜集所有的拒绝并且把他们放在应答中发送。
o 在Digest信任书中,URI需要被引号引起来;这个在RFC2617和RFC2069是不一致的。
o SDP处理被分为独立的规范[13],并且更全面的描述了正式的通过SIP包体隧道进行的offer/answer交换过程。作为SIP实现基线,SIP允许在INVITE/200或者200/ACK中存在;RFC2543间接指出了在INVITE,200和ACK中的单个事务使用这个方法,但是这个在RFC2543中没有很好的定义。在SIP扩展中允许使用更复杂的SDP。
o 增加了对URI中和在Via头域中的IPV6的全面支持。Via头域允许方括号和冒号的参数,要求对Via头域的IPV6的支持。这些字符在先前是不允许的。在理论上,这颗一导致和旧规范实现上的相互操作的问题。不过,我们观察到大部分实现在这个参数上,接受非控制ASCII字符。
o DNS SRV处理步骤现在使用了单独的规范[4]。这个步骤使用了SRV和NAPTR资源记录,并且不在合并从SRV记录的数据(RFC2543)。
o 循环检测变成可选的了,替代为强制使用Max-Forwards参数。这个循环检测在RFC2543由严重bug,可能会在正常情况下把”螺旋”报告成为一个错误。在这里,可选的循环检测步骤是更加全面的和正确的。
o 由于他们现在在对话中是作为识别对话的基本要素存在,所以,对tag的使用是强制的(他们在RFC2543中是可选的)。
o 增加了Supported头域,允许客户端向服务器表示它支持什么样的扩展,服务器可以根据这个字段决定给出包含什么样扩展的应答,并且把他们需要的扩展支持放在Require字段里边。
o 几个头域的扩展参数的BNF描述在RFC2543中忽略了,这里增加了。
o 处理Route和Record-Route的构在在RFC2543中是很不规范的,并且也不正确。它在本规范中更改成为规范正确的(并且大大简化了)。这是很大的改变。对于没有使用”预先加载路由的”部属情况下(在预先加载路由的时候,初始的请求有一个Route路由集合,这个集合不是由Record-Route构造的),依旧提供了向后兼容性。在预先加载路由的情况下,新旧机制是不能互相操作的。
o 在RFC2543中,消息中的行是可以由CR/LF/或者CRLF终端的。本规范只允许CRLF。
o 在CANCLE和ACK请求中的Route的使用,在RFC2543中没有精确定义,在本文档中定义了;如果请求有一个Route头域,他的为一个给请求的非2xx应答的CANCEL或者ACK应当包含相同的Route头域。为2xx应答的ACK请求使用2xx应答的Record-Route构造Route头域值。
o RFC 2543允许一个UDP包中多个请求。这个使用方法在本文档中删去了。
o 对于Expires头域和参数中的绝对时间的使用方式被删去了。当各个节点之间时间不同步的时候,他会带来户操作的问题。本文档使用相对时间。
o Via头域中的branch参数现在是强制每一个元素都使用。它现在作为事务的唯一标志。这避免了RFC2543容易出错的事务鉴别规则的复杂性。我们在这个参数值使用一个乱数cookie来检查上一个节点产生的这个参数是否全局唯一的,如果不是,那么旧采用旧的比较规则。因此,保证了互操作性。
o 在RFC2543,对TCP连接的关闭和CANCEL是相同的。当TCP连接在两个proxy之间的时候,这在实现上是几乎不可能的(也是错误的)。这个要求在这里被删去了,所以,TCP连接状态和SIP处理之间没有直接关系。
o RFC2543中,当UA到一个节点可以初始化一个新的事务,当对方正在进行事务处理的时候。在这里被精确定义了。只允许初始化非INVITE请求,不允许INVITE请求。
o PGP被删去了。它没有很充分的定义,并且和更复杂的PGP MIME不兼容。替换成为S/MIME。
o 增加了”sips” URI方案用于端到端的TLS通讯。这个方案是和RFC2543不能年个兼容的。现有的节点如果接收到请求的Request-URI中包含SIPS URI方案,将拒绝这个请求。这实际上是一个特性;它保证了对SIPS URI只会在所有节点都是安全的情况下被传送。
o 在TLS上附加了安全特性,并且这里也更广泛更完整的描述了安全考虑。
o 在RFC 2543中,并不要求proxy转发101到199的临时应答到上行队列。这里被更改成为必须转发。这是非常重要的,因为很多后续的特性都依赖于传送所有的101到199的临时应答。
o RFC 2543提及了很少的503应答。它用于标志proxy失败或者过载的情况。这要求某些特别的处理。尤其是,接收到503的接受方应当触发一个对于下一个节点在DNS SRV的重新查找。同样的,503应答只由proxy服务器在特定情况下转发到上行队列。
o RFC2543定义了,但是没有充分指出,对于UA认证一个服务器的机制。这个已经删去了。作为替代,允许使用RFC2617的双向认证步骤。
o 对于UA来说,除非它收到了初始请求的ACK,不能发送BYE到一个呼叫。这个在RFC2543是允许的,但是可能导致潜在的空转可能。
o UA或者proxy不能发送CANCEL到一个事务,直到它得到了一个临时应答。这个在RFC2543中是允许的,但是可能导致空转的可能。
o 在注册中的action参数被禁止使用了。它不足以提供任何有用的服务,并且会导致在proxy上的应用程序处理上的冲突。
o RFC2543对于multicast有一堆特别的情况。例如某个应答被禁止了,定时器调整了,等等。multicast现在受到更多限制,并且同unicast相反,协议操作不影响对multicast的使用。
o 基本的认证整个被删除了,不允许用基本的认证。
o proxy不再立刻收到就转发6xx应答。他们立刻CANCEL相关等待的分支。这也避免了潜在的空转,使得UAC再2xx之后收到一个6xx。在除了这个空转的情况,结果都必须是相同的-6xx转发到上行队列。
o RFC2543并不对请求的合并认为是一个错误。这就导致在proxy上分支的请求稍后会在一个节点合并。只有在UA上能进行合并操作,并且处理步骤定义的是除了第一个请求都统统拒绝。
28.2 小功能性的变更
o 为给用户展示可选的内容,增加了Alert-Info,Error-Info,Call-Info头域。
o 增加了Content-Language,Content-Disposition和MIME-Verison头域。
o 增加了一个”glare hanling”机制来处理双方同时都向对方提出一个re-INVITE。它使用信的491(Request Pending)错误码。
o 增加了 In-Reply_To和Reply-To头域来支持稍后对未接呼叫/消息的返回。
o 在SIP通讯协议中增加了TLS和SCTP。
o 描写了很多机制来处理呼叫中的发生的错误;现在做了统一的处理。BYE用来发送会话终结。
o RFC2543要求INVITE应答通过TCP重新传送,但是注意到实际上只对2xx应答是需要的。这就导致了人为的协议不足。通过定义了一个一致的事务层,这就不在需要了。只有给INVITE的2xx应答需要基于TCP重传。
o 客户端和服务端事务机器现在基于超时机制,而不是基于重传次数。这允许状态机能够更好的适应TCP和UDP。
o Date头域在REGISTER应答中使用,提供一个简单的自动配置UA的日期的机制。
o 允许注册服务器拒绝过小的超时时间的注册。并且为此定义了423应答码和Min-Expires头域。
26.2 安全机制
从上边讲述的威胁来看,我们得到了SIP协议所需要的基本安全服务,他们是:保护消息的隐私性和完整性,保护重现(replay)攻击或者消息欺骗,提供会话参与者的身份认证和隐私保护,保护拒绝服务攻击。SIP消息中的包体分别要求机密性,完整性和身份认证。
比为SIP重新定义新的安全机制更好的是,SIP可以重用已经存在的HTTP或者SMTP的安全机制。
全加密的消息提供了最好的机密保护-它也可以保证消息不被恶心的中间节点更改。不过SIP请求和应答不能简单的进行端到端的加密,因为在大多数网络架构下,消息的头域比如Request-URI,Route,Via在中间经过的proxy中需要可见,这样SIP请求才能被正确路由。注意,proxy服务器需要修改一些消息的特定属性(比如增加Via头域),这样才能保证SIP正常工作。那么proxy服务器就必须被UA信任,至少在某种程度上信任。为了这个目标,我们建议为SIP提供低层次的安全机制,他们是基于节点到节点的整个SIP请求和应答的在线加密,并且语序终端节点校验他们发出请求的proxy服务器的身份。
SIP实体也可以为安全保证,需要验证对方的身份。当SIP终端向对方UA或者proxy服务器,声明它的用户的身份时,这个身份应当是可以通过某种方法验证的。SIP中的数字签名机制就是为了这个需要的。
SIP消息体的一个独立的安全(加密)机制提供了另一个端到端的相互认证方式,也降低了UA必须信任中间节点的程度。
26.2.1 通讯和网络层的安全
通讯或者网络层的安全机制是加密信号通讯,保证消息机密的和完整的传送。
很多情况下,低层次的安全是通过证书实现的,这些证书可以在很多架构下用于提供身份认证使用。
两个通常使用的方法,提供了通讯层和网络层的安全,他们是TLS[25]和IPSec[26]。
IPSec是一组网络层的协议工具,他们可以一起使用来作为传统IP通讯的安全替代。IPSec最常用于一组主机或者管理的域有一个现存的互相信任关系的架构下。Ipsec通常由主机的操作系统级别实现,或者在一个提供机密通讯和完整性保证的通讯安全网关上实现(比如VPN结构),IPSec也可以用于点到点的结构。
在很多结构下,IPSec并不要求和SIP应用一起使用;IPSec可能是最适合于部属在那种难于直接在SIP服务器上增加安全性的情况。具有预先共享密钥关系的UA和他们的第一个节点的proxy服务器很适合使用IPSec。为SIP部属的IPSec要求一个IPSec 描述了协议工具的profile。这个profile在本文档中没有提供。
TLS提供了通讯层的安全性,基于连接相关的协议(TCP)。可以通过在Via头域或者在一个SIP URI中列明”tls” (表示基于TCP的TLS)指定通讯协议为TLS。TLS最适合没有事先定义的信任关系的点到点的结构。例如Alice 信任她的本地proxy服务器,这个服务器在进行证书交换后信任Bob的本地proxy服务器,这个Bob的本地proxy服务器是Bob信任的,因此Bob可以和Alice安全的通讯。
TLS必须和SIP应用紧紧联系在一起。注意在SIP中的通讯机制是点到点的,因此一个基于TLS发送请求到proxy服务器的UA并不能保证这个TLS会在端到端的应用。
当实现者在SIP应用中使用TLS的时候,实现者必须支持最小集合的TLS_RSA_WITH_AES_128_CBC_SHA密码套件[6]。并且为了向后兼容,proxy服务器,重定向服务器和注册服务器应当支持TLS_RSA_WITH3DES_EDE_CBC_SHA。实现者也可以支持其他密码套件。
26.2.2 SIPS URI方案
SIPS URI方案是SIP URI(19节)语法的一个附加,虽然这个方案串是”sips”不同于”sip”。SIPS的语义和SIP URI的语义十分不同。SIPS 允许指定希望通过安全访问的资源。SIPS URI可以当作一个特定用户的address-of-record使用-这个用户是已知的(根据他们的名片,在他盟请求的From头域,在REGISTER请求的To头域)。当在请求中使用Request-URI,SIPS 方案指出请求经过的每一个节点,知道请求到达目的这个Request-URI指明的SIP元素,必须通过TLS进行加密;当请求抵达目标的域,他会根据目标域的本地安全策略和转发策略,很有可能最后一部也是用TLS到达UAS。当用在请求的发起方(就像这种情况,当他们使用SIPS URI当作目标的address-of-record一样),SIPS只是这个实体请求,到目的主机的所有路径都应当加密。
SIPS方案适用于很多在SIP中应用的SIP URI,比如附加域Request-Uri,包含在address-of-record,联系地址(Contact的内容,包含REGISTER方法的头域等等),还有Route头域等等。在每个用法中,SIPS URI方案允许这些存在的URI来指明需要安全访问的资源。这些由SIPS URI所替换的东西,有他们自己的安全属性([4]中详细介绍)。
对SIPS的使用在细节上要求必须具备TLS互相的认证,并且要求支持密码套件TLS_RSA_WITH_AES_128_CBC_SHA。在认证过程中接收到的信任书应当从客户端持有的信任书跟节点开始验证;对信任书验证失败应当导致请求的失败。
注意在SIPS URI方案中,通讯层是和TLS没有依赖关系的,并且因此”sips:alice@atlanta.com;transport=tcp”和”sips:alice@atlanta.com;transport=sctp”都是合法的(虽然注意到UDP不能用于SIPS的传送)。我们不建议使用类似”transport=tls”的方式,部分原因是因为这是用于请求的单个节点之间的通讯。这是从RFC2543的一个变化。
将address-of-record用SIPS URI发出的用户,如果在非可靠通讯协议上收到的请求,可以操作设备来拒绝这个请求。
26.2.3 HTTP Authentication
SIP提供了认证机制,基于HTTP认证的身份认证机制,他们依赖于401倒407应答码和相关头域来提供拒绝不信任的信任书。对于SIP使用的HTTP Digest认证机制,并没有做重大的修改,它提供了replay攻击的保护和单向认证关系。
对SIP的Digest 认证使用在22节有描述。
26.2.4 S/MIME
就像上边讲述的,在端到端的过程中加密整个SIP消息体,可以提供机密性的保护,但是并非所有的字段都能使用这个机制进行保护,因为中间的网络节点(比如proxy服务器),需要根据读取适当的头域然后决定这个消息应当转发倒哪里,并且如果这些中间节点由于安全原因被排出在外,那么SIP消息从本质上就是不能路由的。
不过,S/MIME允许SIP 的UA在SIP中加密MIME包体,在不影响消息头的情况下,在端到端的通讯中加密这些MIME包体。S/MIME可以提供消息体的端到端的完整性和机密性,同样也提供了双向的认证机制。使用S/MIME也可以通过SIP消息隧道,为SIP头域提供一个完整性和机密性的方案。
对SIP的S/MIME使用在23节讲述。
26.3 安全机制的实现
26.3.1 对SIP实现者的要求
proxy服务器,重定向服务器,和注册服务器必须实现TLS,并且必须支持双向的和单向的认证关系。强烈建议UA可以初始化TLS;UA同样可以作为一个TLS服务器。proxy服务器,重定向服务器和注册服务器应当有一个站点信任书,这个信任书的主题和他们的规范主机名相关。对于TLS的双向认证,UA可以有他们自己的信任书,但是本文档中,没有规定他们的具体用法。所有的支持TLS的SIP元素必须具备在TLS协商中,验证信任书的机制;这个使得证书机关(可能是有名的类似web浏览器证书发行机构的发行机构)发布的一个或者多个根信任书成为必须。所有支持TLS的SIP元素必须同样支持SIPS URI方案。
Proxy服务器,重定向服务器,注册服务器,和UA可以实现IPSec或者其他底层的安全协议。
当UA试图联系一个proxy服务器,重定向服务器或者主阿服务器,UAC应当初始化一个TLS连接,在这个连接上发起SIP消息。在某些结构吓,UAS可以同样在这些TLS接收请求
Proxy服务器,重定向服务器,注册服务器,和UA必须实现Digest身份认证,包括所有的22节要求的要点。Proxy服务器,重定向服务器,注册服务器应当配置成为至少有一个Digest realm,并且对于给定服务器来说,必须支持至少有一个”realm”字符串和这个服务器的主机名或者hostname相关联。
UA可以支持MIME包体的加密,并且通过23节描述的那样使用S/MIME传送信任书。如果UA具有一个或者多个根身份认证的信任书,用来鉴定TLS或者IPSec的信任书,它应当适当的可以用这些来鉴定S/MIME的信任书。UA可以为S/MIME身份认证而具有特定的根信任书。
注意,随着S/MIME实现,将来会有安全扩展,来提高S/MIME的强度。
26.3.2 安全解决方案
这些安全机制的操作,可以在某种程度上和现存的WEB和EMAIL安全模式一致。在高一点的级别来看,UA通过Digest 用户名和口令把他们自己的身份向服务器(proxy服务器,重定向服务器,注册服务器)认证;服务器把他们自己向UA单节点认证,或者向另外一个服务器进行单节点(one hop)认证(反之亦然),并且是通过TLS来传送服务器节点信任书。
在点对点的级别,UA一般信任网络来进行对方身份的鉴别;不过,如果网络不能够鉴定对方身份,或者网络本身不被信任的情况下,也可以使用S/MIME来提供直接的身份认证。
接下来是一个例子,在这个例子中,不同的UA和服务器使用这些安全机制防止26.1节描述的攻击威胁。实现者和网络管理员可以遵循本节末尾给出的指示来防止攻击威胁,这些指示是作为实现例子提供的。
26.3.2.1 注册
当UA上线,并且注册到它自己的域上,它应当和它的注册服务器建立一个TLS连接(10节描述了UA怎样找到它的注册服务器)。注册服务器应当提供一个信任书给UA,并且这个信任书的节点必须是这个UA想要注册的域相关的信任书节点;例如,如果UA向注册 alice@atlanta.com这个address-of-record,这个信任书节点必须是一个atlanta.com域的主机(比如sip.atlanta.com)。如果它收到了TLS信任书消息,UA应当校验这个信任书,并且检查这个信任书的节点。如果信任书是非法的,作废的,或者它和持有者不符,UA必须不能发送REGISTER消息和进行注册处理。
当UA收到注册服务器提供的一个有效的信任书,UA知道注册服务器并非一个攻击者(可能重定向、窃取口令、或者试图做类似攻击的攻击者)。
于是UA创建了一个REGISTER请求,并且Request-URI应当指向从注册服务器所接收到的信任书站点。当UA通过刚才建立的TLS连接发送REGISTER请求,注册服务器应当给出一个401(Proxy Authentication Required)应答。在这个应答中,Proxy-Authenticate头域的”realm”参数,应当和前边给出的信任书节点的域相同。当UAC收到这个拒绝,它应当提示给用户要求信任书,或者根据应答中的”realm”参数,从现有的密钥组中查找对应的信任书。这个信任书的用户名应当和REGISTER请求的To头域的URI的”userinfo”部分相关。当在一个合适的Proxy-Authorization头域中插入和这个信任书,REGISTER应当重新发送到注册服务器。
由于注册服务器要求UA认证它自己,对于攻击者来说,伪造一个用户的address-of-record的REGISTER请求是很困难的。同样注意到由于REGISTER是通过机密的TLS连接发送的,攻击者不能通过截取REGISTER来记录信任书来进行重放攻击。
当注册请求被注册服务器接收,UA应当继续保持TLS连接,这样使得注册服务器可以既当作proxy服务器,这个proxy服务器可以作为管理这个域的proxy服务器。刚完成注册的TLS连接会继续保留用于接收UA后续发起的请求。
由于UA已经通过在TLS连接的对方的服务器的认证,所有在这个连接上的请求都是经过proxy 服务器的(由于保留了TLS连接,也就是说,刚才的注册服务器更换了角色,变成一个proxy服务器)--攻击者不能伪造好像是刚才从这个proxy服务器发送的请求。
26.3.2.2 在域之间的请求
现在我们说,Alice的UA希望和远端管理的域的一个用户,这个用户叫做” bob@biloxi.com”,初始化一个会话。我们讲会说本地管理的域(atlanta.com)有一个本地外发proxy。
对于一个管理域的Proxy服务器处理那发请求,可以同样作为本地的外发proxy;基于简单的原则,我们假定这就是atlanta.com(否则这时候UA将要初始化一个新的TLS连接到一个独立的服务器)。假定这时候,这个客户端已经完成了注册(参见前边的步骤),当它发出INVITE请求邀请另外一个用户的时候,它将重用这个TLS连接到本地proxy服务器(刚才的注册服务器)。这个UA在INVITE请求中,将会重用刚才cache的信任书,这样可以避免不必要的要求用户输入信任书。
当本地的发外服务器验证了这个UA在INVITE请求中的信任书,它应当检查Request-URI来决定这个消息应当如何路由(参见[4])。如果这个Request-URI的”domainname”部分和一个本地域相关联(atlanta.com)而不是和biloxi.com相关,那么proxy服务器会向本地服务查询来决定怎样最好的访问到被呼叫的用户。
“ alice@atlanta.com”正在尝试联系呼叫” alex@atlanta.com”,本地proxy将会转发这个请求到Alex和它的注册服务器所建立的TLS连接上。由于Alex将会通过它的已经通过认证的连接上收到这个请求,它就确定这个Alice的请求是通过了本地管理域的proxy的身份验证的。
不过,在这个例子中,Request-URI指向的是一个远程域。在atlanta.com的本地外发服务器应当因此而建立一个和在biloxi.com的远程proxy服务器的TLS连接。由于这个TLS连接的两端都是服务器,并且都有服务器的信任书,那么应当使用双向的TLS身份认证。连接的双方应当验证和检查对方的信任书,把在信任书中的域名同SIP消息中的头域做比较。例如,atlanta.com 这个proxy服务器,在这步,应当检查从对方接收到的关于biloxi.com域的信任书。当检查完毕,并且TLS协商完成,就建立了基于两个proxy的安全通道,atlanta.com proxy于是可以把INVITE请求转发给biloxi.com了。
在biloxi.com的proxy服务器应当检查atlanta.com的信任书,并且比较信任书提供的域名和INVITE请求的From头域的”domainname”部分。这个biloxi proxy可以执行严格的安全机制,拒绝那些他们被转发的域和本proxy所管理的域不匹配的请求(这个不太明白)。
这些安全机制可以用来防止SIP和SMTP ‘open relays’一样经常被用于产生垃圾邮件一样的信息。
这个政策,只是保证了请求声明的来源确实是它自己;他并不允许biloxi.com确知如何atlanta.com认证的Alice。只有当biloxi.com由其他方法知道atlanta.com的身份认证机制,他才可能确知Alice如何证明她的身份的。biloxi.com可以接着使用更严格的方法,禁止来自未确定和biloxi.com相同的认证策略的域的请求(就是说所有biloxi.com接受的请求,都必须来自biloxi.com所知道身份认证方式的域)。
当INVITE请求被biloxi.com核准,proxy服务器应当鉴别现存的TLS通道,如果存在现存的TLS通道,并且是和这个请求中的被叫用户(在这个例子中是 bob@biloxi.com)相关联的。那么这个INVITE请求应当通过这个TLS通道发送给Bob。由于通过这个TLS连接收到的请求,这个TLS连接是刚才已经在biloxi proxy上通过了身份认证,Bob于是知道From头域没有被篡改,并且atlanta.com已经认证了Alice,所以就没有必要犹豫是否信任Alice的身份。
在他们转发请求钱,两个proxy服务器应当在请求中增加Record-Route头域,这样所有在这个对话中的后续的请求讲过通过这两个proxy服务器。proxy服务器因此可以在对话生存周期中,继续提供安全服务。如果proxy服务器并不在Record-Route头域增加他们自己,以后的消息将会直接端到端的在Alice和Bob中发送,而没有任何安全措施(除非两个端点使用了某种独立的端到端的安全措施,比如S/MIME)。在这个考虑上,SIP梯形模式可以提供一个精美的结构来在proxy服务器节点之间进行磋商,以提供一个Alice和Bob之间的有道理的安全通道。
例如,一个攻击这个结构的攻击者将会不能伪造BYE请求并且把他插入Bob和Alice的信令流,因为攻击者无从探听会话的参数,这也是由于通讯的完整性机制保证了Alice和Bob之间的通讯是机密的完整的。
26.3.2.3 点对点请求
另外,考虑这样一个情况,UA声称 carol@chicago.com没有一个本地外发proxy。当Carol希望发送INVITE到 bob@biloxi.com,她的UA应当直接初始化一个TLS连接到biloxi proxy(使用附件[4]中描述的机制来检查怎样到达指定的Request-URI)。当她的UA收到一个biloxi proxy发送的信任书,她应当在通过TLS连接发送她的INVITE请求前,正常校验这个信任书。不过,Carol并没有义务相biloxi proxy提供她自己的身份,但是她在INVITE请求的”message/sip”包体中,有一个CMS-detached(分离的)签名。如果Carol在biloxi.com realm有其他信任书的话,就不一定提供这个签名,但是她在biloxi.com上没有正式关系,所以她没有信任书,也就必须提供这个签名。biloxi proxy可以有严格的机制直接把这个请求踢掉,甚至不用麻烦被叫方来验证这个请求,因为在From头域的”domainname”部分,并没有biloxi.com-它可以被当作这个用户是未认证的。
biloxi proxy对于Bob用户有一个政策,就是所有未认证的请求都应当转发到一个适当的地址,对于 bob@biloxic.om,就是<sipo:bob@192.0.2.4>。Carol在和biloxi proxy建立的TLS连接上,收到这个转发请求的应答,于是它信任这个转发地址的真实性。
Carol应当和目标地址建立一个TCP连接,并且发送一个新的INVITE请求,在这个请求中,Request-URI包含刚才接收到的联系地址(需要重新计算包体中的签名,因为请求重新构造了)。Bob从不安全的界面上接收到这个INVITE请求,但是这个UA是特意预留一个不安全的界面,在这个情况下,认可请求中的From头域并且随后把INVITE包体中的签名和一个本地cache的信任书进行匹配。Bob用类似的方法应答,把他自己相Carol进行认证,这样一个安全的对话就开始了。
某些情况下,在一个域的NAT或者防火墙会阻止UA之间直接建立TCP连接。在这个情况下,如果本地策略许可,proxy服务器可以隐含的做UA之间的请求转发,并且这个转发是基于没有信任关系的(比如不用现存的TLS连接,而是通过TCP明码的请求转发)
26.3.2.4 DoS 防护
基于这些安全解决方案,为了使得拒绝服务(DoS)攻击造成的影响最小,实现者应当注意如下的指引:
当SIP proxy服务器所在的主机是基于公共internte做路由的,他应当部署在一个具有防护操作策略的管理域中(比如block源路由,过滤ping包等等)。TLS和IPSec都可以在管理域的边界的防护主机,一起参与安全系统的构家,来提高安全性。这些防护主机可以防护拒绝服务攻击,确保在管理域中的SIP主机不会被大量的消息阻塞。
不管使用什么样的安全措施,对proxy服务器的洪水消息攻击可以耗尽proxy服务器的资源,并且阻止发送到目的地的正确的请求。对于proxy服务器来说,每一个SIP事务都会好用一定的资源,对于有状态的服务器来说这个消耗会更大。因此,有状态的proxy比无状态的proxy更容易受到洪水攻击的影响。
UA和proxy服务器应当用一个401(Unauthorized)或者407(Proxy Authentication Required)拒绝有问题的请求,并且对这些有问题的请求不使用正常的应答重发机制,并且对这些未认证的请求把自己当作无状态的服务器使用。如果攻击者使用假的头域值(例如Via)指向一个第三方的被攻击的服务器,那么对于401(Unauthorized)或者407(Proxy Authentication Required)应答的重发可能正中攻击者的下怀。
总得来说,基于TLS签名的proxy服务器之间的双向认证机制,降低了中间节点潜在的风险,并且减少了可以用作拒绝服务攻击的伪造的请求或者应答。这也同样提高了攻击者利用无知的SIP节点进行放大攻击的难度。
26.4 限制
虽然有这些安全机制,在正确应用的时候,可以阻止很多攻击,但是对于网络管理者和开发者来说,也必须明白他们也会有很多限制的地方。
26.4.1 HTTP Digest
在SIP中对于HTTP Digest一个限制是Digest的完整性机制在SIP下运作的不是很完美。尤其是,他们提供了对消息中的Request-URI和方法的保护,但是并没有对UA希望提供加密的所有头域进行了保护。
RFC2617所提供的回放攻击保护对于SIP来说也有一些限制。例如,next-nonce机制,并不支持通过管道传送的请求。防止回放攻击应当使用nonce-count机制。
另一个HTTP Digest限制是realm的范围。当用户希望把他们自己的身份向一个资源(这个资源和他们有着预先存在的关系)进行认证的时候,Digest是有用的,它就像一个服务提供者,用户是一个客户端(这是十分常见的场景,因此Digest提供了一个十分有用的功能)。与之对应的,TLS的范围是基于域之间的,或者multirealm的,因为信任书通常是全局可验证的,所以UA可以在不需要预先存在关系的服务器上进行身份验证。
26.4.2 S/MIME
对于S/MIME的一个最大的限制是对终端用户来说,缺少广泛的公共密钥机构。如果使用的是自签名(selfsigned)信任书(或者信任书不能被对话的对方所验证),23.2节描述的基于SIP的密钥交换机制就容易遭受中间人攻击(man-in-the-middle),在这个中间人攻击中,攻击者可以悄悄检查和修改S/MIME包体。攻击者需要截取对话中,双方的第一个密钥交换,在请求和应答中移去现有的CMS-detached签名,并且插入另外的包含攻击者提供的信任书(但是看起来像是正确地address-of-record的信任书)的CMS-detached 签名。通讯的双方都回以为他们和对方交换了密钥,实际上他们互相有的只是攻击者的公钥。
必须明确注意到攻击者只能攻击双方的第一次的密钥交换-在后续的情况,密钥的变更对于UA来说就是不可见的了。同样使得攻击者难以窃听对话双方的以后的对话中(比如一天内的对话,周内的,或者一年的对话)。
SSH在第一次密钥交换的时候,也同样容易受到这个中间人攻击;但是,虽然众所周知SSH不完美,但是它确实提高了连接的安全性。对于SIP来说,使用密钥的指纹可以有些帮助,就像在SSH中一样。例如,如果SIP的双方建立了一个语音通讯会话,每一个都可以读取对方传送的密钥指纹,并且可以根据这个指纹和原始指纹做比较。这使得中间人更加难以在语音中模拟通话双方的密钥指纹(实际上这个在Clipper 基于芯片的保密电话中使用)。
在S/MIME机制下,如果UA在他们的密钥组中持有对方address-of-record的信任书,允许UA不用导言来发送加密的请求。不过,存在这个样的情况,如果某个设备注册了这个address-of-record,并且没有持有持有设备当前用户先前使用的信任书。并且它将因此不能正常处理加密的请求,于是可能会导致某些原本可以避免的错误信号。这很像加密的请求被分支的情况。
S/MIME相关的密钥通常用于和特定用户(一个address-of-record)关联起来使用,而不是和一个设备(UA)一起使用。当用户在设备之间移动,很难把私钥安全的在UA之间传递;一个设备如何获得这些密钥不在本文讨论。
另外,使用S/MIME更常见的困难是,它可以导致很大的消息,特别是当采用23.4节的SIP隧道戒指的时候。基于这个原因,当使用S/MIME隧道机制的时候,一定要使用TCP通讯协议。
26.4.3 TLS
TLS最大的问题是,它不能基于UDP;TLS要求面向连接的底层通讯协议,对于本文来说,就是TCP。
对于TLS来说,在本地外发proxy服务器和/或者主车服务器上和大量UA,维持很多并发TLS长连接,是一件费力的事情。这导致某些容量问题,特别是在使用加强密钥套件的时候更容易出现容量问题。维持冗余的TLS长连接,特别是当UA独立负责他们的连接,会很耗资源。
TLS只允许SIP实体到他们临近的认证服务器的连接;TLS提供了严格的节点到节点的安全性(hop-by-hop)。无论TLS,还是其他本文档规定的安全机制,当不能直接建立TCP连接的情况下,都允许客户通过验证自己到proxy服务器来到达目的地。
26.4.4 SIPS URI
实际上在请求经过的每一段都使用TLS,要求了最终的UAS必须能够通过TLS到达(也许是通过SIPS URI作为一个联系地址注册的)。通常这个目的地是用SIPS描述的。在很多结构下,使用TLS保护一部分请求路径,最后一部确实依赖于其他的安全机制到UAS。因此SIPS不能保证TLS是真正的端到端的使用。注意,这是由于许多UA不接受进来的TLS连接,甚至那些支持TLS的UA也可能要求要维持一个永久的TLS连接(就像前边的TLS限制节讲述的一样),目的是为了作为UAS从TLS上接收请求。
位置服务,对于SIPS Request-URI来说,是不要求提供SIPS绑定的。虽然位置服务通常由用户注册(10.2.1节描述)组成,但是对于一个address-of-record来说,可以由不同的协议和界面都可以提供联系地址,并且这些工具都可以用来映射SIPS URI到适当的SIP URI。当对绑定信息查询的时候,位置服务返回它的联系地址,而不关心这个是否是从一个SIPS的Request-URI上收到的请求。如果是转发服务器访问位置服务,那就是说取决于处理转发的Contact头域的SIP实体来决定联系地址的属性。
如果要求请求经过的全部路径都使用TLS通讯,那么对目的域来说稍稍有点麻烦。如果实现困难,也允许在请求传送节点中的基于密码认证的proxy服务器,不兼容或者选择一个折中方案来略过SIPS的转发机制(在16.6节定义的通常转发规则)。但是,恶意的中间节点就有可能把一个基于SIPS URI的请求,重新降级成为SIP URI。
另外,中间节点也可以正常的把一个基于SIP的请求更改成基于SIPS URI的请求。请求的接受方发现请求的Request-URI是基于SIPS URI方案的并不能假设原始请求的Request-URI也是基于SIPS通过中间节点传送的(从客户端往后)。
为了解决这个问题,我们建议请求的收件人,在请求的Request-URI包含一个SIP或者SIPS URI的情况下,检查To头域值,看看是否包含一个SIPS URI(虽然注意到Request-URI可以和To头域URI有相同的URI方案,但是他们不相等并不意味者是一个安全隐患)。虽然客户端可以在一个请求中把Request-URI和To头域做成不一样的,但是当两个字段的SIPS不一样的话,那就是可能的安全隐患,并且请求因此应当被接受方拒绝。接受方也可以检查Via头域链来双重检查是否TLS在整个请求路径中被使用,一直到最近的本地管理域。源UAC也可以用S/MIME来帮助确保原始格式的To头域被端到端的发送。
如果UAS有原因来相信Request-URI的URI方案在通讯中被非法修改,UA应当提示用户这个潜在的安全隐患。
作为更深远的考量,为了防止底层的攻击,如果SIP实体只接受SIPS请求,那么可以拒绝在非安全端口的连接。
终端用户应当完全清楚SIPS和SIP URI的区别,他们可以在应答中手工更改这个URI方案。这可以增加或者降低安全性。例如,如果一个攻击者攻陷了一个DNS的cache,插入了一个假的记录集,这个记录集有效删去了一个proxy服务器的所有SIPS记录,接着经过这个proxy服务器的SIPS请求就会失效。这时候,一个用户,看见这个SIPS address-of-record总是失败,他可以手工改掉URI从SIPS改成SIP,并且重试。当然,这也有一些安全机制防止这类事情发生(如果目标UA真是有神经病拒绝所有非SIPS请求的话)。但是这个限制毫无价值。往好了想,用户也可以把SIP URI变成SIPS URI。
26.5 Privacy(隐私)
SIP消息经常包含发送者的敏感信息-不只是他们将说些什么,也包括了他们和谁在通讯,以及他们通讯了多久,以及从那里到哪里的通讯等等。很多应用和他们的用户都要求这类隐私信息对于没有必要知道的方面来说都必须保持隐秘。
注意,也有隐私信息也有少数直接泄漏的方式。如果用户或者服务,位于一个容易猜到的地址,比如通过用户的名字或者机构的联系(这是构成Address-of-record的最经常的情形),传统保证隐私性的方式是通过一个未列出的”电话号码表”来实现的。在呼叫方发起的会话邀请中,用户位置服务可以提供精确的被叫方的位置从而破坏了隐私;在实现上因此应当更严格,基于每用户的原则,根据不同的呼叫方给出不同的位置信息和可用性的信息。这是一个SIP需要解决的完整的问题。
在某些情况下,用户可能希望在通过身份验证时,在头域中隐藏个人信息。这可以应用于不仅是From和相关表现请求发起方信息的头域,也应用于To头域-他可能不能由快速拨号的nick-name转换成为最终的目的地信息,或者一个为扩展的一组目标的标志,当请求被路由的时候,每一个都可以从Request-URI上被移去,但是如果和To头域初始值相等的时候,不能更改To头域。因此,他可能由于隐私的原因创建和Request-URI不同的To头域。
27 IANA 认证
SIP应用中的所有的方法名字,头域名字,状态码,和option tags,都是通过RFC中关于IANA认证节的说明在IANA注册的。
本规范指示IANA创建了4个新的子注册项目在: http://www.iana.org/assignments/sip-parameters: Option Tags,Warning Codes(warn-codes),Methods和Response Codes,头域的子项也在那里。
27.1 Option Tags
这个规范在 http://www.iana.org/assignments/sip-parameters建立了Option Tags注册项.
Option tags用于类似Require,Supported,Proxy-Require,Unsupoprted这类的头域,用来支持SIP的扩展兼容性机制(19.2节)。Option tag自身时一个字符串,和特定的SIP选项相关(也就是和特定的SIP扩展相关)。它为SIP终端确定这个选项。Option tags当被标准的RFC轨迹扩展,它就在IANA注册了。RFC的IANA认证节必须包含如下内容,这些内容与RFC出版号码一起在IANA注册表登记。
o option tag的名字。名字可以是任意长度的,但是应当不要超过20个字母。丙子必须是alphanum(25节)字符。
o 描述扩展的描述信息
27.2 Warn-Codes
本规范在 http://www.iana.org/assignments/sip-parameters建立了Warn-Codes注册项。并且初始发布的warn-codes值在20.43节列出。附加的warn-codes通过RFC出版物注册发表。
对于warn-codes表的描述信息如下:
当一个会话描述协议(SDP)(RFC 2327[1])出现问题导致事务失败的时候,Warning codes在SIP应答信息中提供了对状态码的补充信息。
“warn-code”包含了3位数字。第一个数字”3”表示warning是SIP的警告信息。除非以后另有描述,只有3xx警告信息可以被注册。
300到329的警告信息为会话描述保留字错误保留,330到339为会话要求基本网络服务错误保留,370到379是为会话描述要求的QoS参数数量错误保留,390到399是无法归类的杂项警告信息。
27.3 头域名
本规范在 http://www.iana.org/assignments/sip-parameters建立了头域的注册项。
为了注册一个新的头域名,下列信息需要在RFC出版物中提供,
o 头域注册的RFC号码
o 注册的头域名
o 如果有缩写,头域的缩写版本。
部分常用的头域可以缩写成1个字母的简写形式(7.3.3节)。简写形式只能在SIP工作组复查的时候被登记在RFC出版物上。
27.4 方法和应答码
本规范在 http://www.iana.org/assignments/sip-parameters建立了Method和Response-code的注册项。并且下边列出了他们的初始值。初始的方法表如下:
INVITE [RFC3261]
ACK [RFC3261]
BYE [RFC3261]
CANCEL [RFC3261]
REGISTER [RFC3261]
OPTIONS [RFC3261]
INFO [RFC2976]
应答码的初始值在21节列出,分为信息部分(Informational),成功(Success),转发(Redirection),客户端错误(Client-Error),服务端错误(Server-Error),全局错误(Global-Failure)几个部分。这个表格有如下格式:
类型(Type,就是Informational), Number, Default Reason Phrase [RFC3261]
如下信息需要提供给RFC出版机构来注册新的应答码或者方法:
o 应答码和方法需要注册的RFC号码
o 应答码号码或者方法名
o 缺省应答码的原因说明
27.5 “message/sip” MIME类型
本文档注册了”message/sip”MIME媒体类型,目的是允许SIP可以通过SIP包体隧道,首要用于端到端的安全保障。这个媒体类型由如下部分组成:
媒体类型名字: message
媒体子类型: sip
要求的参数: none
可选的参数: version/Encoding scheme/Security considerations
version: 这个打包的消息的SIP-version号码(比如”2.0”)。如果没有提供,version的缺省值就是”2.0”。
Encoding scheme: SIP消息包含一个8位的头,在二进制的MIME数据对象后边可选。同样的,SIP消息必须把这个当作一个二进制值。在通常情况下,SIP消息通过二进制兼容的通讯协议传送,不需要特别的编码方式。
Security considerations: 本参数用于同23.4给出的S/MIME安全机制一致,请参见下边的例子和说明。
27.6 新Content-Disposition 参数注册
本文档也注册了4个新的Content-Disposition头”disposition-types”: altert,icon,session和render。作者要求这些值在IANA中为Content-Disposition登记。
对于”disposition-types”的描述,包括例子和说明,在20.11中说明。
在IANA注册中的简单描述是:
alter 包体是一个客户定义的铃声用于提醒用户。
icon 包体是一个显示给用户的icon
render 包体应当显示给用户
session 包体描述了一个通讯会话,例如RFC2327 SDP包体
28 同RFC 2543的改变
这个RFC修订了RFC 2543。它主要和RFC2543向后兼容。这里描述的变更主要修订了RFC2543中发现的错误,并且提供了在RFC2543中没有提及的相关情节信息。在本文中,协议以更清晰的层次结构描述。
我们把和RFC2543的永久改变,分成按功能行为划分这些区别。比如在互操作上不兼容,或者修正了某些情况下的错误操作,以及和RFC2543功能行为不同但是不是一个潜在的兼容性问题。以及有数不清楚的澄清,在本文中没有提及。
28.1 主要的功能改变
o 在UAC还没有给出应答之前,UAC希望终止一个会话,它发送CANCEL来终止。如果原始INVITE依旧给出2xx应答,那么UAC接着发送BYE。BYE只能发给现存的呼叫对话(leg)中(在这个RFC中成为对话(dialog)),但是在RFC2543中可以在任何时间发送BYE。
o SIP的BNF改成RFC2234兼容的了。
o SIP URL的BNF更改的更加通用,在user部分,允许一个很大的字符集。此外,比较规则也简化为首先大小写不敏感,接着详细比较各个参数。最主要的变化是带参数的URI和不带这个参数(但是缺省值相等)的URI 判定是不相等的。
o 删除了隐藏的Via。这要求发出绝对的信任,由于它依赖于下一个节点来执行这个不明确的操作。作为替代,Via隐藏可以通过在proxy服务器上的本地实现选择完成,于是在这里不在说明。
o 在RFC2543,CANCEL和INVITE请求是混和的。他们现在分开了。当用户发出一个INVITE请求接着CANCEL掉,INVITE事务将正常终结。UAS应当对原始INVITE请求给出一个487应答。
o类似的,CANCEL和BYE事务也是混和的;RFC2543允许UAS在收到BYE的时候,不给INVTE请求发送应答。本文档不允许这种情况出现。原始的INVITE请求需要一个应答。
o 在RFC2543中,UA需要支持只有UDP的情况。在这个RFC中,UA应当支持UDP和TCP。
o 在RFC2543中,forking 分支proxy在收到多个拒绝应答的时候,只向下行元素发出一个拒绝身份认证应答,在这个RFC中,proxy应当搜集所有的拒绝并且把他们放在应答中发送。
o 在Digest信任书中,URI需要被引号引起来;这个在RFC2617和RFC2069是不一致的。
o SDP处理被分为独立的规范[13],并且更全面的描述了正式的通过SIP包体隧道进行的offer/answer交换过程。作为SIP实现基线,SIP允许在INVITE/200或者200/ACK中存在;RFC2543间接指出了在INVITE,200和ACK中的单个事务使用这个方法,但是这个在RFC2543中没有很好的定义。在SIP扩展中允许使用更复杂的SDP。
o 增加了对URI中和在Via头域中的IPV6的全面支持。Via头域允许方括号和冒号的参数,要求对Via头域的IPV6的支持。这些字符在先前是不允许的。在理论上,这颗一导致和旧规范实现上的相互操作的问题。不过,我们观察到大部分实现在这个参数上,接受非控制ASCII字符。
o DNS SRV处理步骤现在使用了单独的规范[4]。这个步骤使用了SRV和NAPTR资源记录,并且不在合并从SRV记录的数据(RFC2543)。
o 循环检测变成可选的了,替代为强制使用Max-Forwards参数。这个循环检测在RFC2543由严重bug,可能会在正常情况下把”螺旋”报告成为一个错误。在这里,可选的循环检测步骤是更加全面的和正确的。
o 由于他们现在在对话中是作为识别对话的基本要素存在,所以,对tag的使用是强制的(他们在RFC2543中是可选的)。
o 增加了Supported头域,允许客户端向服务器表示它支持什么样的扩展,服务器可以根据这个字段决定给出包含什么样扩展的应答,并且把他们需要的扩展支持放在Require字段里边。
o 几个头域的扩展参数的BNF描述在RFC2543中忽略了,这里增加了。
o 处理Route和Record-Route的构在在RFC2543中是很不规范的,并且也不正确。它在本规范中更改成为规范正确的(并且大大简化了)。这是很大的改变。对于没有使用”预先加载路由的”部属情况下(在预先加载路由的时候,初始的请求有一个Route路由集合,这个集合不是由Record-Route构造的),依旧提供了向后兼容性。在预先加载路由的情况下,新旧机制是不能互相操作的。
o 在RFC2543中,消息中的行是可以由CR/LF/或者CRLF终端的。本规范只允许CRLF。
o 在CANCLE和ACK请求中的Route的使用,在RFC2543中没有精确定义,在本文档中定义了;如果请求有一个Route头域,他的为一个给请求的非2xx应答的CANCEL或者ACK应当包含相同的Route头域。为2xx应答的ACK请求使用2xx应答的Record-Route构造Route头域值。
o RFC 2543允许一个UDP包中多个请求。这个使用方法在本文档中删去了。
o 对于Expires头域和参数中的绝对时间的使用方式被删去了。当各个节点之间时间不同步的时候,他会带来户操作的问题。本文档使用相对时间。
o Via头域中的branch参数现在是强制每一个元素都使用。它现在作为事务的唯一标志。这避免了RFC2543容易出错的事务鉴别规则的复杂性。我们在这个参数值使用一个乱数cookie来检查上一个节点产生的这个参数是否全局唯一的,如果不是,那么旧采用旧的比较规则。因此,保证了互操作性。
o 在RFC2543,对TCP连接的关闭和CANCEL是相同的。当TCP连接在两个proxy之间的时候,这在实现上是几乎不可能的(也是错误的)。这个要求在这里被删去了,所以,TCP连接状态和SIP处理之间没有直接关系。
o RFC2543中,当UA到一个节点可以初始化一个新的事务,当对方正在进行事务处理的时候。在这里被精确定义了。只允许初始化非INVITE请求,不允许INVITE请求。
o PGP被删去了。它没有很充分的定义,并且和更复杂的PGP MIME不兼容。替换成为S/MIME。
o 增加了”sips” URI方案用于端到端的TLS通讯。这个方案是和RFC2543不能年个兼容的。现有的节点如果接收到请求的Request-URI中包含SIPS URI方案,将拒绝这个请求。这实际上是一个特性;它保证了对SIPS URI只会在所有节点都是安全的情况下被传送。
o 在TLS上附加了安全特性,并且这里也更广泛更完整的描述了安全考虑。
o 在RFC 2543中,并不要求proxy转发101到199的临时应答到上行队列。这里被更改成为必须转发。这是非常重要的,因为很多后续的特性都依赖于传送所有的101到199的临时应答。
o RFC 2543提及了很少的503应答。它用于标志proxy失败或者过载的情况。这要求某些特别的处理。尤其是,接收到503的接受方应当触发一个对于下一个节点在DNS SRV的重新查找。同样的,503应答只由proxy服务器在特定情况下转发到上行队列。
o RFC2543定义了,但是没有充分指出,对于UA认证一个服务器的机制。这个已经删去了。作为替代,允许使用RFC2617的双向认证步骤。
o 对于UA来说,除非它收到了初始请求的ACK,不能发送BYE到一个呼叫。这个在RFC2543是允许的,但是可能导致潜在的空转可能。
o UA或者proxy不能发送CANCEL到一个事务,直到它得到了一个临时应答。这个在RFC2543中是允许的,但是可能导致空转的可能。
o 在注册中的action参数被禁止使用了。它不足以提供任何有用的服务,并且会导致在proxy上的应用程序处理上的冲突。
o RFC2543对于multicast有一堆特别的情况。例如某个应答被禁止了,定时器调整了,等等。multicast现在受到更多限制,并且同unicast相反,协议操作不影响对multicast的使用。
o 基本的认证整个被删除了,不允许用基本的认证。
o proxy不再立刻收到就转发6xx应答。他们立刻CANCEL相关等待的分支。这也避免了潜在的空转,使得UAC再2xx之后收到一个6xx。在除了这个空转的情况,结果都必须是相同的-6xx转发到上行队列。
o RFC2543并不对请求的合并认为是一个错误。这就导致在proxy上分支的请求稍后会在一个节点合并。只有在UA上能进行合并操作,并且处理步骤定义的是除了第一个请求都统统拒绝。
28.2 小功能性的变更
o 为给用户展示可选的内容,增加了Alert-Info,Error-Info,Call-Info头域。
o 增加了Content-Language,Content-Disposition和MIME-Verison头域。
o 增加了一个”glare hanling”机制来处理双方同时都向对方提出一个re-INVITE。它使用信的491(Request Pending)错误码。
o 增加了 In-Reply_To和Reply-To头域来支持稍后对未接呼叫/消息的返回。
o 在SIP通讯协议中增加了TLS和SCTP。
o 描写了很多机制来处理呼叫中的发生的错误;现在做了统一的处理。BYE用来发送会话终结。
o RFC2543要求INVITE应答通过TCP重新传送,但是注意到实际上只对2xx应答是需要的。这就导致了人为的协议不足。通过定义了一个一致的事务层,这就不在需要了。只有给INVITE的2xx应答需要基于TCP重传。
o 客户端和服务端事务机器现在基于超时机制,而不是基于重传次数。这允许状态机能够更好的适应TCP和UDP。
o Date头域在REGISTER应答中使用,提供一个简单的自动配置UA的日期的机制。
o 允许注册服务器拒绝过小的超时时间的注册。并且为此定义了423应答码和Min-Expires头域。