PluginProbe
WebTotem Security / 2.4.21
WebTotem Security v2.4.21
3.0.2 3.0.1 3.0.0 trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.1 2.2.2 2.2.3 All 110 releases
wt-security / lib / modules / logs / Crawler.php

Crawler.php in WebTotem Security 2.4.21, at lib/modules/logs/Crawler.php

298 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('WEBTOTEM_INIT') || WEBTOTEM_INIT !== true) {
4 if (!headers_sent()) {
5 header('HTTP/1.1 403 Forbidden');
6 }
7 die("Protected By WebTotem!");
8 }
9
10 /**
11 * WebTotem page scan class for Wordpress.
12 */
13 class WebTotemCrawler
14 {
15 /**
16 * Running a single iteration
17 */
18 public static function init($scan_temp) {
19
20 $temp_data = json_decode(WebTotemOption::getOption('crawler_temp'), true) ?: [];
21
22 $i = 1;
23 if (!$temp_data) {
24
25 $pre_scan = self::pre_scan();
26
27 $temp_data['internal']['new'] = $pre_scan['internal'];
28 $temp_data['external'] = WebTotem::arrayUniqueKey($pre_scan['external'], 'link');
29 $temp_data['scripts'] = WebTotem::arrayUniqueKey($pre_scan['scripts'], 'link');
30 $temp_data['iframes'] = WebTotem::arrayUniqueKey( $pre_scan['iframes'], 'link');
31 $temp_data['exclude'] = array_unique($pre_scan['exclude']);
32
33 $temp_data['internal']['new'] = WebTotem::arrayUniqueKey( array_merge($temp_data['internal']['new'], $scan_temp['links']), 'link');
34
35 $i++;
36 }
37
38 foreach ($temp_data['internal']['new'] as $item) {
39
40 if($result = self::explore_page($item['link'], $temp_data['exclude'])) {
41 $temp_data['internal']['visited'][] = $item;
42
43 $temp_data['internal']['new'] = WebTotem::arrayUniqueKey(array_merge($temp_data['internal']['new'], $result['internal']), 'link');
44 $temp_data['external'] = WebTotem::arrayUniqueKey(array_merge($temp_data['external'], $result['external']), 'link');
45 $temp_data['scripts'] = WebTotem::arrayUniqueKey(array_merge($temp_data['scripts'], $result['scripts']), 'link');
46 $temp_data['iframes'] = WebTotem::arrayUniqueKey(array_merge($temp_data['iframes'], $result['iframes']), 'link');
47 $temp_data['exclude'] = array_merge($temp_data['exclude'], $result['exclude']);
48 }
49
50 $key = array_search($item, $temp_data['internal']['new']);
51 if ($key !== false) {
52 unset($temp_data['internal']['new'][$key]);
53 }
54
55 if ($i >= 5) break;
56 $i++;
57 }
58
59 if (empty($temp_data['internal']['new'])) {
60
61 if($scan_temp['ready_to_save']){
62
63 $data = [
64 'links' => array_merge($temp_data['internal']['visited'], $temp_data['external']),
65 'scripts' => $temp_data['scripts'],
66 'iframes' => $temp_data['iframes'],
67 ];
68 self::saveData($data);
69
70 WebTotemOption::setOptions(['crawler_temp' => '']);
71 WebTotemOption::setOptions(['scan_temp' => '']);
72 WebTotemOption::setOptions(['scan_init' => 0]);
73
74 // Resetting the task in the cron.
75 wp_clear_scheduled_hook('webtotem_daily_cron');
76 wp_schedule_event(time() + 86395, 'daily', 'webtotem_daily_cron');
77 } else {
78 WebTotemOption::setOptions([
79 'scan_temp' => [
80 'current_scan' => 'crawler',
81 'links' => [],
82 'ready_to_save' => true,
83 ]
84 ]);
85 }
86
87 } else {
88 WebTotemOption::setOptions(['crawler_temp' => $temp_data]);
89 }
90
91 }
92
93 private static function pre_scan() {
94 $site_url = get_site_url();
95 $internal = [];
96 $exclude = [];
97
98 // Сканируем файл robots.txt
99 $robotsTxt = file_get_contents(ABSPATH . '/robots.txt');
100 $lines = explode("\n", $robotsTxt);
101
102 foreach ($lines as $line) {
103 if (strpos($line, 'Disallow:') === 0 || strpos($line, 'Allow:') === 0) {
104 $url = trim(substr($line, strpos($line, ':') + 1));
105 $exclude[] = $url;
106 $robots_urls[] = (string)$url->loc;
107 }
108 }
109
110 foreach ($robots_urls as $url) {
111 if (substr($url, 0, 1) == "#") {
112 continue;
113 }
114 $internal[] = ['link' => $url, 'page' => $site_url . '/robots.txt', 'is_internal' => self::isInternal($url)];
115 }
116
117 // Добавляем ссылки из популярны�
118 sitemaps плагинов
119 $sitemaps = [
120 $site_url . '/sitemaps.xml',
121 $site_url . '/index.php?xml_sitemap=params=.',
122 $site_url . '/?sitemap=1',
123 $site_url . '/sitemap_index.xml',
124 ];
125
126 foreach ($sitemaps as $url) {
127 $internal[] = ['link' => $url, 'page' => __('by sitemap plugins', 'wtotem'), 'is_internal' => true];
128 }
129
130 // Сканируем файл sitemap.xml
131 $xml = simplexml_load_file(ABSPATH . '/sitemap.xml');
132
133 $sitemap_urls = [];
134 foreach ($xml->url as $url) {
135 $exclude[] = (string)$url->loc;
136 $sitemap_urls[] = (string)$url->loc;
137 }
138
139 foreach ($sitemap_urls as $url) {
140 if (substr($url, 0, 1) == "#") {
141 continue;
142 }
143 $internal[] = ['link' => $url, 'page' => $site_url . '/sitemap.xml', 'is_internal' => self::isInternal($url)];
144 }
145
146 // Сканируем главную страницу
147 $result = self::explore_page($site_url);
148
149 $internal = array_merge($internal, $result['internal']);
150 $external = array_unique($result['external']);
151 $exclude = array_merge($exclude, $result['exclude']);
152
153
154 return [
155 'internal' => $internal ?: [],
156 'external' => $external ?: [],
157 'scripts' => $result['scripts'] ?: [],
158 'iframes' => $result['iframes'] ?: [],
159 'exclude' => $exclude ?: [],
160 ];
161
162 }
163
164 private static function explore_page($url, $exclude = []) {
165
166 $headers = get_headers($url);
167
168 if ($headers === false || strpos($headers[0], '200 OK') === false) {
169 return false;
170 }
171
172 // Initializing the cURL session
173 $curl = curl_init();
174
175 // Setting the parameters of the cURL session
176 curl_setopt($curl, CURLOPT_URL, $url); // Устанавливаем URL
177 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // Возвращаем результат в виде строки
178 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // Следуем за редиректами
179 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // Отключаем проверку SSL-сертификата
180
181 // Execute the request and get the content of the page
182 $content = curl_exec($curl);
183
184 // Checking for errors when executing the request
185 if (curl_errno($curl)) {
186 WebTotemOption::setNotification('error', __('Request execution error: ', 'wtotem')) . curl_error($curl);
187 }
188
189 // Closing the cURL session
190 curl_close($curl);
191
192 // Checking the content for matches with the template, using regular expressions
193 return self::getMatches($content, $url, $exclude);
194
195 }
196
197 private static function getMatches($content, $url, $exclude) {
198
199 $pattern = '/(<a.*?href=["\'](([\da-z\.-\/]+)([\/\w\.-\?\%\&]*)*\/?)["\'].*?>|<script.*?src=["\'](.*?)["\'].*?>|<iframe.*?src=["\'](.*?)["\'].*?>|onclick="[^"]*location[^"][^\'"]+\'([^\']+)\')/i';
200 preg_match_all($pattern, $content, $all_matches);
201
202 $array = [
203 'links' => [],
204 'scripts' => [],
205 'iframes' => [],
206 ];
207
208 foreach ($all_matches[0] as $match) {
209 preg_match_all('/<a.*?href=["\'](.*?)["\'].*?>/i', $match, $links_matches);
210 if ($links_matches[1]) $array['links'] = array_merge($array['links'], $links_matches[1]);
211 preg_match_all('/onclick="[^"]*location[^"][^\'"]+\'([^\']+)\'/i', $match, $links_2_matches);
212 if ($links_2_matches[1]) $array['links'] = array_merge($array['links'], $links_2_matches[1]);
213 preg_match_all('/<script.*?src=["\'](.*?)["\'].*?>/i', $match, $js_matches);
214 if ($js_matches[1]) $array['scripts'] = array_merge($array['scripts'], $js_matches[1]);
215 preg_match_all('/<iframe.*?src=["\'](.*?)["\'].*?>/i', $match, $iframe_matches);
216 if ($iframe_matches[1]) $array['iframes'] = array_merge($array['iframes'], $iframe_matches[1]);
217 }
218
219 $matches = [
220 'internal' => [],
221 'external' => [],
222 'exclude' => [],
223 'scripts' => [],
224 'iframe' => [],
225 ];
226
227 foreach ($array['links'] as $link) {
228 if (self::isInternal($link)) {
229 if (substr($link, 0, 1) == "#") {
230 continue;
231 }
232 if (in_array($link, $exclude)) {
233 continue;
234 }
235 $matches['internal'][] = ['link' => $link, 'page' => $url, 'is_internal' => true];
236 $matches['exclude'][] = $link;
237 } else {
238 $matches['external'][] = ['link' => $link, 'page' => $url, 'is_internal' => false];
239 }
240 }
241
242 foreach (array_unique($array['scripts']) as $script) {
243 $matches['scripts'][] = ['link' => $script, 'page' => $url, 'is_internal' => self::isInternal($script)];
244 }
245 foreach (array_unique($array['iframes']) as $iframe) {
246 $matches['iframe'][] = ['link' => $iframe, 'page' => $url, 'is_internal' => self::isInternal($iframe)];
247 }
248
249 return $matches;
250 }
251
252 private static function isInternal($string): bool {
253 $current_domain_parts = parse_url(get_home_url());
254 $current_domain = $current_domain_parts['host'];
255
256 if (substr($string, 0, 5) == "https"
257 || substr($string, 0, 4) == "http"
258 || substr($string, 0, 2) == "//") {
259
260 if (strpos($string, $current_domain) === false) {
261 return false;
262 }
263 }
264
265 return true;
266 }
267
268 /**
269 * Save data.
270 *
271 * @param array $data
272 * Array matches data.
273 */
274 private static function saveData($data) {
275
276 WebTotemDB::deleteData([], 'scan_logs');
277 $values = '';
278 foreach ($data as $data_type => $links) {
279 foreach ($links as $datum) {
280 $values .= sprintf("('%s','%s','%s','%s','%s'),",
281 date("Y-m-d H:i:s"),
282 $data_type,
283 $datum['page'],
284 $datum['link'],
285 $datum['is_internal']
286 );
287 }
288 }
289
290 $values = substr_replace($values, ";", -1);
291
292 $columns = '(created_at, data_type, source, content, is_internal)';
293
294 WebTotemDB::setRows('scan_logs', $columns, $values);
295 }
296
297 }
298