Skip to content

Instantly share code, notes, and snippets.

@thebrecht
Last active March 1, 2018 09:18
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thebrecht/1d5908fa06a6101aca8f49854412d019 to your computer and use it in GitHub Desktop.
Save thebrecht/1d5908fa06a6101aca8f49854412d019 to your computer and use it in GitHub Desktop.
看到北港武德宮動態產生 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 檔案,就會生出一個字串了。

不想用網頁執行,也可以去掉頭尾的<script>標記,把最後一行 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 的地方即可。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment