В чем разница между offer и suggest? Конструкции с глаголом offer

USSCLTD 10 мая 2016 в 12:38

Ищем уязвимости с помощью google

  • Информационная безопасность

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

Как такое возможно?
Поисковые роботы, безостановочно бродящие по просторам интернета, помимо информации, полезной обычному пользователю, часто фиксируют то, что может быть использовано злоумышленниками при атаке на веб-ресурс. Например, ошибки скриптов и файлы с чувствительной информацией (начиная от конфигурационных файлов и логов, заканчивая файлами с аутентификационными данными и бэкапами баз данных).
С точки зрения поискового робота сообщение об ошибке выполнения sql-запроса - это обычный текст, неотделимый, например, от описания товаров на странице. Если вдруг поисковый робот наткнулся на файл с расширением.sql, который почему-то оказался в рабочей папке сайта, то он будет воспринят как часть содержимого сайта и так же будет проиндексирован (включая, возможно, указанные в нём пароли).

Подобную информацию можно найти, зная устойчивые, часто уникальные, ключевые слова, которые помогают отделить «уязвимые страницы» от страниц, не содержащих уязвимости.
Огромная база специальных запросов с использованием ключевых слов (так называемых дорков) существует на exploit-db.com и известна под названием Google Hack Database.

Почему google?
Дорки ориентированы в первую очередь на google по двум причинам:
− наиболее гибкий синтаксис ключевых слов (приведен в Таблице 1) и специальных символов (приведен в Таблице 2);
− индекс google всё же более полный нежели у других поисковых систем;

Таблица 1 – Основные ключевые слова google

Ключевое слово
Смысл
Пример
site
Поиск только на указанном сайте. Учитывает только url
site:somesite.ru - найдет все страницы по данному домену и поддоменам
inurl
Поиск по словам, присутствующим в uri. В отличие от кл. слова “site”, ищет совпадения после имени сайта
inurl:news - найдет все страницы, где в uri встретится данное слово
intext
Поиск в теле страницы
intext:”пробки” - полностью аналогично обычному запросу “пробки”
intitle
Поиск в заголовке страницы. Текст, заключенный между тэгами <br></td> <td width="214">intitle:”index of” - найдет все страницы с листингом директории<br></td> </tr><tr><td width="214">ext<br></td> <td width="214">Поиск страниц с указанным расширением<br></td> <td width="214">ext:pdf - найдет все pdf-файлы<br></td> </tr><tr><td width="214">filetype<br></td> <td width="214">В настоящее время полностью аналогично кл. слову “ext”<br></td> <td width="214">filetype:pdf - аналогично<br></td> </tr><tr><td width="214">related<br></td> <td width="214">Поиск сайтов со схожей тематикой<br></td> <td width="214">related:google.ru - покажет свои аналоги<br></td> </tr><tr><td width="214">link<br></td> <td width="214">Поиск сайтов, ссылающихся на данный<br></td> <td width="214">link:somesite.ru - найдет все сайты, на которых есть ссылка на данный<br></td> </tr><tr><td width="214">define<br></td> <td width="214">Показать определение слова<br></td> <td width="214">define:0day - определение термина<br></td> </tr><tr><td width="214">cache<br></td> <td width="214">Показать содержимое страницы в кэше (если имеется)<br></td> <td width="214">cache:google.com - откроет страницу из кэша<br></td> </tr></tbody></table><p>Таблица 2 – Специальные символы запросов google<br></p><table><tbody><tr><td width="214"><b>Символ </b><br></td> <td width="214"><b>Смысл </b><br></td> <td width="214"><b>Пример </b><br></td> </tr><tr><td width="214">“<br></td> <td width="214">Точная фраза<br></td> <td width="214">intitle:«RouterOS router configuration page» - поиск роутеров<br></td> </tr><tr><td width="214">*<br></td> <td width="214">Любой текст<br></td> <td width="214">inurl:«bitrix*mcart» - поиск сайтов на bitrix с уязвимым модулем mcart<br></td> </tr><tr><td width="214">.<br></td> <td width="214">Любой символ<br></td> <td width="214">Index.of - аналогично запросу index of<br></td> </tr><tr><td width="214">-<br></td> <td width="214">Исключить слово<br></td> <td width="214">error -warning - показать все страницы, где есть error, но нет warning<br></td> </tr><tr><td width="214">..<br></td> <td width="214">Диапазон<br></td> <td width="214">cve 2006..2016 - показать уязвимости по годам начиная с 2006<br></td> </tr><tr><td width="214">|<br></td> <td width="214">Логическое «или»<br></td> <td width="214">linux | windows - показать страницы, где встречается либо первое либо второе слово<br></td> </tr></tbody></table><br> Стоит понимать, что любой запрос к поисковой системе - это поиск только по словам.<br> Бесполезно искать на странице мета-символы (кавычки, скобки, знаки пунктуации и т. п). Даже поиск по точной фразе, указанной в кавычках, - это поиск по словам, с последующим поиском точного совпадения уже в результатах.<p>Все дорки Google Hack Database разделены логически на 14 категорий и представлены в таблице 3.<br> Таблица 3 – Категории Google Hack Database<br></p><table><tbody><tr><td width="168"><b>Категория </b><br></td> <td width="190"><b>Что позволяет найти </b><br></td> <td width="284"><b>Пример </b><br></td> </tr><tr><td width="168">Footholds<br></td> <td width="190">Веб-шеллы, публичные файловые менеджеры<br></td> <td width="284">Найти все взломанные сайты, где залиты перечисленные вебшеллы:<br> (intitle:«phpshell» OR intitle:«c99shell» OR intitle:«r57shell» OR intitle:«PHP Shell » OR intitle:«phpRemoteView») `rwx` «uname»<br></td> </tr><tr><td width="168">Files containing usernames<br></td> <td width="190">Файлы реестра, конфигурационные файлы, логи, файлы, содержащие историю введенных команд<br></td> <td width="284">Найти все файлы реестра, содержащие информацию об аккаунтах:<br><i>filetype:reg reg +intext:«internet account manager» </i><br></td> </tr><tr><td width="168">Sensitive Directories<br></td> <td width="190">Каталоги с различной информацией (личные документы, конфиги vpn, скрытые репозитории и т.д.)<br></td> <td width="284">Найти все листинги директорий содержащих файлы, относящиеся к vpn:<br><i>«Config» intitle:«Index of» intext:vpn </i><br> Сайты, содержащие git-репозитории:<br><i>(intext:«index of /.git») («parent directory») </i><br></td> </tr><tr><td width="168">Web Server Detection<br></td> <td width="190">Версию и иную информацию о веб-сервере<br></td> <td width="284">Найти административные консоли сервера JBoss:<br><i>inurl:"/web-console/" intitle:«Administration Console» </i><br></td> </tr><tr><td width="168">Vulnerable Files<br></td> <td width="190">Скрипты, содержащие известные уязвимости<br></td> <td width="284">Найти сайты, использующие скрипт, позволяющий выгрузить произвольный файл с сервера:<br><i>allinurl:forcedownload.php?file= </i><br></td> </tr><tr><td width="168">Vulnerable Servers<br></td> <td width="190">Инсталляционные скрипты, веб-шеллы, открытые административные консоли и т.д<br></td> <td width="284">Найти открытые PHPMyAdmin консоли, запущенные от root:<br><i>intitle:phpMyAdmin «Welcome to phpMyAdmin ***» «running on * as root@*» </i><br></td> </tr><tr><td width="168">Error Messages<br></td> <td width="190">Различные ошибки и предупреждения часто раскрывающие важную информацию - начиная от версии CMS до паролей<br></td> <td width="284">Сайты, имеющие ошибки в выполнении sql-запросов к базе:<br><i>«Warning: mysql_query()» «invalid query» </i><br></td> </tr><tr><td width="168">Files containing juicy info<br></td> <td width="190">Сертификаты, бэкапы, электронные письмы, логи, sql-скрипты и т.д<br></td> <td width="284">Найти инициализационные sql-скрипты:<br><i>filetype:sql and «insert into» -site:github.com </i><br></td> </tr><tr><td width="168">Files containing passwords<br></td> <td width="190">Всё что может содержать пароли - логи, sql-скрипты и т.д<br></td> <td width="284">Логи, упоминающие пароли:<br><i>filetype: </i><i>log </i><i>intext: </i><i>password | </i><i>pass | </i><i>pw </i><br> sql-скрипты содержащие пароли:<br><i>ext: </i><i>sql </i><i>intext: </i><i>username </i><i>intext: </i><i>password </i><br></td> </tr><tr><td width="168">Sensitive Online Shopping Info<br></td> <td width="190">Информацию связанную с онлайн покупками<br></td> <td width="284">Найти пинкоды:<br><i>dcid= </i><i>bn= </i><i>pin </i><i>code= </i><br></td> </tr><tr><td width="168">Network or vulnerability data<br></td> <td width="190">Информацию, не относящуюся непосредственно к веб-ресурсу, но затрагивающую сеть либо прочие не веб-сервисы<br></td> <td width="284">Найти скрипты автоматической настройки прокси, содержащие информацию о внутренней сети:<br><i>inurl:proxy | inurl:wpad ext:pac | ext:dat findproxyforurl </i><br></td> </tr><tr><td width="168">Pages containing login portals<br></td> <td width="190">Страницы, содержащие формы входа<br></td> <td width="284">Веб-страницы saplogon:<br><i>intext:«2016 SAP AG. All rights reserved.» intitle:«Logon» </i><br></td> </tr><tr><td width="168">Various Online Devices<br></td> <td width="190">Принтеры, роутеры, системы мониторинга и т.д<br></td> <td width="284">Найти конфигурационную панель принтера:<br><i>intitle:" </i><i>hp </i><i>laserjet" </i><i>inurl: </i><i>SSI/ </i><i>Auth/ </i><i>set_ </i><i>config_ </i><i>deviceinfo. </i><i>htm </i><br></td> </tr><tr><td width="168">Advisories and Vulnerabilities<br></td> <td width="190">Сайты на уязвимых версиях CMS<br></td> <td width="284">Найти уязвимые плагины, через которые можно загрузить произвольный файл на сервер:<br><i>inurl:fckeditor -intext:«ConfigIsEnabled = False» intext:ConfigIsEnabled </i><br></td> </tr></tbody></table><br> Дорки чаще ориентированы на поиск по всем сайтам сети интернет. Но ни что не мешает ограничить область поиска на каком-либо сайте или сайтах.<br> Каждый запрос к google можно сосредоточить на определённом сайте, добавив к запросу ключевое слово «site:somesite.com». Данное ключевое слово может быть дописано к любому дорку.<p><b>Автоматизация поиска уязвимостей </b><br> Так родилась идея написать простенькую утилиту, автоматизирующую поиск уязвимостей с помощью поисковой системы (google) и опирающуюся на Google Hack Database.</p><p>Утилита представляет из себя скрипт, написанный на nodejs с использованием phantomjs. Если быть точным, то скрипт интерпретируется самим phantomjs.<br> Phantomjs - это полноценный веб-браузер без графического интерфейса, управляемый с помощью js-кода и обладающий удобным API.<br> Утилита получила вполне понятное название – dorks. Запустив её в командной строке (без опций) получаем короткую справку с несколькими примерами использования:<br><br><img src='https://i0.wp.com/habrastorage.org/getpro/habr/post_images/edd/6fb/ccc/edd6fbccc5ec340abe750f3073c1b427.jpg' width="100%" loading=lazy><br> Рисунок 1 - Список основных опций dorks</p><p>Общий синтаксис утилиты: dork «команда» «список опций».<br> Подробное описание всех опций представлено в таблице 4. </p><p>Таблица 4 – Синтаксис dorks<br></p><table border="1"><tbody><tr><td width="214"><b>Команда </b><br></td> <td width="214"><b>Опция </b><br></td> <td width="214"><b>Описание </b><br></td> </tr><tr><td rowspan="4" width="214">ghdb<br></td> <td width="214">-l<br></td> <td width="214">Вывести нумерованный список категорий дорков Google Hack Database<br></td> </tr><tr><td width="214">-c «номер или название категории»<br></td> <td width="214">Загрузить дорки указанной категории по номеру или названию<br></td> </tr><tr><td width="214">-q «фраза»<br></td> <td width="214">Загрузить дорки, найденные по запросу<br></td> </tr><tr><td width="214">-o «файл»<br></td> <td width="214">Сохранить результат в файл (только вместе с опциями -c|-q)<br></td> </tr><tr><td rowspan="8" width="214">google<br></td> <td width="214">-d «дорк»<br></td> <td width="214">Задать произвольный дорк (опция может использоваться много раз, допускается сочетание с опцией -D)<br></td> </tr><tr><td width="214">-D «файл»<br></td> <td width="214">Использовать дорки из файла<br></td> </tr><tr><td width="214">-s «сайт»<br></td> <td width="214">Задать сайт (опция может использоваться много раз, допускается сочетание с опцией -S)<br></td> </tr><tr><td width="214">-S «файл»<br></td> <td width="214">Использовать сайты из файла (перебор дорков будет выполнен по каждому сайту независимо)<br></td> </tr><tr><td width="214">-f «фильтр»<br></td> <td width="214">Задать дополнительные ключевые слова (будет добавлено к каждому дорку)<br></td> </tr><tr><td width="214">-t «количество мс»<br></td> <td width="214">Интервал между запросами к google<br></td> </tr><tr><td width="214">-T «количество мс»<br></td> <td width="214">Таймаут, если встретилась каптча<br></td> </tr><tr><td width="214">-o «файл»<br></td> <td width="214">Сохранить результат в файл (будут сохранены только те дорки, по которым что-то нашлось)<br></td> </tr></tbody></table><br> С помощью команды ghdb можно получить все дорки с exploit-db по произвольному запросу, либо указать целиком всю категорию. Если указать категорию 0 - то будет выгружена вся база (около 4.5 тысяч дорков).<p>Список категорий доступных на данный момент представлен на рисунке 2.<br><br><img src='https://i2.wp.com/habrastorage.org/getpro/habr/post_images/b8f/b11/ffe/b8fb11ffeaced5066fd2fd9e43be67fb.jpg' width="100%" loading=lazy></p><p>Рисунок 2 - Список доступных категорий дорков GHDB</p><p>Командой google будет произведена подстановка каждого дорка в поисковик google и проанализирован результат на предмет совпадений. Дорки по которым что-то нашлось будут сохранены в файл.<br> Утилита поддерживает разные режимы поиска:<br> 1 дорк и 1 сайт;<br> 1 дорк и много сайтов;<br> 1 сайт и много дорков;<br> много сайтов и много дорков;<br> Список дорков и сайтов можно задать как через аргумент, так и через файл.</p><p><b>Демонстрация работы </b><br> Попробуем поискать какие-либо уязвимости на примере поиска сообщений об ошибках. По команде: dorks ghdb –c 7 –o errors.dorks будут загружены все известные дорки категории “Error Messages” как показано на рисунке 3.<br><br><img src='https://i1.wp.com/habrastorage.org/getpro/habr/post_images/28c/386/641/28c386641d1528652f7f8e8b8089097a.jpg' width="100%" loading=lazy><br> Рисунок 3 – Загрузка всех известных дорков категории “Error Messages”</p><p>Дорки загружены и сохранены в файл. Теперь остается «натравить» их на какой-нибудь сайт (см. рисунок 4).<br><br><img src='https://i0.wp.com/habrastorage.org/getpro/habr/post_images/8e0/a8a/3af/8e0a8a3af4f26544da1faa584813dbff.jpg' width="100%" loading=lazy><br> Рисунок 4 – Поиск уязвимостей интересуемого сайта в кэше google</p><p>Спустя какое-то время на исследуемом сайте обнаруживается несколько страниц, содержащих ошибки (см. рисунок 5).</p><p><img src='https://i1.wp.com/habrastorage.org/getpro/habr/post_images/10b/e83/ba3/10be83ba38f172213ba06b3f9ad05a58.jpg' width="100%" loading=lazy><br> Рисунок 5 – Найденные сообщения об ошибках</p><p>Итог, в файле result.txt получаем полный список дорков, приводящих к появлению ошибки.<br> На рисунке 6 представлен результат поиска ошибок сайта.<br><br> Рисунок 6 – Результат поиска ошибок</p><p>В кэше по данному дорку выводится полный бэктрейс, раскрывающих абсолютные пути скриптов, систему управления содержимым сайта и тип базы данных (см. рисунок 7).<br><br><img src='https://i0.wp.com/habrastorage.org/getpro/habr/post_images/0a9/455/588/0a9455588496d6609f5e13d598cb5a48.jpg' width="100%" loading=lazy><br> Рисунок 7 – раскрытие информации об устройстве сайта </p><p>Однако стоит учитывать, что не все дорки из GHDB дают истинный результат. Так же google может не найти точного совпадения и показать похожий результат.</p><p>В таком случае разумнее использовать свой персональный список дорков. Например, всегда стоит поискать файлы с «необычными» расширениями, примеры которых приведены на рисунке 8.<br><br><img src='https://i2.wp.com/habrastorage.org/getpro/habr/post_images/d7f/865/693/d7f865693f7fcf13137598eeed0ecb58.jpg' width="100%" loading=lazy><br> Рисунок 8 – Список расширений файлов, не характерных для обычного веб-ресурса</p><p>В итоге, по команде dorks google –D extensions.txt –f банк, с самого первого запроса google начинает отдавать сайты с «необычными» расширениями файлов (см. рисунок 9). <br><br><img src='https://i1.wp.com/habrastorage.org/getpro/habr/post_images/107/e1f/a2f/107e1fa2f41c4169bcc254cba2f2f4b6.jpg' width="100%" loading=lazy><br> Рисунок 9 – Поиск «нехороших» типов файлов на сайтах банковской тематики</p><p>Стоит иметь ввиду, что google не воспринимает запросы длиннее 32 слов.</p><p>С помощью команды dorks google –d intext:”error|warning|notice|syntax” –f университет <br> можно поискать ошибки интерпретатора PHP на сайтах учебной тематики (см. рисунок 10). <br><br><img src='https://i0.wp.com/habrastorage.org/getpro/habr/post_images/717/74f/e36/71774fe3656bfc058c42d43262fdec4a.jpg' width="100%" loading=lazy><br> Рисунок 10 – Поиск PHP-ошибок времени исполнения</p><p>Иногда пользоваться какой-то одной или двумя категориями дорков не удобно.<br> Например, если известно, что сайт работает на движке wordpress, то нужны дорки именно по wordpress. В таком случае удобно воспользоваться поиском Google Hack Database. Команда dorks ghdb –q wordpress –o wordpress_dorks.txt загрузит все дорки по Wordpress, как показано на рисунке 11:<br><br><img src='https://i1.wp.com/habrastorage.org/getpro/habr/post_images/dcb/ac9/a4e/dcbac9a4eb12f6ec775d9cccc2fdee87.jpg' width="100%" loading=lazy><br> Рисунок 11 – Поиск дорков, относящихся к Wordpress</p><p>Снова вернемся к банкам и командой dorks google –D wordpress_dords.txt –f банк попробуем найти что-нибудь интересное, связанное с wordpress (см. рисунок 12).<br><br><img src='https://i2.wp.com/habrastorage.org/getpro/habr/post_images/042/0c2/c43/0420c2c435931704288b171f725ccc6a.jpg' width="100%" loading=lazy><br> Рисунок 12 – Поиск уязвимостей Wordpress</p><p>Стоит заметить, что поиск на Google Hack Database не воспринимает слова короче 4 символов. Например, если CMS сайта не известна, но известен язык - PHP. В таком случае можно отфильтровать нужное вручную с помощью пайпа и системной утилиты поиска dorks –c all | findstr /I php > php_dorks.txt (см. рисунок 13):<br><br><img src='https://i0.wp.com/habrastorage.org/getpro/habr/post_images/4c1/2f8/6e1/4c12f86e111074293c14d6a939c6ebab.jpg' width="100%" loading=lazy><br> Рисунок 13 – Поиск по всем доркам, где есть упоминание PHP</p><p>Поиск уязвимостей или какой-то чувствительной информации в поисковой системе следует искать только в случае если по данному сайту есть значительный индекс. Например, если у сайта проиндексировано 10-15 страниц, то глупо что-либо искать подобным образом. Проверить размер индекса просто - достаточно ввести в строку поиска google «site:somesite.com». Пример сайта с недостаточным индексом показан на рисунке 14.<br><br><img src='https://i1.wp.com/habrastorage.org/getpro/habr/post_images/78e/1db/b4f/78e1dbb4fc78cd422cec311fc2ca9d33.jpg' width="100%" loading=lazy><br> Рисунок 14 – Проверка размера индекса сайта</p><p>Теперь о неприятном… Периодически google может запросить каптчу - тут ничего не поделать - её придется ввести. Например, у меня, при переборе категории «Error Messages» (90 дорков) каптча выпала только один раз.</p><p>Стоит добавить, что phantomjs поддерживает работу так же и через прокси, как через http, так и через socks интерфейс. Для включения режима прокси надо раскоментировать соответствующую строку в dorks.bat или dorks.sh.</p><p>Инструмент доступен в виде исходного кода</p> <p>WikiHow работает по принципу вики, а это значит, что многие наши статьи написаны несколькими авторами. При создании этой статьи над ее редактированием и улучшением работали авторы-волонтеры.</p> <p>Слово “suggest” является глаголом, то есть словом, описывающим, что делает субъект предложения в то или иное время. В данном случае глагол “suggest” значит “предложить” - идею или мысль, мнение. Корни этого глагола восходят к латыни, к глаголу со значением “побуждать.” Если же вы хотите научиться правильно использовать глагол "suggest", то прочитайте эту статью!</p> <h2>Шаги </h2> <h3> <span>“Suggest” в предложении </span></h3> <ol><li> <p><b>Выберите субъект предложения. </b> Субъектом является лицо, место, предмет или идея, что-то делающая. Начните предложения с того, что выберите, кто будет что-то “предлагать”. </p><ul><li>Как правило, этим занимают люди, так как людям вообще свойственно выражать свои мысли в речи. Давайте придумаем имя человека, который будет нам что-то предлагать. Пусть это будет "Sally".</li> <li>А какие еще существительные можно использовать с suggest? Порой “suggestive”, то есть “наводящими на размышления”, могут быть и вещи. Например, улики: “The evidence suggests the dog ate his homework.”</li> <li>Остальные существительные вряд ли столь же хорошо встанут с этим глаголом. Например, мало кому будет просто увязать фразу “drinking glass” как субъект и “suggest” как предикат. Сами посудите, стаканы редко предлагают мысли (хотя бывает и так).</li> </ul></li> <li> <p><b>Теперь настал черед глагола. </b> Выбрав субъект, пора ставить к нему глагол - "suggest". Это и понятно, ведь глагол описывает субъект - его действия, чувства и прочее. Кроме того, следует проспрягать глагол так, чтобы тот сочетался с существительным. </p><ul><li>Наша "Sally" из пирмера - это “третье лицо, единственное число”. Или, проще говоря, это та волшебная формула, что превращает “suggest” в “suggests,” в результате чего получается “Sally suggests.”</li> <li>Будь вместо Салли местоимение первого лица и единственного числа ("I"), то форма глагола бы не изменилась, оставшись прежней - "suggest". Тогда было бы "I suggest".</li> </ul></li> <li> <p><b>Сейчас пришла очередь прямого дополнения. </b> Это, собственно, так часть предложения, из которой становится понятно, по отношению к кому совершается действие субъектом. Тут может быть и существительное, и местоимение, и фраза, и даже предложение. Разумеется, дополнение должно быть таким, чтобы его можно было “предложить”. </p><ul><li>Чтобы придумать дополнение для примера, давайте зададимся простым вопросом: “What Sally is suggesting?” Если вы сумеете ответить на этот вопрос, то найдете и дополнение, благо что предлагать Салли может что угодно - хоть мороженое на десерт.</li> <li>Обратите внимание, “ice cream” - существительное, которое становится дополнением. Его добавлять надо уже в конец предложения, после слова "suggests“. Получится следующее предложение: "Sally suggests ice cream for dessert.”</li> </ul></li> <li> <p><b>Научитесь правильно строить предложение, когда в качестве дополнения выступает целая фраза. </b> Порой бывает и так, что дополнение получается сложным, особенно когда в качестве существительного выступает глагол или даже предложение. <i> </i></p> <h3> <span>Грамматические правила </span></h3> <ol><li> <p><b>Спрягайте глагол "suggest" по используемому местоимению. </b> Порой на месте существительного оказывается местоимение ("you", "we", "it", "he" или "she"), но и это не повод теряться. </p><ul><li>Запомните: если местоимения “I” или “You” даны в такой форме, форме единственного числе, то глагол не меняет форму и остается “suggest,” то есть “I suggest picking flowers”, “You suggest it to the group.”</li> <li>Имея же дело с местоимениями “We” or “You”, то есть формами множественного числа, вы снова не меняете форму глагола “suggest.” Пример:“We suggest a different color”, “You (all) suggest eating out.”</li> <li>Единственный случай, когда “suggests” таки меняет форму, это при использовании его с местоимениями третьего лица и единственного числа. В нашем примере это - "Sally", хотя и при местоимениях “he” или “she” схема будет аналогичной. Множественное форма местоимения третьего лица, то есть “they”, уже требует использования формы “suggest”, а не “suggests.”</li> </ul></li> <li> <p><b>Имеете дело с прошлым временем? </b> Меняйте "suggest" на "suggested". Глагол, стоящий в форме прошедшего времени, как бы намекает читателю, что действие было в прошлом. Форма прошедшего времени от глагола “suggest” - “suggested.” </p><ul><li>Пример: “I suggested it to Rob yesterday, but he didn"t like the idea.”</li> <li>Не забывайте, что в прошедшем времени “suggested” остается “suggested” при любом субъекте. При любом!</li> </ul></li> <li>Как правило, если вы работаете с предложением в настоящем времени, то использовать вам нужно будет в основном форму “suggest”. Когда же в качестве субъекта будет выступать кто-то или что-то 3-го лица единственного числа, то использовать надо будет форму “suggests.” И помните, что “suggest” требует использования дополнения, поэтому не забудьте указать в конце предложения, что же именно предполагалось.</li> <li>Местоимение “I” является местоимением 1-го лица и единственного числа, так как вы имеете в виду самого себя, единственного и неповторимого. Местоимение “we” также относится к местоимению первого лица, однако является местоимением множественного числа, так как указывает на группу людей. Местоимение “you” является местоимением второго лица, но его форма как в единственном, так и во множественном числе не меняется.</li> </ol></li> </ol> <p>Listening to what your visitors have to say, is always beneficial when planning new features or changes in your website. For a long time we"ve been limited to just setting up a contact form and hoping that quality feedback will follow, which unfortunately is not always the case.</p> <p>Today we are taking things up a notch - we are applying the same social principles that have brought success to sharing sites such as Digg and delicious, and encourage visitors to suggest and vote on features that they want implemented on your website.</p> <h2>The XHTML</h2> <p>Starting with the new HTML5 doctype, we define the opening and closing head and title tags, and include the main stylesheet of the app - <b>styles.css </b>, in the document.</p> <h3>suggestions.php</h3> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Feature Suggest w/ PHP, jQuery & MySQL | Tutorialzine Demo

