iT邦幫忙

7

看到北港武德宮動態產生 email 而來的 email 混淆字串產生器

偶然的機會,看到北港武德宮的網站,相當精美,一改過去對宮廟網站的印象。

照例,要打開原始碼來學習一下,馬上發現一串不太尋常的 JS

<script type="text/javascript">
	//<![CDATA[
	var l=new Array();
	l[0] = '>';
	l[1] = 'a';
	l[2] = '/';
	l[3] = '<';
	l[4] = '|119';
	l[5] = '|116';
	l[6] = '|46';
	l[7] = '|103';
	l[8] = '|114';
	l[9] = '|111';
	l[10] = '|46';
	l[11] = '|101';
	l[12] = '|100';
	l[13] = '|117';
	l[14] = '|119';
	l[15] = '|64';
	l[16] = '|110';
	l[17] = '|105';
	l[18] = '|109';
	l[19] = '|100';
	l[20] = '|97';
	l[21] = '>';
	l[22] = '"';
	l[23] = '|108';
	l[24] = '|105';
	l[25] = '|97';
	l[26] = '|109';
	l[27] = '|95';
	l[28] = '|101';
	l[29] = '|100';
	l[30] = '|117';
	l[31] = '|119';
	l[32] = ' class="';
	l[33] = '"';
	l[34] = '|119';
	l[35] = '|116';
	l[36] = '|46';
	l[37] = '|103';
	l[38] = '|114';
	l[39] = '|111';
	l[40] = '|46';
	l[41] = '|101';
	l[42] = '|100';
	l[43] = '|117';
	l[44] = '|119';
	l[45] = '|64';
	l[46] = '|110';
	l[47] = '|105';
	l[48] = '|109';
	l[49] = '|100';
	l[50] = '|97';
	l[51] = ':';
	l[52] = 'o';
	l[53] = 't';
	l[54] = 'l';
	l[55] = 'i';
	l[56] = 'a';
	l[57] = 'm';
	l[58] = '"';
	l[59] = '=';
	l[60] = 'f';
	l[61] = 'e';
	l[62] = 'r';
	l[63] = 'h';
	l[64] = ' ';
	l[65] = 'a';
	l[66] = '<';

	for (var i = l.length-1; i >= 0; i=i-1) {
		if (l[i].substring(0, 1) === '|') document.write("&#"+unescape(l[i].substring(1))+";");
		else document.write(unescape(l[i]));
	}
	//]]>
</script>

稍微研究之後,發現這是用來動態產生網站上的 email。之所以這麼費事,因為有些爬蟲會去抓網站上的 email,作為垃圾郵件的發送名單,某些網站安全掃描軟體,也會把網站上有 email 列作缺點,因此這種作法是好的。

它的做法,是:

  • 將 email 的字串轉成 ascii 的10進位號碼
  • 將號碼轉成帶有|前綴的字串
  • 將包含有mailto 的 email 的 html 片段,轉成陣列後再反轉,增加混淆程度

看起來是簡單易作的方法,只是看到那一長串的陣列宣告,就有點不舒服,總覺得在維護上應該會有點麻煩。

如果是我自己來做,要怎麼改呢?在最低限的修改前提下,我會希望那段陣列是有辦法自動產生,產生之後再放到網站上去,這樣如果需要修改 email,或是相同的方法要套到別的網站上去,都會很快。

但如果是有產生器來產出,於其產生陣列,不如產出字串,在複製/貼上時,會更簡單一點。

