Хлебные крошки для WordPress (breadcrumbs). Хлебные крошки для WordPress (breadcrumbs) Забираемся в облака

Вы наверняка уже знакомы с понятием «Хлебные крошки» в веб-разработке и возможно даже приходилось реализовывать эти самые «крошки» на WordPress.

(с англ. breadcrumbs) - это элемент навигации по сайту, выглядит как путь от главной страницы до текущей, на которой находится пользователь. Более логичное название - навигационная цепочка . Хлебные крошки называются так по ироничной аналогии со сказкой, в которой дети, когда их завели в лес во второй раз, не смогли найти обратную дорогу, так как на этот раз вместо маленьких камешков они оставляли за собой хлебные крошки, впоследствии склеванные лесными птицами.

Если этот код работает не так как нужно, есть платная версия в виде плагина. Этот код я больше не поддерживаю.

Выглядят «хлебные крошки» так:

Главная страница » Раздел » Подраздел » Текущая страница

«Хлебные крошки» наиболее рекомендуются сайтам со сложной структурой разделов (рубрик), ведь с ними гораздо легче и понятнее разобраться посетителю в каком разделе сайта он находится и если нужно, можно легко подняться на уровень выше и осмотреть ветку целиком.

Теперь, после короткого ликбеза, буду делиться очередной функцией для WordPress, реализовывающей всю цепочку от главной страницы до текущей на всех типах страниц, включая таксономии и произвольные типы записей.

Функция будет показывать «хлебные крошки» для страниц следующих типов:
  • Главная страница;
  • Постоянная страница;
  • Страница любого древовидного типа записи;
  • Страница поста;
  • Страница вложения (учитывается прикреплено вложение к записи или нет);
  • Любой не древовидный тип записи (прикрепленный к любой таксономии, например, к стандартным "рубрикам");
  • Страница рубрики;
  • Страница меток;
  • Страница таксономии (как древовидной, так и одноуровневой (метки));
  • Страницы архивов по датам, авторам;
  • Страница пагинации для всех типов где предусмотрена пагинация
    (отображается как: Главная » Рубрика » Страница 2,3,4).
  • Поддерживает микроразметку. Инструменты для проверки: для Яндекса и для Google .

Из особенностей, которые я не встретил в аналогичных функциях представленных в сети, стоит отметить правильный показ "хлебных крошек" для произвольных типов записей и произвольных таксономий, также в аналогах страница пагинации отображалась как, например, "Рубрика (страница 2)", а не "Рубрика > Страница 2", что, на мой взгляд, неправильно.

Для визуального восприятия, взгляните как выглядят «хлебные крошки» разных типов:

Также, я старался написать как можно менее прожорливый вариант функции.

Что касается плагина Breadcrumb NavXT , который повсеместно рекомендуется для вывода «хлебных крошек» - он мне не понравился из-за своей громоздкости. Моя функция не хуже, а в чем-то даже лучше: за счет функциональности, компактности и местами быстродействия!

Также функция поддерживает микроразметки: schema.org или RDF, смотрите параметр "markup".

Функция «хлебных крошек» для WordPress

