Создаем расширения для Google Chrome

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

    Принцип написания расширений

    Некоторые думают, что расширение это нечто заморское, неподвластно простым людям. На самом деле для его написания достаточно просто блокнота, в котором это все писать, и браузера, в котором тестировать написаное.Итак, любое расширение для Chrome состоит из следующих составляющих:
    • Файл манифеста manifest.json, в котором записывается информация о приложении;
    • Иные файлы, которые используются в расширении: это могут быть html-странички, скрипты, картинки и т.д.
    Для запуска готовой работы нужно всего лишь на стрничке расширений в режиме разработчика загрузить ваше творение.Итак, приступим к работе!

    Пишем файл манифеста

    Файл манифеста хранится в корневой папке проекта и, как вы уже поняли, представляет из себя набор данных в формате JSON. Рассмотрим исхоник манифеста, а после этого я опишу каждый пункт подробнее.
    {
    	"name": "Profile Guard",
    	"description": "Google Chrome profile protection by password",
    	"version": "0.1",
    	"permissions": [ "tabs" ],
    	"background_page": "background.html",
    	"icons": {
    		"48":"images/bigicon.jpg"
    	},
    	"browser_action": {
    		"default_title": "Profile Guard",
    		"default_icon": "images/icon.png",
    		"popup": "popup.html"
    	}
    }
    
    А теперь по порядку:
    • name, description и version — собственно имя расширения, его короткое описание и номер версии;
    • permissions — массив с дополнительными правами расширения на выполнение тех или иных действий. Подробнее почитать можно здесь. В даном случае расширение может знать об открытых вкладках и окнах браузера;
    • background_page — главная страника которая запускается при запуске браузера и работает на фоне;
    • icons — набор иконок разных размеров;
    • browser_action — описание кнопочки на панели инструментов. default_title — текст при наведении на кнопку, default_icon — иконка на кнопке, popup — html-страничка, которая откроется в попапе после клика;
    Кроме этих параметров в файле манифеста может быть и много других.

    Фоновая страничка

    Фоновая страничка будет проверять установлен ли пароль и запрашивать его при открытии браузера или создании окна с неким профилем. От протестированных мною расширений моё отличается именно тем, что более менее адекватно работает в режиме мультипрофильности.Пароль от нашего профиля будет храниться в localStorage['profile_password']. localStorage — это массив, в котором расширение может хранить свои какие-то данные, не беспокоясь о том, что какое-то другое расширение доступиться к этим данным. Для каждого расширения и профиля это отдельная переменная, поэтому можно устанавливать разные пароли на разные профили.
    <html>
    <head>
    <title>Profile Guard background page</title>
    </head>
    <body onload="pass();">
    <script type="text/javascript">
    function pass(){
    	chrome.windows.getAll(function(wins){
    		if(wins.length==1){
    			if(localStorage["profile_password"]){
    				var pass = prompt("Please enter password:","");
    				if(pass!=localStorage["profile_password"]){
    					alert("Access denied!");
    					chrome.windows.getCurrent(function (window){chrome.windows.remove(window.id)});
    				}
    			}
    		}
    	});
    }
    chrome.windows.onCreated.addListener(function(window){
    	pass();
    });
    </script>
    </body>
    </html>
    

    Установка и изменение пароля

    Устанавливать и изменять пароль мы сможем на страничке, которая открывается в попапе popup.html. Изначально пароль не установлен и его можно будет установить. Пожже его можно будет изменить или вовсе удалить. Короче говоря всяческие манипуляции с паролем для входа.Исходник файла запихиваю в спойлер, особо пояснять ничего не буду, по-моему и так всё понятно.
    файл popup.html
    <html>
    <head>
    <title>Profile Guard background page</title>
    <style>
    body {
    	font-family: sans-serif;
    	background: #f5f5f5;
    	color: #666E79;
    	width: 250px;
    }
    div.form {
    	background: white;
    	border: solid 1px #e8e8e8;
    	border-radius: 5px;
    }
    h1 {
    	margin:0;
    	padding: 0;
    	font-size: 20px;
    	text-align: center;
    	line-height: 40px;
    }
    h2 {
    	margin: 0 10px;
    	padding: 0;
    	font-size: 14px;
    	line-height: 30px;
    }
    button {
    	border:solid 1px #5E697C;
    	background: URL(images/button.jpg);
    	color: white;
    	font-size: 13px;
    	line-height: 22px;
    	margin: 10px auto 15px;
    	display: block;
    	border-radius: 4px;
    	cursor: pointer;
    	font-weight: bolder;
    	text-shadow: rgba(0,0,0,0.2) -1px -1px 0px;
    	padding: 1 10px;
    }
    button:hover {
    	background: URL(images/hover.jpg);
    }
    .newbutton {
    	margin: 15px 0;
    }
    .field {
    	padding: 5px 15px;
    	text-align:right;
    }
    label {
    	color: #989898;
    	font-weight: bolder;
    	font-size: 12px;
    	float:left;
    	line-height: 25px;
    }
    input {
    	width: 135px;
    	text-align:left;
    }
    </style>
    <script>
    
    	function view(){
    		if(!localStorage["profile_password"]){
    			elnewbutton.style.display = "block";
    			elnewpass.style.display = "none";
    			elchpass.style.display = "none";
    		} else {
    			elnewbutton.style.display = "none";
    			elnewpass.style.display = "none";
    			elchpass.style.display = "block";
    		}
    	}
    
    	function newpass(){
    		if(!localStorage["profile_password"]){
    			elnewpass.style.display = "block";
    			elnewbutton.style.display = "none";
    		}
    	}
    	function newset(){
    		if(!localStorage["profile_password"]){
    			var pass = npass.value;
    			var conf = nconf.value;
    			if(pass){
    				if(pass!=conf){
    					alert("Passwords don't match!");
    					npass.value = "";
    					nconf.value = "";
    				} else {
    					localStorage["profile_password"] = pass;
    					view();
    					alert("Password successfully changed");
    				}
    			}
    		}
    	}
    	function chset(){
    		if(localStorage["profile_password"]){
    			var old = cold.value;
    			var pass = cpass.value;
    			var conf = cconf.value;
    			if(old!=localStorage["profile_password"]){
    				alert("Old password is entered incorrectly");
    				cold.value = "";
    			} else {
    				if(pass){
    					if(pass!=conf){
    						cpass.value = "";
    						cconf.value = "";
    						alert("Passwords do not match!");
    					} else {
    						localStorage["profile_password"] = pass;
    						view();
    						alert("Password successfully changed");
    					}
    				}
    			}
    		}
    	}
    	function claerpass(){
    		if(localStorage["profile_password"]){
    			var old = cold.value;
    			if(!old){
    				alert("Enter old password first!");
    			} else {
    				if(old!=localStorage["profile_password"]){
    					alert("Old password is entered incorrectly");
    					cold.value = "";
    				} else {
    					localStorage["profile_password"] = "";
    					view();
    					alert("Password successfully deleted");
    				}
    			}
    		}
    	}
    </script>
    </head>
    <body onload="view();">
    <div class="form">
    <h1>Profile Guard</h1>
    </div>
    <div class="newbutton" id="newbutton">
    <button id="elnewbutton" onclick="newpass();">Set new password</button>
    </div>
    <div class="newpass form" id="elnewpass" style="display:block;">
    <h2>Set password</h2>
    <div class="field">
    <label>Password</label>
    <input type="password" id="npass" />
    </div>
    <div class="field">
    <label>Confirm</label>
    <input type="password" id="nconf" />
    </div>
    <button id="elnewset" onclick="newset();">Set new password</button>
    </div>
    <div class="newpass form" id="elchpass" style="display:block;">
    <h2>Password change</h2>
    <div class="field">
    <label>Old password</label>
    <input type="password" id="cold" />
    </div>
    <div class="field">
    <label>Password</label>
    <input type="password" id="cpass" />
    </div>
    <div class="field">
    <label>Confirm</label>
    <input type="password" id="cconf" />
    </div>
    <button id="elchset" onclick="chset();">Change password</button>
    <button id="elclear" onclick="claerpass();">Delete password</button>
    </div>
    </body>
    </html>
    

    Завершающий этап

    В завершение нам нужно упаковать наше творение, это можна сделать на странице расширений нажав на кнопку «Упаковка расширений». Указав папку с нашим расширением на выходе мы получаем два файла: .crx — файл с самим расширением, .rem — файл с секретным ключом. В придачу вы можете поделится своим творением с людьми, залив его на Chrome WebStore, но для этого нужно заплатить гуглу взнос в размере $5 (но это лишь первый раз, при регистрации).Прошу не судить строго, ведь это моё первое расширение. Скачать исходник можно по ссылке. Уапкованное расширение не даю, так как в хроме можно устанавливать лишь расширения с Вебстора.
    Пишите свои замечания и замеченные баги.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 18

      +5
      Еще в манифест следует прописать "manifest_version": 2.
      Иначе расширение скоро не будет приниматься в Chrome Web Store.
      А с третьей четверти 2013 года и вовсе не запустится в Хроме.

      Детали здесь.
        +1
        и не только.
        все скрипты из background.html и popup.html следует перенести в отдельные файлы, потому что расширение не запустится с новой версией манифеста.
      • UFO just landed and posted this here
          +7
          В личку.
          0
          >Уапкованное расширение не даю, так как в хроме можно устанавливать лишь расширения с Вебстора
          Я наверное немного отстал от жизни, но раньше он давал устанавливать уже упакованные расширения из веба.
            0
            скорее всего так и есть)
              0
              И это прекрасно.
                0
                хм, у меня с дропбокса поставилось. чяднт?((
                  +1
                  Хм, я недавно разрабатывал свое расширение и использовал для установки прямую ссылку на crx файл (без WebStore). Все нормально устанавливалось и даже обновлялось (тут описано как это работает).
                  0
                  Недавно читал, что все еще чтобы установить можно перетащить на список расширений, но сам не пробовал.
                  • UFO just landed and posted this here
                  +3
                  Жду не дождусь статьи не про «кнопочка с popup-окном», а про правильную организацию сложного расширения с background, injected, settings скриптами, где было бы хорошо показано их взаимодействие между собой. А ещё лучше нечто адаптированное ещё и под Opera. Может такие статьи уже были, а я как то проглядел?
                  +1
                  Прошу не судить строго, ведь это моё первое расширение.


                  Знаете, мои знания о javascript на уровне того, что я просто могу определить, что передо мной он (и то не всегда). А прочтя ваш пост, и изучив код, смог понять что там и к чему. :) И это оказалось интересно!
                    0
                    localStorage это, конечно, хорошо, но не для хранения пароля в плейнТексте.
                    просмотреть все файлы из C:\Users\username\AppData\Local\Google\Chrome\User Data\Default\Local Storage, начинающиеся с chrome-extension на предмет «profile_password» и все =)

                    Придираюсь, конечно, но «параноик не одобряет». А обычный хэш туда засунуть и все ок =)
                      0
                      И от чего это спасёт? Весь профиль всё равно как на ладони. Защита ведь так, от дурака в любом случае. :)
                      0
                      недавно тоже сделал расширение на примере с хабра Моё первое расширение к Chrome

                      Only users with full accounts can post comments. Log in, please.