After this comes the body tag and the #page div, which is the main container element. It holds the heading, the unordered list with all the suggestions (which is generated by PHP, as you will see in a moment), and the submit form.

Lastly we include the jQuery library from Google"s AJAX Library CDN, and our own script.js file, which is discussed in detail in the last section of this tutorial.

The Table Schema

The app uses two MySQL tables to store data. Suggestions and Suggestions_votes. The first table contains the text of the suggestion and data such as rating and the number of votes the item has received. The second table keeps record of the IPs of the voters and prevents more than one vote to be cast in a single day per IP.


To speed up the selection queries, an index is defined on the rating field. This helps when showing the suggestions ordered by popularity.

The suggestion votes table has a primary key consisting of three fields - the suggestion_id , the IP of the voter, and the date of the vote. And because primary keys do not allow for duplicate rows, we can be sure that users can vote only once per day by just checking the value of the affected_rows variable after the insert.


The PHP

Before delving into the generation of the suggestion items and the AJAX interactions, first we have to take a look at the suggestion PHP class. It uses two PHP magic methods (apart from the constructor) to provide rich functionality to our code. When generating the front page, PHP runs a MySQL select query against the database, and creates an object of this class for every table row. The columns of the row are added as properties to the object.

suggestion.class.php

class Suggestion { private $data = array(); public function __construct($arr = array()) { if(!empty($arr)){ // The $arr array is passed only when we manually // create an object of this class in ajax.php $this->data = $arr; } } public function __get($property){ // This is a magic method that is called if we // access a property that does not exist. if(array_key_exists($property,$this->data)){ return $this->data[$property]; } return NULL; } public function __toString() { // This is a magic method which is called when // converting the object to string: return "
  • id."">
    have_voted ? "inactive" : "active")."">
    ".$this->suggestion."
    ".(int)$this->rating."
  • "; } }

