PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.0.3
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.0.3
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Tracker / VisitExcluded.php
matomo / app / core / Tracker Last commit date
Db 2 years ago Handler 2 years ago Visit 2 years ago Action.php 2 years ago ActionPageview.php 2 years ago Cache.php 2 years ago Db.php 2 years ago Failures.php 2 years ago FingerprintSalt.php 2 years ago GoalManager.php 2 years ago Handler.php 2 years ago IgnoreCookie.php 2 years ago LogTable.php 2 years ago Model.php 2 years ago PageUrl.php 2 years ago Request.php 2 years ago RequestProcessor.php 2 years ago RequestSet.php 2 years ago Response.php 2 years ago ScheduledTasksRunner.php 2 years ago Settings.php 2 years ago TableLogAction.php 2 years ago TrackerCodeGenerator.php 2 years ago TrackerConfig.php 2 years ago Visit.php 2 years ago VisitExcluded.php 2 years ago VisitInterface.php 2 years ago Visitor.php 2 years ago VisitorNotFoundInDb.php 2 years ago VisitorRecognizer.php 2 years ago
VisitExcluded.php
329 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 *
9 */
10 namespace Piwik\Tracker;
11
12 use Piwik\Cache as PiwikCache;
13 use Piwik\Common;
14 use Piwik\Container\StaticContainer;
15 use Piwik\DeviceDetector\DeviceDetectorFactory;
16 use Piwik\Exception\UnexpectedWebsiteFoundException;
17 use Matomo\Network\IP;
18 use Piwik\Piwik;
19 use Piwik\Plugins\SitesManager\SiteUrls;
20 use Piwik\Tracker\Visit\ReferrerSpamFilter;
21 use Piwik\Config;
22 /**
23 * This class contains the logic to exclude some visitors from being tracked as per user settings
24 */
25 class VisitExcluded
26 {
27 /**
28 * @var ReferrerSpamFilter
29 */
30 private $spamFilter;
31 private $siteCache = array();
32 public $request;
33 public $idSite;
34 public $userAgent;
35 public $ip;
36 /**
37 * @param Request $request
38 */
39 public function __construct(\Piwik\Tracker\Request $request)
40 {
41 $this->spamFilter = new ReferrerSpamFilter();
42 $this->request = $request;
43 try {
44 $this->idSite = $request->getIdSite();
45 } catch (UnexpectedWebsiteFoundException $e) {
46 // most checks will still work on a global scope and we still want to be able to test if this is a valid
47 // visit or not
48 $this->idSite = 0;
49 }
50 $userAgent = $request->getUserAgent();
51 $this->userAgent = Common::unsanitizeInputValue($userAgent);
52 $this->ip = $request->getIp();
53 }
54 /**
55 * Test if the current visitor is excluded from the statistics.
56 *
57 * Plugins can for example exclude visitors based on the
58 * - IP
59 * - If a given cookie is found
60 *
61 * @return bool True if the visit must not be saved, false otherwise
62 */
63 public function isExcluded()
64 {
65 $excluded = false;
66 if ($this->isNonHumanBot()) {
67 Common::printDebug('Search bot detected, visit excluded');
68 $excluded = true;
69 }
70 /*
71 * Requests built with piwik.js will contain a rec=1 parameter. This is used as
72 * an indication that the request is made by a JS enabled device. By default, Piwik
73 * doesn't track non-JS visitors.
74 */
75 if (!$excluded) {
76 $toRecord = $this->request->getParam($parameterForceRecord = 'rec');
77 if (!$toRecord) {
78 Common::printDebug(@$_SERVER['REQUEST_METHOD'] . ' parameter ' . $parameterForceRecord . ' not found in URL, request excluded');
79 $excluded = true;
80 Common::printDebug("'{$parameterForceRecord}' parameter not found.");
81 }
82 }
83 /**
84 * Triggered on every tracking request.
85 *
86 * This event can be used to tell the Tracker not to record this particular action or visit.
87 *
88 * @param bool &$excluded Whether the request should be excluded or not. Initialized
89 * to `false`. Event subscribers should set it to `true` in
90 * order to exclude the request.
91 * @param Request $request The request object which contains all of the request's information
92 *
93 */
94 Piwik::postEvent('Tracker.isExcludedVisit', array(&$excluded, $this->request));
95 /*
96 * Following exclude operations happen after the hook.
97 * These are of higher priority and should not be overwritten by plugins.
98 */
99 // Checking if in config some requests are excluded
100 if (!$excluded) {
101 $excluded = $this->request->isRequestExcluded();
102 if ($excluded) {
103 Common::printDebug("Request is excluded.");
104 }
105 }
106 // Checking if the Piwik ignore cookie is set
107 if (!$excluded) {
108 $excluded = $this->isIgnoreCookieFound();
109 if ($excluded) {
110 Common::printDebug("Ignore cookie found.");
111 }
112 }
113 // Checking for excluded IPs
114 if (!$excluded) {
115 $excluded = $this->isVisitorIpExcluded();
116 if ($excluded) {
117 Common::printDebug("IP excluded.");
118 }
119 }
120 // Check if user agent should be excluded
121 if (!$excluded) {
122 $excluded = $this->isUserAgentExcluded();
123 if ($excluded) {
124 Common::printDebug("User agent excluded.");
125 }
126 }
127 // Check if Referrer URL is a known spam
128 $generalConfig = Config::getInstance()->Tracker;
129 if ($generalConfig['enable_spam_filter']) {
130 if (!$excluded) {
131 $excluded = $this->isReferrerSpamExcluded();
132 if ($excluded) {
133 Common::printDebug("Referrer URL is listed as spam.");
134 }
135 }
136 } else {
137 Common::printDebug("Spam list is disabled.");
138 }
139 // Check if request URL is excluded
140 if (!$excluded) {
141 $excluded = $this->isUrlExcluded();
142 if ($excluded) {
143 Common::printDebug("Unknown URL is not allowed to track.");
144 }
145 }
146 if (!$excluded) {
147 if ($this->isPrefetchDetected()) {
148 $excluded = true;
149 Common::printDebug("Prefetch request detected, not a real visit so we Ignore this visit/pageview");
150 }
151 }
152 if ($excluded) {
153 Common::printDebug("Visitor excluded.");
154 return true;
155 }
156 return false;
157 }
158 protected function isPrefetchDetected()
159 {
160 return isset($_SERVER["HTTP_X_PURPOSE"]) && in_array($_SERVER["HTTP_X_PURPOSE"], array("preview", "instant")) || isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == "prefetch";
161 }
162 /**
163 * Live/Bing/MSN bot and Googlebot are evolving to detect cloaked websites.
164 * As a result, these sophisticated bots exhibit characteristics of
165 * browsers (cookies enabled, executing JavaScript, etc).
166 *
167 * @see \DeviceDetector\Parser\Bot
168 *
169 * @return boolean
170 */
171 protected function isNonHumanBot()
172 {
173 $allowBots = $this->request->getParam('bots');
174 $deviceDetector = StaticContainer::get(DeviceDetectorFactory::class)->makeInstance($this->userAgent, $this->request->getClientHints());
175 return !$allowBots && ($deviceDetector->isBot() || $this->isIpInRange());
176 }
177 private function isIpInRange()
178 {
179 $cache = PiwikCache::getTransientCache();
180 $ip = IP::fromBinaryIP($this->ip);
181 $key = 'VisitExcludedIsIpInRange' . $ip->toString();
182 if ($cache->contains($key)) {
183 $isInRanges = $cache->fetch($key);
184 } else {
185 if ($this->isChromeDataSaverUsed($ip)) {
186 $isInRanges = false;
187 } else {
188 $isInRanges = $ip->isInRanges($this->getBotIpRanges());
189 }
190 $cache->save($key, $isInRanges);
191 }
192 return $isInRanges;
193 }
194 public function isChromeDataSaverUsed(IP $ip)
195 {
196 // see https://github.com/piwik/piwik/issues/7733
197 return !empty($_SERVER['HTTP_VIA']) && false !== strpos(strtolower($_SERVER['HTTP_VIA']), 'chrome-compression-proxy') && $ip->isInRanges($this->getGoogleBotIpRanges());
198 }
199 protected function getBotIpRanges()
200 {
201 return array_merge($this->getGoogleBotIpRanges(), array(
202 // Live/Bing/MSN
203 '64.4.0.0/18',
204 '65.52.0.0/14',
205 '157.54.0.0/15',
206 '157.56.0.0/14',
207 '157.60.0.0/16',
208 '207.46.0.0/16',
209 '207.68.128.0/18',
210 '207.68.192.0/20',
211 '131.253.26.0/20',
212 '131.253.24.0/20',
213 // Yahoo
214 '72.30.198.0/20',
215 '72.30.196.0/20',
216 '98.137.207.0/20',
217 // Chinese bot hammering websites
218 '1.202.218.8',
219 ));
220 }
221 private function getGoogleBotIpRanges()
222 {
223 return array('216.239.32.0/19', '64.233.160.0/19', '66.249.80.0/20', '72.14.192.0/18', '209.85.128.0/17', '66.102.0.0/20', '74.125.0.0/16', '64.18.0.0/20', '207.126.144.0/20', '173.194.0.0/16');
224 }
225 /**
226 * Looks for the ignore cookie that users can set in the Piwik admin screen.
227 * @return bool
228 */
229 protected function isIgnoreCookieFound()
230 {
231 if (\Piwik\Tracker\IgnoreCookie::isIgnoreCookieFound()) {
232 Common::printDebug('Matomo ignore cookie was found, visit not tracked.');
233 return true;
234 }
235 return false;
236 }
237 /**
238 * Checks if the visitor ip is in the excluded list
239 *
240 * @return bool
241 */
242 protected function isVisitorIpExcluded()
243 {
244 $excludedIps = $this->getAttributes('excluded_ips', 'global_excluded_ips');
245 if (!empty($excludedIps)) {
246 $ip = IP::fromBinaryIP($this->ip);
247 if ($ip->isInRanges($excludedIps)) {
248 Common::printDebug('Visitor IP ' . $ip->toString() . ' is excluded from being tracked');
249 return true;
250 }
251 }
252 return false;
253 }
254 private function getAttributes($siteAttribute, $globalAttribute)
255 {
256 if (!isset($this->siteCache[$this->idSite])) {
257 $this->siteCache[$this->idSite] = array();
258 }
259 try {
260 if (empty($this->siteCache[$this->idSite])) {
261 $this->siteCache[$this->idSite] = \Piwik\Tracker\Cache::getCacheWebsiteAttributes($this->idSite);
262 }
263 if (isset($this->siteCache[$this->idSite][$siteAttribute])) {
264 return $this->siteCache[$this->idSite][$siteAttribute];
265 }
266 } catch (UnexpectedWebsiteFoundException $e) {
267 $cached = \Piwik\Tracker\Cache::getCacheGeneral();
268 if ($globalAttribute && isset($cached[$globalAttribute])) {
269 return $cached[$globalAttribute];
270 }
271 }
272 }
273 /**
274 * Checks if request URL is excluded
275 * @return bool
276 */
277 protected function isUrlExcluded()
278 {
279 $excludedUrls = $this->getAttributes('exclude_unknown_urls', null);
280 $siteUrls = $this->getAttributes('urls', null);
281 if (!empty($excludedUrls) && !empty($siteUrls)) {
282 $url = $this->request->getParam('url');
283 $parsedUrl = parse_url($url);
284 $trackingUrl = new SiteUrls();
285 $urls = $trackingUrl->groupUrlsByHost(array($this->idSite => $siteUrls));
286 $idSites = $trackingUrl->getIdSitesMatchingUrl($parsedUrl, $urls);
287 $isUrlExcluded = !isset($idSites) || !in_array($this->idSite, $idSites);
288 return $isUrlExcluded;
289 }
290 return false;
291 }
292 /**
293 * Returns true if the specified user agent should be excluded for the current site or not.
294 *
295 * Visits whose user agent string contains one of the excluded_user_agents strings for the
296 * site being tracked (or one of the global strings) will be excluded. Regular expressions
297 * are also supported.
298 *
299 * @internal param string $this ->userAgent The user agent string.
300 * @return bool
301 */
302 protected function isUserAgentExcluded() : bool
303 {
304 $excludedAgents = $this->getAttributes('excluded_user_agents', 'global_excluded_user_agents');
305 if (!empty($excludedAgents)) {
306 foreach ($excludedAgents as $excludedUserAgent) {
307 // if the excluded user agent string part is in this visit's user agent, this visit should be excluded
308 if (stripos($this->userAgent, $excludedUserAgent) !== false) {
309 return true;
310 }
311 // if the string is a valid regex, and the user agent matches, this visit should be excluded
312 if (@preg_match($excludedUserAgent, null) !== false) {
313 return preg_match($excludedUserAgent, $this->userAgent) ? true : false;
314 }
315 }
316 }
317 return false;
318 }
319 /**
320 * Returns true if the Referrer is a known spammer.
321 *
322 * @return bool
323 */
324 protected function isReferrerSpamExcluded()
325 {
326 return $this->spamFilter->isSpam($this->request);
327 }
328 }
329