get_crumbs($sep, $l10n, $args); } class Kama_Breadcrumbs { public $arg; // Локализация static $l10n = array("home" => "Главная", "paged" => "Страница %d", "_404" => "Ошибка 404", "search" => "Результаты поиска по запросу - %s", "author" => "Архив автора: %s", "year" => "Архив за %d год", "month" => "Архив за: %s", "day" => "", "attachment" => "Медиа: %s", "tag" => "Записи по метке: %s", "tax_tag" => "%1$s из "%2$s" по тегу: %3$s", // tax_tag выведет: "тип_записи из "название_таксы" по тегу: имя_термина". // Если нужны отдельные холдеры, например только имя термина, пишем так: "записи по тегу: %3$s"); // Параметры по умолчанию static $args = array("on_front_page" => true, // выводить крошки на главной странице "show_post_title" => true, // показывать ли название записи в конце (последний элемент). Для записей, страниц, вложений "show_term_title" => true, // показывать ли название элемента таксономии в конце (последний элемент). Для меток, рубрик и других такс "title_patt" => "%s", // шаблон для последнего заголовка. Если включено: show_post_title или show_term_title "last_sep" => true, // показывать последний разделитель, когда заголовок в конце не отображается "markup" => "schema.org", // "markup" - микроразметка. Может быть: "rdf.data-vocabulary.org", "schema.org", "" - без микроразметки // или можно указать свой массив разметки: // array("wrappatt"=>"
%s
", "linkpatt"=>"%s", "sep_after"=>"",) "priority_tax" => array("category"), // приоритетные таксономии, нужно когда запись в нескольких таксах "priority_terms" => array(), // "priority_terms" - приоритетные элементы таксономий, когда запись находится в нескольких элементах одной таксы одновременно. // Например: array("category"=>array(45,"term_name"), "tax_name"=>array(1,2,"name")) // "category" - такса для которой указываются приор. элементы: 45 - ID термина и "term_name" - ярлык. // порядок 45 и "term_name" имеет значение: чем раньше тем важнее. Все указанные термины важнее неуказанных... "nofollow" => false, // добавлять rel=nofollow к ссылкам? // служебные "sep" => "", "linkpatt" => "", "pg_end" => "",); function get_crumbs($sep, $l10n, $args){ global $post, $wp_query, $wp_post_types; self::$args["sep"] = $sep; // Фильтрует дефолты и сливает $loc = (object) array_merge(apply_filters("kama_breadcrumbs_default_loc", self::$l10n), $l10n); $arg = (object) array_merge(apply_filters("kama_breadcrumbs_default_args", self::$args), $args); $arg->sep = "". $arg->sep .""; // дополним // упростим $sep = & $arg->sep; $this->arg = & $arg; // микроразметка --- if(1){ $mark = & $arg->markup; // Разметка по умолчанию if(! $mark) $mark = array("wrappatt" => "
%s
", "linkpatt" => "%s", "sep_after" => "",); // rdf elseif($mark === "rdf.data-vocabulary.org") $mark = array("wrappatt" => "
%s
", "linkpatt" => "%s", "sep_after" => "", // закрываем span после разделителя!); // schema.org elseif($mark === "schema.org") $mark = array("wrappatt" => "
%s
", "linkpatt" => "%s", "sep_after" => "",); elseif(! is_array($mark)) die(__CLASS__ .": "markup" parameter must be array..."); $wrappatt = $mark["wrappatt"]; $arg->linkpatt = $arg->nofollow ? str_replace("sep .= $mark["sep_after"]."\n"; } $linkpatt = $arg->linkpatt; // упростим $q_obj = get_queried_object(); // может это архив пустой таксы? $ptype = null; if(empty($post)){ if(isset($q_obj->taxonomy)) $ptype = & $wp_post_types[ get_taxonomy($q_obj->taxonomy)->object_type ]; } else $ptype = & $wp_post_types[ $post->post_type ]; // paged $arg->pg_end = ""; if(($paged_num = get_query_var("paged")) || ($paged_num = get_query_var("page"))) $arg->pg_end = $sep . sprintf($loc->paged, (int) $paged_num); $pg_end = $arg->pg_end; // упростим $out = ""; if(is_front_page()){ return $arg->on_front_page ? sprintf($wrappatt, ($paged_num ? sprintf($linkpatt, get_home_url(), $loc->home) . $pg_end: $loc->home)) : ""; } // страница записей, когда для главной установлена отдельная страница. elseif(is_home()) { $out = $paged_num ? (sprintf($linkpatt, get_permalink($q_obj), esc_html($q_obj->post_title)) . $pg_end) : esc_html($q_obj->post_title); } elseif(is_404()){ $out = $loc->_404; } elseif(is_search()){ $out = sprintf($loc->search, esc_html($GLOBALS["s"])); } elseif(is_author()){ $tit = sprintf($loc->author, esc_html($q_obj->display_name)); $out = ($paged_num ? sprintf($linkpatt, get_author_posts_url($q_obj->ID, $q_obj->user_nicename) . $pg_end, $tit) : $tit); } elseif(is_year() || is_month() || is_day()){ $y_url = get_year_link($year = get_the_time("Y")); if(is_year()){ $tit = sprintf($loc->year, $year); $out = ($paged_num ? sprintf($linkpatt, $y_url, $tit) . $pg_end: $tit); } // month day else { $y_link = sprintf($linkpatt, $y_url, $year); $m_url = get_month_link($year, get_the_time("m")); if(is_month()){ $tit = sprintf($loc->month, get_the_time("F")); $out = $y_link . $sep . ($paged_num ? sprintf($linkpatt, $m_url, $tit) . $pg_end: $tit); } elseif(is_day()){ $m_link = sprintf($linkpatt, $m_url, get_the_time("F")); $out = $y_link . $sep . $m_link . $sep . get_the_time("l"); } } } // Древовидные записи elseif(is_singular() && $ptype->hierarchical){ $out = $this->_add_title($this->_page_crumbs($post), $post); } // Таксы, плоские записи и вложения else { $term = $q_obj; // таксономии // определяем термин для записей (включая вложения attachments) if(is_singular()){ // изменим $post, чтобы определить термин родителя вложения if(is_attachment() && $post->post_parent){ $save_post = $post; // сохраним $post = get_post($post->post_parent); } // учитывает если вложения прикрепляются к таксам древовидным - все бывает:) $taxonomies = get_object_taxonomies($post->post_type); // оставим только древовидные и публичные, мало ли... $taxonomies = array_intersect($taxonomies, get_taxonomies(array("hierarchical" => true, "public" => true))); if($taxonomies){ // сортируем по приоритету if(! empty($arg->priority_tax)){ usort($taxonomies, function($a,$b)use($arg){ $a_index = array_search($a, $arg->priority_tax); if($a_index === false) $a_index = 9999999; $b_index = array_search($b, $arg->priority_tax); if($b_index === false) $b_index = 9999999; return ($b_index === $a_index) ? 0: ($b_index < $a_index ? 1: -1); // меньше индекс - выше }); } // пробуем получить термины, в порядке приоритета такс foreach($taxonomies as $taxname){ if($terms = get_the_terms($post->ID, $taxname)){ // проверим приоритетные термины для таксы $prior_terms = & $arg->priority_terms[ $taxname ]; if($prior_terms && count($terms) > 2){ foreach((array) $prior_terms as $term_id){ $filter_field = is_numeric($term_id) ? "term_id" : "slug"; $_terms = wp_list_filter($terms, array($filter_field=>$term_id)); if($_terms){ $term = array_shift($_terms); break; } } } else $term = array_shift($terms); break; } } } if(isset($save_post)) $post = $save_post; // вернем обратно (для вложений) } // вывод // все виды записей с терминами или термины if($term && isset($term->term_id)){ $term = apply_filters("kama_breadcrumbs_term", $term); // attachment if(is_attachment()){ if(! $post->post_parent) $out = sprintf($loc->attachment, esc_html($post->post_title)); else { if(! $out = apply_filters("attachment_tax_crumbs", "", $term, $this)){ $_crumbs = $this->_tax_crumbs($term, "self"); $parent_tit = sprintf($linkpatt, get_permalink($post->post_parent), get_the_title($post->post_parent)); $_out = implode($sep, array($_crumbs, $parent_tit)); $out = $this->_add_title($_out, $post); } } } // single elseif(is_single()){ if(! $out = apply_filters("post_tax_crumbs", "", $term, $this)){ $_crumbs = $this->_tax_crumbs($term, "self"); $out = $this->_add_title($_crumbs, $post); } } // не древовидная такса (метки) elseif(! is_taxonomy_hierarchical($term->taxonomy)){ // метка if(is_tag()) $out = $this->_add_title("", $term, sprintf($loc->tag, esc_html($term->name))); // такса elseif(is_tax()){ $post_label = $ptype->labels->name; $tax_label = $GLOBALS["wp_taxonomies"][ $term->taxonomy ]->labels->name; $out = $this->_add_title("", $term, sprintf($loc->tax_tag, $post_label, $tax_label, esc_html($term->name))); } } // древовидная такса (рибрики) else { if(! $out = apply_filters("term_tax_crumbs", "", $term, $this)){ $_crumbs = $this->_tax_crumbs($term, "parent"); $out = $this->_add_title($_crumbs, $term, esc_html($term->name)); } } } // влоежния от записи без терминов elseif(is_attachment()){ $parent = get_post($post->post_parent); $parent_link = sprintf($linkpatt, get_permalink($parent), esc_html($parent->post_title)); $_out = $parent_link; // вложение от записи древовидного типа записи if(is_post_type_hierarchical($parent->post_type)){ $parent_crumbs = $this->_page_crumbs($parent); $_out = implode($sep, array($parent_crumbs, $parent_link)); } $out = $this->_add_title($_out, $post); } // записи без терминов elseif(is_singular()){ $out = $this->_add_title("", $post); } } // замена ссылки на архивную страницу для типа записи $home_after = apply_filters("kama_breadcrumbs_home_after", "", $linkpatt, $sep, $ptype); if("" === $home_after){ // Ссылка на архивную страницу типа записи для: отдельных страниц этого типа; архивов этого типа; таксономий связанных с этим типом. if($ptype && $ptype->has_archive && ! in_array($ptype->name, array("post","page","attachment")) && (is_post_type_archive() || is_singular() || (is_tax() && in_array($term->taxonomy, $ptype->taxonomies)))){ $pt_title = $ptype->labels->name; // первая страница архива типа записи if(is_post_type_archive() && ! $paged_num) $home_after = sprintf($this->arg->title_patt, $pt_title); // singular, paged post_type_archive, tax else{ $home_after = sprintf($linkpatt, get_post_type_archive_link($ptype->name), $pt_title); $home_after .= (($paged_num && ! is_tax()) ? $pg_end: $sep); // пагинация } } } $before_out = sprintf($linkpatt, home_url(), $loc->home) . ($home_after ? $sep.$home_after: ($out ? $sep: "")); $out = apply_filters("kama_breadcrumbs_pre_out", $out, $sep, $loc, $arg); $out = sprintf($wrappatt, $before_out . $out); return apply_filters("kama_breadcrumbs", $out, $sep, $loc, $arg); } function _page_crumbs($post){ $parent = $post->post_parent; $crumbs = array(); while($parent){ $page = get_post($parent); $crumbs = sprintf($this->arg->linkpatt, get_permalink($page), esc_html($page->post_title)); $parent = $page->post_parent; } return implode($this->arg->sep, array_reverse($crumbs)); } function _tax_crumbs($term, $start_from = "self"){ $termlinks = array(); $term_id = ($start_from === "parent") ? $term->parent: $term->term_id; while($term_id){ $term = get_term($term_id, $term->taxonomy); $termlinks = sprintf($this->arg->linkpatt, get_term_link($term), esc_html($term->name)); $term_id = $term->parent; } if($termlinks) return implode($this->arg->sep, array_reverse($termlinks)) /*. $this->arg->sep*/; return ""; } // добалвяет заголовок к переданному тексту, с учетом всех опций. Добавляет разделитель в начало, если надо. function _add_title($add_to, $obj, $term_title = ""){ $arg = & $this->arg; // упростим... $title = $term_title ? $term_title: esc_html($obj->post_title); // $term_title чиститься отдельно, теги моугт быть... $show_title = $term_title ? $arg->show_term_title: $arg->show_post_title; // пагинация if($arg->pg_end){ $link = $term_title ? get_term_link($obj) : get_permalink($obj); $add_to .= ($add_to ? $arg->sep: "") . sprintf($arg->linkpatt, $link, $title) . $arg->pg_end; } // дополняем - ставим sep elseif($add_to){ if($show_title) $add_to .= $arg->sep . sprintf($arg->title_patt, $title); elseif($arg->last_sep) $add_to .= $arg->sep; } // sep будет потом... elseif($show_title) $add_to = sprintf($arg->title_patt, $title); return $add_to; } } /** * Изменения: * 3.3 - новые хуки: attachment_tax_crumbs, post_tax_crumbs, term_tax_crumbs. Позволяют дополнить крошки таксономий. * 3.2 - баг с разделителем, с отключенным "show_term_title". Стабилизировал логику. * 3.1 - баг с esc_html() для заголовка терминов - с тегами получалось криво... * 3.0 - Обернул в класс. Добавил опции: "title_patt", "last_sep". Доработал код. Добавил пагинацию для постов. * 2.5 - ADD: Опция "show_term_title" * 2.4 - Мелкие правки кода * 2.3 - ADD: Страница записей, когда для главной установлена отделенная страница. * 2.2 - ADD: Link to post type archive on taxonomies page * 2.1 - ADD: $sep, $loc, $args params to hooks * 2.0 - ADD: в фильтр "kama_breadcrumbs_home_after" добавлен четвертый аргумент $ptype * 1.9 - ADD: фильтр "kama_breadcrumbs_default_loc" для изменения локализации по умолчанию * 1.8 - FIX: заметки, когда в рубрике нет записей * 1.7 - Улучшена работа с приоритетными таксономиями. */