    The __toString() method is used to create a string representation of the object. With its help we can build the HTML markup, complete with the suggestion title and number of votes.

    The __get() method is used to route the access to undefined properties of the class to the $data array. This means that if we access $obj->suggestion , and this property is undefined, it is going to be fetched from the $data array, and returned to us as if it existed. This way we can just pass an array to the constructor, instead of setting up all the properties. We are using this when creating an object in ajax.php .

    Now lets proceed with the generation of the unordered list on the front page.

    suggestions.php

    require "connect.php"; require "suggestion.class.php"; // Converting the IP to a number. This is a more effective way // to store it in the database: $ip = sprintf("%u",ip2long($_SERVER["REMOTE_ADDR"])); // The following query uses a left join to select // all the suggestions and in the same time determine // whether the user has voted on them. $result = $mysqli->query(" SELECT s.*, if (v.ip IS NULL,0,1) AS have_voted FROM suggestions AS s LEFT JOIN suggestions_votes AS v ON(s.id = v.suggestion_id AND v.day = CURRENT_DATE AND v.ip = $ip) ORDER BY s.rating DESC, s.id DESC "); $str = ""; if(!$mysqli->error) { // Generating the UL $str = "
      "; // Using MySQLi"s fetch_object method to create a new // object and populate it with the columns of the result query: while($suggestion = $result->fetch_object("Suggestion")){ $str.= $suggestion; // Uses the __toString() magic method. } $str .="
    "; }