於是寫了下面的產生器(https://jsfiddle.net/brecht/fsq6e7uo/2/):

<script>
  //填入網站所使用的 email
  var email = "service@example.com";
  //填入該 email 所使用的 HTML 片段  
  var template = '<a href="mailto:{{email}}" class="email_style">{{email}}</a>';

  var EmailMixer = {
    mix: function(email, template) {
      this.template = template.replace(/{{email}}/g, this.toCharNo(email));
      var arr = this.revert(this.template);
      return arr.join(";");
    },
    toCharNo: function(str) {
      var code = "";
      for (var i = 0; i < str.length; i++) {
        code += ("|" + str[i].charCodeAt() + ";");
      }
      return code;
    },
    revert: function(str) {
      var reverseArr = [];
      var encodeStr = "";
      var isEnCodeStr = false;

      while (str) {
        theChar = str[0];

        if (isEnCodeStr === true) {
          if (theChar !== ";") {
            encodeStr += theChar;
          } else {
            isEnCodeStr = false;
            reverseArr.unshift(encodeStr);
            encodeStr = "";
          }
        } else {
          if (theChar === "|") {
            isEnCodeStr = true;
            encodeStr += theChar;
          } else {
            reverseArr.unshift(theChar);
          }
        }
        str = str.substr(1);
      }
      return reverseArr;
    }
  }

  document.write(EmailMixer.mix(email, template));

</script>

修改上面 email 的資訊,以及依自己的網站需要,修改 html 片段的組成,存成一個 html 檔案,就會生出一個字串了。

不想用網頁執行,也可以去掉頭尾的標記,把最後一行 document.write(EmailMixer.mix(email, template)); 改成 console.log(EmailMixer.mix(email, template)) 之後,存成js檔,就可以用 node xxx.js 執行,在命令列底下取得字串了。

有了字串之後,就要想辦法還原,動態產生的方法如下(https://jsfiddle.net/brecht/kq38vw03/3/):

<script type="text/javascript">
  //由 email 字串產生器產出的字串,因字串符中有使用雙引號,因此產生的字串要用單引號來表示
  var str = '>;a;/;<;|109;|111;|99;|46;|101;|108;|112;|109;|97;|120;|101;|64;|101;|99;|105;|118;|114;|101;|115;>;";e;l;y;t;s;_;l;i;a;m;e;";=;s;s;a;l;c; ;";|109;|111;|99;|46;|101;|108;|112;|109;|97;|120;|101;|64;|101;|99;|105;|118;|114;|101;|115;:;o;t;l;i;a;m;";=;f;e;r;h; ;a;<';

  renderMail(str);

  function renderMail(str) {
    var arr = str.split(";");
    arr.reverse();
    for (var i = 0; i < arr.length; i++) {
      document.write(unescape(arr[i].replace(/\|(.*)/, "&#$1;")));
    }
  }

</script>

只要把 str 的字串換成上一步生成的字串,然後把上面這一段 script 片段,放到要產生 email 的地方即可。

ps. 這篇文章放到gist沒多久,同事做了驗證,這種方式防得了一般爬網頁原始碼的爬蟲,但是防不了一些會將網頁 DOM 生成後再爬的,例如 Google 搜尋引擎


2 則留言

2
海綿寶寶
iT邦超人 1 級 ‧ 2018-03-14 09:38:22

感覺拿來混淆 email 有點小題大作大材小用
易學易用
應該可以應用在其他地方
推三下
/images/emoticon/emoticon12.gif/images/emoticon/emoticon12.gif/images/emoticon/emoticon12.gif

其實就是看到後就想「如果是我要怎麼寫」就寫下去了,實用性什麼的其實沒有考慮

/images/emoticon/emoticon37.gif

其實看到後我的想法是
「如果是我,我寫不出來」
/images/emoticon/emoticon05.gif

froce iT邦高手 1 級‧ 2018-04-26 16:19:14 檢舉

感想同樓上。哈

1
weiclin
iT邦高手 4 級 ‧ 2018-03-15 18:16:56

大概四五年前也做過類似的東西, 不過是用 php 去產生 js code
貼到 gist 獻醜一下 https://gist.github.com/weichenlin/117171171d3c7947218ef3dca6bf3f66

產生出來的 js code 大概長這樣:

<script type="text/javascript">var jeucpivrdp = [ "\xac","\xf1","\xb0","\xf8","\xe2","\xf5","\xf6","\xad","\xb2","\xfd","\xf1","\xf9","\xfc","\xe4","\xff","\xaa","\xfd","\xf1","\xf9","\xfc","\xd0","\xf5","\xe8","\xf1","\xfd","\xe0","\xfc","\xf5","\xbe","\xf3","\xff","\xfd","\xb2","\xae","\xfd","\xf1","\xf9","\xfc","\xd0","\xf5","\xe8","\xf1","\xfd","\xe0","\xfc","\xf5","\xbe","\xf3","\xff","\xfd","\xac","\xbf","\xf1","\xae" ];for(var i=0; i<jeucpivrdp.length; i++){document.write(String.fromCharCode(jeucpivrdp[i].charCodeAt(0) ^ 144));}</script>
看更多先前的回應...收起先前的回應...

/images/emoticon/emoticon12.gif

froce iT邦高手 1 級‧ 2018-04-25 10:21:57 檢舉

我現在大概會用ajax去取網址,然後用click事件去呼叫吧。
就點下連結、取得mailto網址,然後window.open網址。

weiclin iT邦高手 4 級‧ 2018-04-25 14:37:22 檢舉

froce 只是要防止信箱被大量爬走的話, 綁個 click 事件就差不多了吧

froce iT邦高手 1 級‧ 2018-04-26 16:18:17 檢舉

要就做徹底一點,反正聯絡我這功能應該不太耗流量。哈

我要留言

立即登入留言