Вставлять этот код нужно в файл шаблона functions.php или непосредственно в тот файл где вызывается функция.

Вызывать функцию нужно в шаблоне, в том месте, где должны выводится крошки, так:

Примеры использование фильтров

#1 Установка параметров через фильтр

Измени дефолтные параметры через фильтр

Add_filter("kama_breadcrumbs_default_args", function($args){ $args["on_front_page"] = 0; $args["show_post_title"] = ""; $args["priority_tax"] = array("mytax"); return $args; });

Если установить параметры при вызове функции в третьем аргументе функции, то они перебьют параметры указанные в фильтре...

#3 Пример перевода крошек на английский

Эти примеры показывают как перевести крошки на нужный язык или просто изменить дефолтные значения:

Вариант 1

При вызове функции нужно указать строки локализации так:

// Локализация if(function_exists("kama_breadcrumbs")){ $myl10n = array("home" => "Front page", "paged" => "Page %d", "_404" => "Error 404", "search" => "Search results by query - %s", "author" => "Author archve: %s", "year" => "Archive by %d год", "month" => "Archive by: %s", "day" => "", "attachment" => "Media: %s", "tag" => "Posts by tag: %s", "tax_tag" => "%1$s from "%2$s" by tag: %3$s", // tax_tag выведет: "тип_записи из "название_таксы" по тегу: имя_термина". // Если нужны отдельные холдеры, например только имя термина, пишем так: "записи по тегу: %3$s"); kama_breadcrumbs(" » ", $myl10n); }