    After running the query, we use the fetch_object() method of the $result object. This method creates an object of the given class for every row in the result, and assigns the columns of that row to the object as public properties.

    PHP also manages the AJAX requests sent by jQuery. This is done in ajax.php . To distinguish one AJAX action from another, the script takes a $_GET["action"] parameter, which can have one of two values - "vote " or "submit ".

    ajax.php

    require "connect.php"; require "suggestion.class.php"; // If the request did not come from AJAX, exit: if($_SERVER["HTTP_X_REQUESTED_WITH"] !="XMLHttpRequest"){ exit; } // Converting the IP to a number. This is a more effective way // to store it in the database: $ip = sprintf("%u",ip2long($_SERVER["REMOTE_ADDR"])); if($_GET["action"] == "vote"){ $v = (int)$_GET["vote"]; $id = (int)$_GET["id"]; if($v != -1 && $v != 1){ exit; } // Checking to see whether such a suggest item id exists: if(!$mysqli->query("SELECT 1 FROM suggestions WHERE id = $id")->num_rows){ exit; } // The id, ip and day fields are set as a primary key. // The query will fail if we try to insert a duplicate key, // which means that a visitor can vote only once per day. $mysqli->query(" INSERT INTO suggestions_votes (suggestion_id,ip,day,vote) VALUES ($id, $ip, CURRENT_DATE, $v) "); if($mysqli->affected_rows == 1) { $mysqli->query(" UPDATE suggestions SET ".($v == 1 ? "votes_up = votes_up + 1" : "votes_down = votes_down + 1").", rating = rating + $v WHERE id = $id "); } } else if($_GET["action"] == "submit"){ // Stripping the content $_GET["content"] = htmlspecialchars(strip_tags($_GET["content"])); if(mb_strlen($_GET["content"],"utf-8")<3){ exit; } $mysqli->query("INSERT INTO suggestions SET suggestion = "".$mysqli->real_escape_string($_GET["content"])."""); // Outputting the HTML of the newly created suggestion in a JSON format. // We are using (string) to trigger the magic __toString() method. echo json_encode(array("html" => (string)(new Suggestion(array("id" => $mysqli->insert_id, "suggestion" => $_GET["content"]))))); }

