@@ -81,10 +81,62 @@ const applyHeaderRuleList = (
8181const applyHeaderValueRuleList = ( add : any , rules : any [ ] , value : string , rawValue : string , headerName : string ) => {
8282 if ( ! value || ! Array . isArray ( rules ) || ! rules . length ) return
8383
84+ // Server / X-Powered-By 字段正常只对应一个产品;带逗号往往是反代叠加或伪造
85+ // 只匹配第一段,避免被「openresty, Microsoft-IIS/10.0」这种伪造糊弄
86+ const primaryValue = headerName === 'server' || headerName === 'x-powered-by' ? value . split ( ',' ) [ 0 ] . trim ( ) : value
87+ if ( ! primaryValue ) return
88+
8489 for ( const rule of rules ) {
85- if ( ! matchesHeaderPatterns ( rule . patterns , value , rule ) ) continue
90+ if ( ! matchesHeaderPatterns ( rule . patterns , primaryValue , rule ) ) continue
8691 const evidence = rule . evidence || `${ headerName } : ${ rawValue } `
8792 add ( rule . category || '其他库' , rule . name , rule . confidence || '高' , evidence )
93+ if ( headerName === 'server' || headerName === 'x-powered-by' ) break // 这两个字段正常只标识一种产品
94+ }
95+ }
96+
97+ // 主体身份响应头:每一项理论上只对应一种栈,集齐很多个不同身份就是被伪造的强信号
98+ const SPOOF_INDICATOR_HEADERS = [
99+ 'server' ,
100+ 'x-powered-by' ,
101+ 'x-aspnet-version' ,
102+ 'x-aspnetmvc-version' ,
103+ 'x-drupal-cache' ,
104+ 'x-drupal-dynamic-cache' ,
105+ 'x-generator' ,
106+ 'x-powered-cms' ,
107+ 'x-varnish' ,
108+ 'x-rails-version' ,
109+ 'x-runtime' ,
110+ 'x-php-version' ,
111+ 'x-jenkins' ,
112+ 'x-cocoon-version'
113+ ]
114+
115+ const SPOOF_PRONE_CATEGORIES = new Set ( [ 'Web 服务器' , '网站程序' , '后端 / 服务器框架' , '开发语言 / 运行时' , 'CMS / 电商平台' ] )
116+
117+ const countSpoofIndicators = ( headers : Record < string , string > ) : number => {
118+ let count = 0
119+ for ( const name of SPOOF_INDICATOR_HEADERS ) {
120+ const value = headers [ name ]
121+ if ( typeof value === 'string' && value . trim ( ) ) count += 1
122+ }
123+ return count
124+ }
125+
126+ const SPOOF_NOTICE = '响应头里同时出现多种不同主体身份字段,识别结果可能被伪造'
127+
128+ const markSpoofedHeaderDetections = ( technologies : any [ ] , headers : Record < string , string > ) : void => {
129+ const indicatorCount = countSpoofIndicators ( headers )
130+ // server 自身就带多个产品 / 出现 4+ 个不同身份字段:视为伪造
131+ const serverHasMultiple = typeof headers . server === 'string' && headers . server . includes ( ',' )
132+ if ( indicatorCount < 4 && ! serverHasMultiple ) return
133+ for ( const tech of technologies ) {
134+ if ( ! SPOOF_PRONE_CATEGORIES . has ( tech . category ) ) continue
135+ tech . confidence = '低'
136+ const evidence : string [ ] = Array . isArray ( tech . evidence ) ? tech . evidence : tech . evidence ? [ tech . evidence ] : [ ]
137+ if ( ! evidence . some ( ( line : string ) => typeof line === 'string' && line . includes ( SPOOF_NOTICE ) ) ) {
138+ tech . evidence = [ SPOOF_NOTICE , ...evidence ]
139+ }
88140 }
89141}
90142
@@ -131,6 +183,8 @@ const detectFromHeaders = (headers: Record<string, string>, url: string, headerR
131183 lowerHeaderBlob
132184 )
133185
186+ markSpoofedHeaderDetections ( technologies , headers )
187+
134188 return technologies
135189}
136190
0 commit comments