Вариант 2

C версии 1.9. Можно использовать хук kama_breadcrumbs_default_loc , чтобы для каждого вызова одно и тоже не указывать. Для этого рядом с исходным кодом крошек добавьте такой хук:

Add_filter("kama_breadcrumbs_default_loc", function($l10n){ // Локализация return array("home" => "Front page", "paged" => "Page %d", "_404" => "Error 404", "search" => "Search results by query - %s", "author" => "Author archve: %s", "year" => "Archive by %d год", "month" => "Archive by: %s", "day" => "", "attachment" => "Media: %s", "tag" => "Posts by tag: %s", "tax_tag" => "%1$s from "%2$s" by tag: %3$s", // tax_tag выведет: "тип_записи из "название_таксы" по тегу: имя_термина". // Если нужны отдельные холдеры, например только имя термина, пишем так: "записи по тегу: %3$s"); });

Function_exists("kama_breadcrumbs") && kama_breadcrumbs();

Установки через хук имеют меньший приоритет, чем через вызов функции из варианта 1. Это значит, что если указать локализацию через хук, то потом через вызов можно перебить локализацию отдельных строк.

#3 Добавление произвольной ссылки в начало крошек

Допустим нам нужно добавить после пункта "Главная" ссылку на страницу 7, если в текущий момент мы находимся в категории 5 или в её дочерней категории (учитывается один уровень вложенности).

Для этого добавьте такой хук рядом с кодом крошек:

Add_action("kama_breadcrumbs_home_after", "my_breadcrumbs_home_after", 10, 4); function my_breadcrumbs_home_after($false, $linkpatt, $sep, $ptype){ // если мы в рубрике с ID 5 или в дочерней рубрике, // то дополним начало крошек ссылкой на страницу с ID 7 $qo = get_queried_object(); if(is_category() && ($qo->term_id == 5 || $qo->parent == 5)){ $page = get_post(7); return sprintf($linkpatt, get_permalink($page), $page->post_title) . $sep; } return $false; }

#4 Добавление еще таксономий в крошки

По умолчанию в крошках обрабатывается только одна таксономи. Но иногда нужно несколько, для этого в версии 3.3 я вставил хуки: "attachment_tax_crumbs" , "post_tax_crumbs" , "term_tax_crumbs" .

Допустим, у нас есть тип записи realty и 3 таксы для него: country , type_deal , type_realty . Нужно, чтобы у страницы записи в крошках отображались все таксы в указанном порядке. Также нужно, чтобы на каждой странице таксы указывались все предыдущие таксы и текущая в указанном порядке: country > type_deal > type_realty ...

// apply_filters("term_tax_crumbs", "", $term, $that); add_filter("term_tax_crumbs", "more_tax_crumbs", 10, 3); add_filter("post_tax_crumbs", "more_tax_crumbs", 10, 3); function more_tax_crumbs($empty, $term, $that){ $is_post_filter = doing_filter("post_tax_crumbs"); // else "term_tax_crumbs" if(($is_post_filter && is_singular("realty")) || is_tax("country")){ global $post; $out = ""; $out = $that->_tax_crumbs($term, "self") . $that->arg->sep; // базовая такса - country // тип сделки $term = get_query_var("type_deal"); if($term && ($term = get_term_by("slug", $term, "type_deal"))) $out .= $that->_tax_crumbs($term, "self") . $that->arg->sep; // тип сделки // тип недвижимости $term = get_query_var("type_realty"); if($term && ($term = get_term_by("slug", $term, "type_realty"))){ // запись if($is_post_filter){ $_crumbs = $that->_tax_crumbs($term, "self"); $out .= $that->_add_title($_crumbs, $post); } // такса else { $_crumbs = $that->_tax_crumbs($term, "parent"); $out .= $that->_add_title($_crumbs, $term, esc_html($term->name)); } } return $out; } return $empty; }

Другой вариант крошек

Этот вариант я стянул по ссылке , которую в комментариях дал Master . Весьма занимательное решение, потому и не удержался.

Условно, этот код подойдет не только к WordPress, а вообще к любому движку. Для WordPress он подойдет:

  • во-первых, если включены ЧПУ ;
  • во-вторых, если в ссылках присутствуют названия категории;
  • в-третьих, если названия статей и категорий в УРЛ пишутся в кириллице или это вообще англ. блог.

В других случаях будет работать, но, думаю, как-то не круто получится. Такие условия, потому что этот вариант разбирает ссылку на страницу (УРЛ) и по её элементам создает хлебные крошки. Ссылка разбивается разделителем / .

Допустим у нас УРЛ на статью имеет вид:
http://wptest.ru/рецепты/торт/готовим наполеон
тогда, мы получим цепочку крошек вида:
Главная » Рецепты » Торт » Готовим наполеон

Function breadcrumbs($separator = " » ", $home = "Главная") { $path = array_filter(explode("/", parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH))); $base_url = ($_SERVER["HTTPS"] ? "https" : "http") . "://" . $_SERVER["HTTP_HOST"] . "/"; $breadcrumbs = array("$home"); $last = end(array_keys($path)); foreach($path as $x => $crumb){ $title = ucwords(str_replace(array(".php", "_"), Array("", " "), $crumb)); if($x != $last){ $breadcrumbs = "".$title.""; } else { $breadcrumbs = $title; } } return implode($separator, $breadcrumbs); }

Используется аналогично моей функции, только на экран выводить надо через echo:

Получение частных данных не всегда означает взлом - иногда они опубликованы в общем доступе. Знание настроек Google и немного смекалки позволят найти массу интересного - от номеров кредиток до документов ФБР.

WARNING

Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи.

К интернету сегодня подключают всё подряд, мало заботясь об ограничении доступа. Поэтому многие приватные данные становятся добычей поисковиков. Роботы-«пауки» уже не ограничиваются веб-страницами, а индексируют весь доступный в Сети контент и постоянно добавляют в свои базы не предназначенную для разглашения информацию. Узнать эти секреты просто - нужно лишь знать, как именно спросить о них.

Ищем файлы

В умелых руках Google быстро найдет все, что плохо лежит в Сети, - например, личную информацию и файлы для служебного использования. Их частенько прячут, как ключ под половиком: настоящих ограничений доступа нет, данные просто лежат на задворках сайта, куда не ведут ссылки. Стандартный веб-интерфейс Google предоставляет лишь базовые настройки расширенного поиска, но даже их будет достаточно.

Ограничить поиск по файлам определенного вида в Google можно с помощью двух операторов: filetype и ext . Первый задает формат, который поисковик определил по заголовку файла, второй - расширение файла, независимо от его внутреннего содержимого. При поиске в обоих случаях нужно указывать лишь расширение. Изначально оператор ext было удобно использовать в тех случаях, когда специфические признаки формата у файла отсутствовали (например, для поиска конфигурационных файлов ini и cfg, внутри которых может быть все что угодно). Сейчас алгоритмы Google изменились, и видимой разницы между операторами нет - результаты в большинстве случаев выходят одинаковые.


Фильтруем выдачу

По умолчанию слова и вообще любые введенные символы Google ищет по всем файлам на проиндексированных страницах. Ограничить область поиска можно по домену верхнего уровня, конкретному сайту или по месту расположения искомой последовательности в самих файлах. Для первых двух вариантов используется оператор site, после которого вводится имя домена или выбранного сайта. В третьем случае целый набор операторов позволяет искать информацию в служебных полях и метаданных. Например, allinurl отыщет заданное в теле самих ссылок, allinanchor - в тексте, снабженном тегом , allintitle - в заголовках страниц, allintext - в теле страниц.

Для каждого оператора есть облегченная версия с более коротким названием (без приставки all). Разница в том, что allinurl отыщет ссылки со всеми словами, а inurl - только с первым из них. Второе и последующие слова из запроса могут встречаться на веб-страницах где угодно. Оператор inurl тоже имеет отличия от другого схожего по смыслу - site . Первый также позволяет находить любую последовательность символов в ссылке на искомый документ (например, /cgi-bin/), что широко используется для поиска компонентов с известными уязвимостями.

Попробуем на практике. Берем фильтр allintext и делаем так, чтобы запрос выдал список номеров и проверочных кодов кредиток, срок действия которых истечет только через два года (или когда их владельцам надоест кормить всех подряд).

Allintext: card number expiration date /2017 cvv

Когда читаешь в новостях, что юный хакер «взломал серверы» Пентагона или NASA, украв секретные сведения, то в большинстве случаев речь идет именно о такой элементарной технике использования Google. Предположим, нас интересует список сотрудников NASA и их контактные данные. Наверняка такой перечень есть в электронном виде. Для удобства или по недосмотру он может лежать и на самом сайте организации. Логично, что в этом случае на него не будет ссылок, поскольку предназначен он для внутреннего использования. Какие слова могут быть в таком файле? Как минимум - поле «адрес». Проверить все эти предположения проще простого.


Inurl:nasa.gov filetype:xlsx "address"


Пользуемся бюрократией

Подобные находки - приятная мелочь. По-настоящему же солидный улов обеспечивает более детальное знание операторов Google для веб-мастеров, самой Сети и особенностей структуры искомого. Зная детали, можно легко отфильтровать выдачу и уточнить свойства нужных файлов, чтобы в остатке получить действительно ценные данные. Забавно, что здесь на помощь приходит бюрократия. Она плодит типовые формулировки, по которым удобно искать случайно просочившиеся в Сеть секретные сведения.

Например, обязательный в канцелярии министерства обороны США штамп Distribution statement означает стандартизированные ограничения на распространение документа. Литерой A отмечаются публичные релизы, в которых нет ничего секретного; B - предназначенные только для внутреннего использования, C - строго конфиденциальные и так далее до F. Отдельно стоит литера X, которой отмечены особо ценные сведения, представляющие государственную тайну высшего уровня. Пускай такие документы ищут те, кому это положено делать по долгу службы, а мы ограничимся файлами с литерой С. Согласно директиве DoDI 5230.24, такая маркировка присваивается документам, содержащим описание критически важных технологий, попадающих под экспортный контроль. Обнаружить столь тщательно охраняемые сведения можно на сайтах в домене верхнего уровня.mil, выделенного для армии США.

"DISTRIBUTION STATEMENT C" inurl:navy.mil

Очень удобно, что в домене.mil собраны только сайты из ведомства МО США и его контрактных организаций. Поисковая выдача с ограничением по домену получается исключительно чистой, а заголовки - говорящими сами за себя. Искать подобным образом российские секреты практически бесполезно: в доменах.ru и.рф царит хаос, да и названия многих систем вооружения звучат как ботанические (ПП «Кипарис», САУ «Акация») или вовсе сказочные (ТОС «Буратино»).


Внимательно изучив любой документ с сайта в домене.mil, можно увидеть и другие маркеры для уточнения поиска. Например, отсылку к экспортным ограничениям «Sec 2751», по которой также удобно искать интересную техническую информацию. Время от времени ее изымают с официальных сайтов, где она однажды засветилась, поэтому, если в поисковой выдаче не удается перейти по интересной ссылке, воспользуйся кешем Гугла (оператор cache) или сайтом Internet Archive.

Забираемся в облака

Помимо случайно рассекреченных документов правительственных ведомств, в кеше Гугла временами всплывают ссылки на личные файлы из Dropbox и других сервисов хранения данных, которые создают «приватные» ссылки на публично опубликованные данные. С альтернативными и самодельными сервисами еще хуже. Например, следующий запрос находит данные всех клиентов Verizon, у которых на роутере установлен и активно используется FTP-сервер.

Allinurl:ftp:// verizon.net

Таких умников сейчас нашлось больше сорока тысяч, а весной 2015-го их было на порядок больше. Вместо Verizon.net можно подставить имя любого известного провайдера, и чем он будет известнее, тем крупнее может быть улов. Через встроенный FTP-сервер видно файлы на подключенном к маршрутизатору внешнем накопителе. Обычно это NAS для удаленной работы, персональное облако или какая-нибудь пиринговая качалка файлов. Все содержимое таких носителей оказывается проиндексировано Google и другими поисковиками, поэтому получить доступ к хранящимся на внешних дисках файлам можно по прямой ссылке.

Подсматриваем конфиги

До повальной миграции в облака в качестве удаленных хранилищ рулили простые FTP-серверы, в которых тоже хватало уязвимостей. Многие из них актуальны до сих пор. Например, у популярной программы WS_FTP Professional данные о конфигурации, пользовательских аккаунтах и паролях хранятся в файле ws_ftp.ini . Его просто найти и прочитать, поскольку все записи сохраняются в текстовом формате, а пароли шифруются алгоритмом Triple DES после минимальной обфускации. В большинстве версий достаточно просто отбросить первый байт.

Расшифровать такие пароли легко с помощью утилиты WS_FTP Password Decryptor или бесплатного веб-сервиса .

Говоря о взломе произвольного сайта, обычно подразумевают получение пароля из логов и бэкапов конфигурационных файлов CMS или приложений для электронной коммерции. Если знаешь их типовую структуру, то легко сможешь указать ключевые слова. Строки, подобные встречающимся в ws_ftp.ini , крайне распространены. Например, в Drupal и PrestaShop обязательно есть идентификатор пользователя (UID) и соответствующий ему пароль (pwd), а хранится вся информация в файлах с расширением.inc. Искать их можно следующим образом:

"pwd=" "UID=" ext:inc

Раскрываем пароли от СУБД

В конфигурационных файлах SQL-серверов имена и адреса электронной почты пользователей хранятся в открытом виде, а вместо паролей записаны их хеши MD5. Расшифровать их, строго говоря, невозможно, однако можно найти соответствие среди известных пар хеш - пароль.

До сих пор встречаются СУБД, в которых не используется даже хеширование паролей. Конфигурационные файлы любой из них можно просто посмотреть в браузере.

Intext:DB_PASSWORD filetype:env

С появлением на серверах Windows место конфигурационных файлов отчасти занял реестр. Искать по его веткам можно точно таким же образом, используя reg в качестве типа файла. Например, вот так:

Filetype:reg HKEY_CURRENT_USER "Password"=

Не забываем про очевидное

Иногда добраться до закрытой информации удается с помощью случайно открытых и попавших в поле зрения Google данных. Идеальный вариант - найти список паролей в каком-нибудь распространенном формате. Хранить сведения аккаунтов в текстовом файле, документе Word или электронной таблице Excel могут только отчаянные люди, но как раз их всегда хватает.

Filetype:xls inurl:password

С одной стороны, есть масса средств для предотвращения подобных инцидентов. Необходимо указывать адекватные права доступа в htaccess, патчить CMS, не использовать левые скрипты и закрывать прочие дыры. Существует также файл со списком исключений robots.txt, запрещающий поисковикам индексировать указанные в нем файлы и каталоги. С другой стороны, если структура robots.txt на каком-то сервере отличается от стандартной, то сразу становится видно, что на нем пытаются скрыть.

Список каталогов и файлов на любом сайте предваряется стандартной надписью index of. Поскольку для служебных целей она должна встречаться в заголовке, то имеет смысл ограничить ее поиск оператором intitle . Интересные вещи находятся в каталогах /admin/, /personal/, /etc/ и даже /secret/.

Следим за обновлениями

Актуальность тут крайне важна: старые уязвимости закрывают очень медленно, но Google и его поисковая выдача меняются постоянно. Есть разница даже между фильтром «за последнюю секунду» (&tbs=qdr:s в конце урла запроса) и «в реальном времени» (&tbs=qdr:1).

Временной интервал даты последнего обновления файла у Google тоже указывается неявно. Через графический веб-интерфейс можно выбрать один из типовых периодов (час, день, неделя и так далее) либо задать диапазон дат, но такой способ не годится для автоматизации.

По виду адресной строки можно догадаться только о способе ограничить вывод результатов с помощью конструкции &tbs=qdr: . Буква y после нее задает лимит в один год (&tbs=qdr:y), m показывает результаты за последний месяц, w - за неделю, d - за прошедший день, h - за последний час, n - за минуту, а s - за секунду. Самые свежие результаты, только что ставшие известными Google, находится при помощи фильтра &tbs=qdr:1 .

Если требуется написать хитрый скрипт, то будет полезно знать, что диапазон дат задается в Google в юлианском формате через оператор daterange . Например, вот так можно найти список документов PDF со словом confidential, загруженных c 1 января по 1 июля 2015 года.

Confidential filetype:pdf daterange:2457024-2457205

Диапазон указывается в формате юлианских дат без учета дробной части. Переводить их вручную с григорианского календаря неудобно. Проще воспользоваться конвертером дат .

Таргетируемся и снова фильтруем

Помимо указания дополнительных операторов в поисковом запросе их можно отправлять прямо в теле ссылки. Например, уточнению filetype:pdf соответствует конструкция as_filetype=pdf . Таким образом удобно задавать любые уточнения. Допустим, выдача результатов только из Республики Гондурас задается добавлением в поисковый URL конструкции cr=countryHN , а только из города Бобруйск - gcs=Bobruisk . В разделе для разработчиков можно найти полный список .

Средства автоматизации Google призваны облегчить жизнь, но часто добавляют проблем. Например, по IP пользователя через WHOIS определяется его город. На основании этой информации в Google не только балансируется нагрузка между серверами, но и меняются результаты поисковой выдачи. В зависимости от региона при одном и том же запросе на первую страницу попадут разные результаты, а часть из них может вовсе оказаться скрытой. Почувствовать себя космополитом и искать информацию из любой страны поможет ее двухбуквенный код после директивы gl=country . Например, код Нидерландов - NL, а Ватикану и Северной Корее в Google свой код не положен.

Часто поисковая выдача оказывается замусоренной даже после использования нескольких продвинутых фильтров. В таком случае легко уточнить запрос, добавив к нему несколько слов-исключений (перед каждым из них ставится знак минус). Например, со словом Personal часто употребляются banking , names и tutorial . Поэтому более чистые поисковые результаты покажет не хрестоматийный пример запроса, а уточненный:

Intitle:"Index of /Personal/" -names -tutorial -banking

Пример напоследок

Искушенный хакер отличается тем, что обеспечивает себя всем необходимым самостоятельно. Например, VPN - штука удобная, но либо дорогая, либо временная и с ограничениями. Оформлять подписку для себя одного слишком накладно. Хорошо, что есть групповые подписки, а с помощью Google легко стать частью какой-нибудь группы. Для этого достаточно найти файл конфигурации Cisco VPN, у которого довольно нестандартное расширение PCF и узнаваемый путь: Program Files\Cisco Systems\VPN Client\Profiles . Один запрос, и ты вливаешься, к примеру, в дружный коллектив Боннского университета.

Filetype:pcf vpn OR Group

INFO

Google находит конфигурационные файлы с паролями, но многие из них записаны в зашифрованном виде или заменены хешами. Если видишь строки фиксированной длины, то сразу ищи сервис расшифровки.

Пароли хранятся в зашифрованном виде, но Морис Массар уже написал программу для их расшифровки и предоставляет ее бесплатно через thecampusgeeks.com .

При помощи Google выполняются сотни разных типов атак и тестов на проникновение. Есть множество вариантов, затрагивающих популярные программы, основные форматы баз данных, многочисленные уязвимости PHP, облаков и так далее. Если точно представлять то, что ищешь, это сильно упростит получение нужной информации (особенно той, которую не планировали делать всеобщим достоянием). Не Shodan единый питает интересными идеями, но всякая база проиндексированных сетевых ресурсов!

Как правильно искать с помощью google.com

Все наверное умеют пользоваться такой поисковой системой, как гугл =) Но не все знают, что если грамотно составить поисковой запрос с помощью специальных конструкций, то можно достичь результатов того, что Вы ищете намного эффективнее и быстрее =) В этой статье я постараюсь показать что и как Вам нужно делать, чтобы искать правильно