    When jQuery fires the "vote " request, it does not expect any return values, so the script does not output any. In the "submit " action, however, jQuery expects a JSON object to be returned, containing the HTML markup of the suggestion that was just inserted. This is where we create a new Suggestion object for the sole purpose of using its __toString() magic method and converting it with the inbuilt json_encode() function.


    The jQuery

    All of the jQuery code resides in script.js . It listens for click events on the green and red arrows. But as suggestions can be inserted at any point, we are using the live() jQuery method, so we can listen for the event even on elements that are not yet created.

    script.js

    $(document).ready(function(){ var ul = $("ul.suggestions"); // Listening of a click on a UP or DOWN arrow: $("div.vote span").live("click",function(){ var elem = $(this), parent = elem.parent(), li = elem.closest("li"), ratingDiv = li.find(".rating"), id = li.attr("id").replace("s",""), v = 1; // If the user"s already voted: if(parent.hasClass("inactive")){ return false; } parent.removeClass("active").addClass("inactive"); if(elem.hasClass("down")){ v = -1; } // Incrementing the counter on the right: ratingDiv.text(v + +ratingDiv.text()); // Turning all the LI elements into an array // and sorting it on the number of votes: var arr = $.makeArray(ul.find("li")).sort(function(l,r){ return +$(".rating",r).text() - +$(".rating",l).text(); }); // Adding the sorted LIs to the UL ul.html(arr); // Sending an AJAX request $.get("ajax.php",{action:"vote",vote:v,"id":id}); }); $("#suggest").submit(function(){ var form = $(this), textField = $("#suggestionText"); // Preventing double submits: if(form.hasClass("working") || textField.val().length<3){ return false; } form.addClass("working"); $.getJSON("ajax.php",{action:"submit",content:textField.val()},function(msg){ textField.val(""); form.removeClass("working"); if(msg.html){ // Appending the markup of the newly created LI to the page: $(msg.html).hide().appendTo(ul).slideDown(); } }); return false; }); });

    When a click on one of those arrows occurs, jQuery determines whether the "inactive" class is present on the LI element. This class is only assigned to the suggestion, if the user has voted during the last day, and, if present, the script will ignore any click events.

    Как правильно искать с помощью 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?”