/
flameeyes_60_fake_browsers.conf
297 lines (254 loc) · 16.1 KB
/
flameeyes_60_fake_browsers.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# -*- apache -*-
# Copyright © 2010-2013 Diego Elio Pettenò <flameeyes@flameeyes.eu>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# Checking if the browser provides all HTTP/1.1-compliant headers is
# usually very helpful; unfortunately a number of transparent proxies
# seem to mangle this badly, causing false positives. If using a
# proxy, bypass this section.
# A number of spambots tries to mimic known good User-Agent values,
# harvested either on honeypot sites, or through statistics viewers
# such as awstats. Often enough, they get them slightly wrong,
# mangling a few key characters, try to identify them and reject them
# on that basis.
SecRule REQUEST_HEADERS:User-Agent "@beginsWith mozilla/4.0+" \
"id:436001,phase:2,t:lowercase,msg:'Spaces converted to + symbols: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "@contains (compatible- " \
"id:436002,phase:2,t:lowercase,msg:'Semicolons replaced by dashes: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "\([^\)]+$" \
"id:436003,phase:2,msg:'Unterminated User-Agent string: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
SecRule Request_HEADERS:User-Agent "@endsWith ))" \
"id:436004,phase:2,msg:'Double-terminated User-Agent string: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
SecRule REQUEST_HEADERS:UserAgent "@contains ()" \
"id:436005,phase:2,msg:'Empty parenthesis in the User-Agent string.',logdata:'%{REQUEST_HEADERS.User-Agent}',deny,status:403"
# Yet another common mistake from false user-agents: they don't add
# the proper space after the first agent declaration (noticed with
# fake Opera strings, but it's a possible general mistake).
SecRule REQUEST_HEADERS:User-Agent "^[a-zA-Z]+/[0-9.]+\(" \
"id:436010,phase:2,t:none,msg:'No space before parenthesis: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
# MSIE reports a single .NET CLR token for each version of .NET that
# is installed, but only once per series.
#
# An exception seems to apply to .net clr 3.0 whereby many users seem
# to report both version 3.0.4506.2152 and 3.0.04504.30, in different
# orders; while it might be some malware's watermarking I've been
# unable to track it down for sure and it causes more trouble than it
# fixes.
SecRule REQUEST_HEADERS:User-Agent "(?:\.net clr 1\.0.*\.net clr 1\.0|\.net clr 1\.1.*\.net clr 1\.1|\.net clr 2\.0.*\.net clr 2\.0|\.net clr 3\.5.*\.net clr 3\.5|\.net clr 4\.0.*\.net clr 4\.0)" \
"id:436020,phase:2,t:lowercase,msg:'Multiple .NET CLR tokens: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
# No known implementation of .NET on Linux adds a ".NET CLR"
# specification to Firefox; so if you find any of that, simply kill
# them.
SecRule REQUEST_HEADERS:User-Agent ".* Linux .* Gecko/.*\.NET CLR" \
"id:436030,phase:2,t:none,msg:'Fake browser; Firefox on Linux never reports .NET CLR: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
# There are a few browsers that really doesn't exist anymore for real,
# but are still used by spammers; make sure to filter those as well.
SecRule REQUEST_HEADERS:User-Agent "@pmFromFile flameeyes_bad_browsers.data" \
"id:436040,phase:2,t:lowercase,deny,status:403,msg:'Known bad browser',logdata:'%{REQUEST_HEADERS.User-Agent}'"
# Opera is one of the few browsers that doesn't declare itself as
# Mozilla/*; some spammers though insists on declaring themselves MSIE
# and Opera. And there are other fake Opera browser User-Agents
# around.
#
# Unfortunately, Opera Mini on some operating systems has a very bogus
# User-agent string, so don't report problems if the connection is
# declaring itself WAP.
SecRule REQUEST_HEADERS:User-Agent "^.* opera[ /][0-9]" \
"id:436050,phase:2,t:lowercase,msg:'Fake Opera browser (not starting with Opera)',deny,status:403,chain"
SecRule &REQUEST_HEADERS:X-Wap-Profile "@eq 0"
# Mozilla/5.0 is legitimately declared by Gecko (Mozilla engine),
# WebKit (Safari, Chrome, ...) and MSIE 9.0, plus a bunch of crawlers.
#
# Anything else (MSIE < 9, Opera, ...) is a spammer or some other
# likely try at DDoSing, so reject them.
SecRule REQUEST_HEADERS:User-Agent "@beginsWith mozilla/5.0 " \
"id:436060,phase:2,t:lowercase,chain,msg:'Unexpected Mozilla/5.0 browser %{REQUEST_HEADERS.User-Agent}.',deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "! (?:gecko|msie 9\.)" "t:lowercase,chain"
SecRule IP:IS_ROBOT "!@eq 1"
# Futhermore, Mozilla/x.0 is generally used at the start of the
# User-Agent string, only a handful of crawlers don't do that, but
# ignore their existence (they should be fixed anyway); so if there is
# mozilla/x.0 but it's not at the start, kill the request.
SecRule REQUEST_HEADERS:User-Agent "^.+mozilla/[45]\.0" \
"id:436070,phase:2,t:lowercase,msg:'Invalid Mozilla/5.0 browser %{REQUEST_HEADERS.User-Agent}.',deny,status:403,chain"
# NewsBlur and LinkedInBot send a Mozilla/5.0 string in the middle of the User-Agent.
SecRule REQUEST_HEADERS:User-Agent "!@pm newsblur linkedinbot" "t:lowercase"
###############################
# HEADER CHECKING STARTS HERE #
###############################
SecRule IP:FLAMEEYES_IS_PROXY "@eq 1" \
"id:436080,phase:2,skipAfter:FLAMEEYES_END_FAKE_BROWSERS_HEADERS,nolog"
# Don't try to validate GoogleBot, BingBot and other similar user
# agents because they will try to pass for browsers even when they are
# not. We already validate these bots to be coming from a FcRDNS so we
# don't need to apply more validation.
SecRule REQUEST_AGENT:User-Agent "@pm googlebot bingbot bingpreview" \
"id:436998,phase:1,skipAfter:FLAMEEYES_END_FAKE_BROWSERS_HEADERS,nolog"
# Unfortunately some of bing verification features try to pass for
# MSIE8 even though the request does not look like it.
SecRule REMOTE_HOST "^msnbot(?:-[0-9]+){4}.search.msn.com$" \
"id:436999,phase:1,skipAfter:FLAMEEYES_END_FAKE_BROWSERS_HEADERS,nolog"
# Not sending Accept header is a protocol violation in HTTP/1.1;
# browsers always send it, legit crawlers sometimes don't send it, but
# spammers masking as real browsers do often enough.
SecRule &REQUEST_HEADERS:Accept "@eq 0" \
"id:436081,chain,phase:2,msg:'Missing Accept header when passing as a browser',deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "@pm safari firefox msie opera" \
"t:lowercase,chain"
# The stock browser on Android 4.0 and later does not send any
# `Accept` header, but it does send headers. Instead of matching on
# "Android 4" in the User-Agent, check on the X-Requested-With header
# that it sends.
# Some more apps seem to use the same library and have the same problem.
SecRule REQUEST_HEADERS:X-Requested-With "!@pm com.android.browser com.google.android.browser com.devhd.feedly"
# There is no requirement for other Accept-* headers to be present,
# but most browsers send it anyway, and we can use them to judge
# whether a browser is real or not.
SecRule &REQUEST_HEADERS:Accept-Encoding "@eq 0" \
"id:436082,chain,phase:2,msg:'Missing Accept-Encoding header when passing as a browser',deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "@pm msie safari opera" \
"t:lowercase,chain"
SecRule REMOTE_HOST "!msnbot-\d+-\d+-\d+-\d+\.search\.msn\.com"
SecRule &REQUEST_HEADERS:Accept-Language "@eq 0" \
"id:436083,chain,phase:2,msg:'Missing Accept-Language header when passing as a browser',deny,status:403"
SecRule REQUEST_HEADERS:User-Agent "@pm safari opera" \
"t:lowercase,chain"
# A number of browsers appear to report themselves as Safari/Chrome,
# but with significantly rougher HTTP clients, namely Epiphany and
# dwb.
#
# Feed readers including NewsBlur, Feedly and Vienna appear to have
# similar bugs.
SecRule REQUEST_HEADERS:User-Agent "!@pm dwb epiphany newsblur s~feedly-social vienna" \
"t:lowercase"
# Sony PlayStation 3 systems will provide a further header that stay
# to tell us the version of the browser as well as the firmware. It
# seems like PlayStation systems are often spoofed because of the
# limited browser, and the limited javascript that is running on it.
SecRule REQUEST_HEADERS:User-Agent "playstation 3" \
"id:436086,chain,phase:2,msg:'Fake PlayStation 3 browser: %{REQUEST_HEADERS.User-Agent}',deny,status:403"
SecRule &REQUEST_HEADERS:x-ps3-browser "@eq 0"
# The WebP format for images has been introduced by Google between
# 2011 and 2012, but hasn't got to Internet Explorer as of version 9
# at least. If the Accept header includes it, the request can't be
# coming from IE.
SecRule REQUEST_HEADERS:User-Agent "@contains msie" \
"id:436087,chain,phase:2,msg:'Fake Internet Explorer browser accepting WebP images',deny,status:403"
SecRule REQUEST_HEADERS:Accept "@contains image/webp"
# Firefox will use persistent connections, so if there is no
# Connection: keep-alive header, something is likely wrong. Most spam
# tools do *not* support persistent connections.
SecRule REQUEST_HEADERS:User-Agent "@contains firefox" \
"id:436088,t:lowercase,chain,phase:2,msg:'Fake Firefox without persistent connection',deny,status:403"
SecRule REQUEST_HEADERS:Connection "!@strmatch keep-alive" "t:lowercase"
# Puffin browser will always send an X-Puffin-UA, if it declares
# itself as such.
SecRule REQUEST_HEADERS:User-Agent "@contains Puffin" \
"id:436090,chain,phase:2,msg:'Fake Puffin browser, missing X-Puffin-UA.',deny,status:403"
SecRule &REQUEST_HEADERS:X-Puffin-UA "@eq 0"
SecRule REQUEST_HEADERS:User-Agent "@contains xbox" \
"id:436091,chain,phase:2,t:lowercase,msg:'Fake Xbox browser, invalid UA-CPU header.',deny,status:403"
SecRule REQUEST_HEADERS:UA-CPU "@streq PPC"
# I've seen a lot of spammers trying to pass for Safari/iOS and then
# providing an Accept header resembling Opera, in particular for the
# presence of image/webp.
SecRule REQUEST_HEADERS:User-Agent "Version/[0-9.]+ Mobile/[0-9A-Z]+" \
"id:436092,phase:2,msg:'Fake Safari/iOS accepting webp',logdata:'%{REQUEST_HEADERS.User-Agent}',deny,status:403,chain"
SecRule REQUEST_HEADERS:Accept "@contains image/webp"
# Opera Mini is an easy problem to solve as it concentrates all requests
# through their proxies. Refuse anything trying to pass for one but not
# coming from their hosts.
SecRule REQUEST_HEADERS:User-Agent "@contains opera mini" \
"id:436300,t:lowercase,phase:2,deny,status:403,msg:'Trying to pass for Opera Mini on non-Opera IP address.',logdata:'%{REMOTE_HOST}',chain"
SecRule REMOTE_HOST "!@endsWith opera-mini.net"
##################################################################
# Rule IDs 436100-436199 are reserved for SDCH tests (see below) #
# Rule IDs 436200-436299 are reserved for Gecko tests #
##################################################################
# The Shared Dictionary Compression for HTTP is a technique proposed
# by Google for compressing HTML output. Only Chrome, Chromium and
# derivates (Chrome for Android and iOS) implement it.
#
# Unfortunately verifying that the browser is one of those is not
# possible because Chrome for iOS does not report itself as Chrome
# when "requesting desktop site".
#
# At the same time a number of browsers report themselves as Chrome
# without supporting it, mostly WebKit based ones (Epiphany, Midori),
# as well as relatives of Chrome (ChromeFrame, Android Browser on 4.1+
# when requesting desktop site).
SecRule REQUEST_HEADERS:User-Agent "!@pm chrome crios" \
"id:436100,t:lowercase,phase:2,nolog,skipAfter:FLAMEEYES_END_SDCH"
SecRule REQUEST_HEADERS:User-Agent "@pm epiphany midori chromeframe android" \
"id:436101,t:lowercase,phase:2,nolog,skipAfter:FLAMEEYES_END_SDCH"
SecRule REQUEST_HEADERS:X-Requested-With "@streq com.android.browser" \
"id:436102,t:lowercase,phase:2,nolog,skipAfter:FLAMEEYES_END_SDCH"
SecMarker FLAMEEYES_END_SDCH
# Start testing Gecko version strings.
#
# Luckily for us, Mozilla is very strict on modern Gecko-based agents'
# identifiers, and their policy is fully documented at
# https://developer.mozilla.org/en-US/docs/Gecko_user_agent_string_reference
SecRule REQUEST_HEADERS:User-Agent "^Mozilla/5.0 \((.*); rv:([0-9.ab]+)\) Gecko/([0-9.ab]+)(?: Firefox/([0-9.ab]+))? ([A-Za-z]+)/([0-9.ab]+)" \
"id:436200,phase:2,nolog,capture,setvar:TX.FLAMEEYES_GECKO_PLATFORM=%{TX.1},setvar:TX.FLAMEEYES_GECKO_VERSION=%{TX.2},setvar:TX.FLAMEEYES_GECKO_TRAIL=%{TX.3},setvar:TX.FLAMEEYES_GECKO_FF_VERSION=%{TX.4},setvar:TX.FLAMEEYES_GECKO_APP=%{TX.5},setvar:TX.FLAMEEYES_GECKO_APP_VERSION=%{TX.6},msg:'Platform %{TX.FLAMEEYES_GECKO_PLATFORM} Gecko version %{TX.FLAMEEYES_GECKO_VERSION} Gecko trail %{TX.FLAMEEYES_GECKO_TRAIL} Firefox version %{TX.FLAMEEYES_GECKO_FF_VERSION} Gecko App %{TX.FLAMEEYES_GECKO_APP} Gecko App version %{TX.FLAMEEYES_GECKO_APP_VERSION}'"
SecRule TX:FLAMEEYES_GECKO_PLATFORM "^$" \
"id:436201,phase:2,nolog,skipAfter:FLAMEEYES_END_GECKO"
# This should never happen, as when Firefox is the App, it will be
# found as such, but since there are non-spec-compliant valid
# User-Agents out there, better safe than sorry.
SecRule TX:FLAMEEYES_GECKO_APP "^$" \
"id:436202,phase:2,nolog,setvar:TX.FLAMEEYES_GECKO_APP=Firefox,setvar:TX.FLAMEEYES_GECKO_APP_VERSION=%{TX.FLAMEEYES_GECKO_FF_VERSION}"
# If the Gecko app detected is Firefox, make sure to set the
# FF_VERSION the same, to simplify later rules.
SecRule TX:FLAMEEYES_GECKO_APP "@streq Firefox" \
"id:436203,phase:2,nolog,setvar:TX.FLAMEEYES_GECKO_FF_VERSION=%{TX.FLAMEEYES_GECKO_APP_VERSION}"
# Even though Mozilla says that, on Desktop, the Gecko Trail is a
# fixed string of 20100101, that's no longer true with Firefox Aurora
# (congrats for the idiocy, Mozilla). We can still refuse trails that
# are too old though.
#SecRule TX:FLAMEEYES_GECKO_TRAIL "@lt 20100101" \
# "id:436221,phase:2,deny,status:403,msg:'Gecko trail too old for desktop',chain"
#SecRule TX:FLAMEEYES_GECKO_PLATFORM "!@pm Mobile Tablet"
SecRule TX:FLAMEEYES_GECKO_PLATFORM "@pm Mobile Tablet" \
"id:436222,phase:2,deny,status:403,msg:'Gecko trail does not match Firefox version',chain"
SecRule TX:FLAMEEYES_GECKO_APP "@streq Firefox" "chain"
SecRule TX:FLAMEEYES_GECKO_TRAIL "!@streq %{TX.FLAMEEYES_GECKO_APP_VERSION}"
SecRule TX:FLAMEEYES_GECKO_PLATFORM "@beginsWith Macintosh" \
"id:436223,phase:2,deny,status:403,msg:'Gecko Mac platform does not comply to spec.',chain"
SecRule TX:FLAMEEYES_GECKO_PLATFORM "!^Macintosh; (?:Intel|PPC) Mac OS X [0-9]+\.[0-9]+$"
SecRule TX:FLAMEEYES_GECKO_PLATFORM "@beginsWith Windows" \
"id:436224,phase:2,deny,status:403,msg:'Gecko Win platform does not comply to spec.',chain"
SecRule TX:FLAMEEYES_GECKO_PLATFORM "!^Windows NT (?:[4-6]|1[0-9])\.[0-9](?:; Win64; x64|; WOW64)?$"
# Ensure that the Gecko version match the Firefox version (if any).
#
# On modern browsers, when a Firefox version is declared it will match
# the rv: Gecko version, but some spammers have been using different
# values for the two, this will filter them out.
SecRule TX:FLAMEEYES_GECKO_VERSION "!@streq %{TX.FLAMEEYES_GECKO_FF_VERSION}" \
"id:436225,phase:2,deny,status:403,msg:'Gecko version does not match Firefox version',chain"
SecRule TX:FLAMEEYES_GECKO_FF_VERSION "!^$" "chain"
# Unfortunately there are multiple Firefox rebuilds that don't follow
# the right pattern, so if the gecko app is one of them, don't enforce
# this rule.
SecRule TX:FLAMEEYES_GECKO_APP "!@pm PaleMoon Waterfox"
# This pairs with id 436221 above; valid trails are either version
# numbers (including dots), or a ISO date (eight digits). Anything
# else, refuse it.
SecRule TX:FLAMEEYES_GECKO_TRAIL "!^(?:[0-9.]+|[0-9]{8})$" \
"id:436226,phase:2,deny,status:403,msg:'Invalid Gecko trail',logdata:'%{TX.FLAMEEYES_GECKO_TRAIL}'"
SecMarker FLAMEEYES_END_GECKO
##################################################################
# Rule IDs 436100-436199 are reserved for SDCH tests (see above) #
# Rule IDs 436200-436299 are reserved for Gecko tests #
# Rule IDs 436900-436999 are reserved for flow control #
##################################################################
SecMarker FLAMEEYES_END_FAKE_BROWSERS_HEADERS