Гугл поддерживает несколько расширенных операторов поиска, имеющими специальное значение при поиске на google.com. Типично, эти операторы изменяют поиск, или даже говорят гуглу делать полностью различные типы поиска. Например, конструкция link: является специальным оператором, и запрос link:www.google.com не даст вам нормального поиска, но вместо этого найдет все web-страницы, которые имеют связи к google.com.
альтернативные типы запросов

cache: Если Вы будете включать другие слова в запрос, то Гугл подсветит эти включенные слова в пределах кэшируемого документа.
Например, cache:www.сайт web покажет кэшируемое содержимое с подсвеченным словом "web".

link: рассматриваемый выше поисковой запрос покажет веб-страницы, на которых содержатся ссылки к указанному запросу.
Например: link:www.сайт отобразит все страницы на которых есть ссылка на http://www.сайт

related: Отобразит web-страницы, которые являются "подобными" (related) указанной web-странице.
Например, related: www.google.com перечислит web-страницы, которые являются подобными домашней странице Гугл.

info: Информация запроса: представит немного информации, которую Гугл имеет о запрашиваемой web-странице.
Например, info:сайт покажет информацию о нашем форуме =) (Армада - Форум адалт вебмастеров).

Другие информационные запросы

define: Запрос define: обеспечит определение слов, которые Вы вводите после того, как это, собранный из различных сетевых источников. Определение будет для всей введенной фразы (то есть, это будет включать все слова в точный запрос).

stocks: Если Вы начинаете запрос с stocks: Гугл обработает остальную часть сроков запроса как символы биржевых сводок, и свяжется со страницей, показывающую готовую информацию для этих символов.
Например, stocks: Intel yahoo покажет информацию о Intel и Yahoo. (Отметьте, что Вы должны напечатать символы последних новостей, не название компании)

Модификаторы Запросов

site: Если Вы включаете site: в ваш запрос, Гугл ограничит результаты теми вебсайтами, которые найдет в данном домене.
Также можно искать и по отдельным зонам, как таковое ru, org, com, etc ( site:com site:ru )

allintitle: Если Вы запускаете запрос с allintitle:, Гугл ограничит результаты со всеми словами запроса в заголовке.
Например, allintitle: google search вернет все страницы гугла по поиску как то images, Blog, etc

intitle: Если Вы включаете intitle: в вашем запросе, Гугл ограничит результаты документами, содержащими то слово в заголовке.
Например, intitle:Бизнес

allinurl: Если Вы запускаете запрос с allinurl: Гугл ограничит результаты, со всеми словами запроса в URL.
Например, allinurl: google search вернет документы с google и search в заголовке. Также как вариант можно разделять слова слэшем (/) тогда слова по обе стороны слэша будут искаться в пределах одной страницы: Пример allinurl: foo/bar

inurl: Если Вы включаете inurl: в вашем запросе, Гугл ограничит результаты документами, содержащими то слово в URL.
Например, Animation inurl:сайт

intext: ищет только в тексте страницы указанное слово, игнорируя заглавие и тексты ссылок, и прочее не относящееся к. Есть также и производная этого модификатора - allintext: т.е. далее все слова в запросе будут искаться только в тексте, что тоже бывает важно, игнорируя часто используемые слова в ссылках
Например, intext:форум

daterange: ищет во временных рамках (daterange:2452389-2452389), даты для времени указываются в Юлианском формате.

Ну и еще всякие интересные примеры запросов

Примеры составления запросов для Google. Для спамеров

Inurl:control.guest?a=sign

Site:books.dreambook.com “Homepage URL” “Sign my” inurl:sign

Site:www.freegb.net Homepage

Inurl:sign.asp “Character Count”

“Message:” inurl:sign.cfm “Sender:”

Inurl:register.php “User Registration” “Website”

Inurl:edu/guestbook “Sign the Guestbook”

Inurl:post “Post Comment” “URL”

Inurl:/archives/ “Comments:” “Remember info?”

“Script and Guestbook Created by:” “URL:” “Comments:”

Inurl:?action=add “phpBook” “URL”

Intitle:”Submit New Story”

Журналы

Inurl:www.livejournal.com/users/ mode=reply

Inurl greatestjournal.com/ mode=reply

Inurl:fastbb.ru/re.pl?

Inurl:fastbb.ru /re.pl? “Гостевая книга”

Блоги

Inurl:blogger.com/comment.g?”postID”"anonymous”

Inurl:typepad.com/ “Post a comment” “Remember personal info?”

Inurl:greatestjournal.com/community/ “Post comment” “addresses of anonymous posters”

“Post comment” “addresses of anonymous posters” -

Intitle:”Post comment”

Inurl:pirillo.com “Post comment”

Форумы

Inurl:gate.html?”name=Forums” “mode=reply”

Inurl:”forum/posting.php?mode=reply”

Inurl:”mes.php?”

Inurl:”members.html”

Inurl:forum/memberlist.php?”