2

I'm creating an application with Node.js and Mongo DB, rendering the views with Swig. I have a database of business names, addresses and geo location data that is being plotted onto a Google map with pins.

I'd like to stop users from easily copying the raw JSON data using view source, Firebug, Chrome Dev tools etc.

I'm not after bank grade security, just want to make it hard enough for most users to give up.

I have two routes of delivering the JSON package to the browser:

1) Using Swig, passing the JSON package directly to the view. Problem is that a simple view source will show the JSON.

2) Requesting the data with an AJAX call. In this scenario the data is easily accessible with Chrome Dev tools.

What are my options?

Flexo
  • 87,323
  • 22
  • 191
  • 272
ChrisRich
  • 8,300
  • 11
  • 48
  • 67
  • Is it for privacy reasons ? – tomaoq Nov 07 '14 at 13:09
  • 1
    No matter how you send it to the client, 99% of the time it can be de-obfuscated/decoded. Is there any way for you to render it server-side and serve the parsed data? – Ben Fortune Nov 07 '14 at 13:20
  • @lechariotdor: To me sounds more like copycat prevention, against possible competitors. – Stefan Steiger Nov 07 '14 at 13:22
  • I don't want my competitors to copy my database. The information is not private, I just spend a great deal of time building the database and I'm not intereated in someone just grabbing the raw data – ChrisRich Nov 07 '14 at 13:38
  • 1
    @ChristianSch: You better build-in bot protection. Just limit the number of request per time per IP, or user, if you require login. I don't think anybody will sit there copying for hours manually via the Chrome dev-tools. – Stefan Steiger Nov 07 '14 at 15:14
  • @ChristianSch You're approaching this in a way that will lead you nowhere. You cannot prevent someone from copying your data, and in fact it isn't even hard to do. As Quandary says, add in rate limits and keep an eye on your logs to watch for someone scraping you. If you have any numeric data in your data set, consider fudging the numbers to watermark them in a way that you could identify. At the end of the day your competitors probably won't scrape you since the risk to them is very high. If the data is public, they will get it themselves. – Brad Nov 08 '14 at 18:24
  • @Brad Thanks for your comment. I haven't explained my self properly so I will try again. Some folks on this thread are saying that I can never prevent anyone from copying my data. This is not what I'm trying to achieve! If you read my question I say "I'm not after bank grade security, just want to make it hard enough for most users to give up." – ChrisRich Nov 11 '14 at 08:40
  • I ended up using this: https://www.npmjs.org/package/node-cryptojs-aes – ChrisRich Nov 11 '14 at 08:41
  • @ChristianSch I understand that, but what I'm telling you is that no matter what you do, 5 minutes worth of work is all it takes to break it. When working on this client side, there is no level of security that will make "most" users give up. – Brad Nov 11 '14 at 13:45

4 Answers4

2

Base-64 encode the string.
Then you can just base64-decode it in JavaScript.
That should make it sufficiently unreadable, no real security though - of course.
Plus it's fast.

You need to take care with UTF-8 characters (e.g. German äöüÄÖÜ, or French èéàâôû)

e.g. like this in JavaScript:

var str = "äöüÄÖÜçéèñ";
var b64 = window.btoa(unescape(encodeURIComponent(str)))
console.log(b64);

var str2 = decodeURIComponent(escape(window.atob(b64)));
console.log(str2);

example:

 var imgsrc = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(markup)));
 var img = new Image(1, 1); // width, height values are optional params 
 img.src = imgsrc;

More secure variant:
Return encrypted base64 encoded JSON, plus the decryption algorithm, base64 encode them server-side, bit-shift it a few bits, return via ajax, then de-bitshift the string on the webpage, pass it to eval, which will give you the decrypt function, then decrypt the encrypted base64 string, then base-64 decode that string.

But that takes only a few seconds more on the chrome debug console to decrypt, i did decrypt such a thing once, I think on codecanyon to get to a "Tabs" script for free; (don't bother for the tabs, they're bloatware, better invest the time to do it yourself) ;)

I think you find that nowadays here http://www.slidetabs.com/, but I don't know if the "encryption" method is still in there.

Additionally, you can also escape the string in JavaScript, that then looks like this:

var _0xe91d=["\x28\x35\x28\x24\x29\x7B\x24\x2E\x32\x77\x2E

...


x5F\x63\x6F\x6E\x74\x5F\x64\x75\x72\x7C\x76\x5F\x74\x61\x62\x73\x5F\x61\x6C\x69\x67\x6E\x7C\x76\x5F\x74\x61\x62\x73\x5F\x64\x75\x72\x7C\x76\x5F\x73\x63\x72\x6F\x6C\x6C\x7C\x63\x6F\x6E\x74\x5F\x61\x6E\x69\x6D\x7C\x63\x6F\x6E\x74\x5F\x66\x78\x7C\x74\x61\x62\x5F\x66\x78\x7C\x72\x65\x70\x6C\x61\x63\x65\x7C\x62\x61\x6C\x69\x67\x6E\x7C\x61\x6C\x69\x67\x6E\x5F\x7C\x75\x6E\x6D\x6F\x75\x73\x65\x77\x68\x65\x65\x6C\x7C\x73\x77\x69\x74\x63\x68\x7C\x64\x65\x66\x61\x75\x6C\x74\x7C\x6A\x51\x75\x65\x72\x79","","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","\x72\x65\x70\x6C\x61\x63\x65","\x5C\x77\x2B","\x5C\x62","\x67"]
;eval(function (_0x173cx1,_0x173cx2,_0x173cx3,_0x173cx4,_0x173cx5,_0x173cx6){_0x173cx5=function (_0x173cx3){return (_0x173cx3<_0x173cx2?_0xe91d[4]:_0x173cx5(parseInt(_0x173cx3/_0x173cx2)))+((_0x173cx3=_0x173cx3%_0x173cx2)>35?String[_0xe91d[5]](_0x173cx3+29):_0x173cx3.toString(36));} ;if(!_0xe91d[4][_0xe91d[6]](/^/,String)){while(_0x173cx3--){_0x173cx6[_0x173cx5(_0x173cx3)]=_0x173cx4[_0x173cx3]||_0x173cx5(_0x173cx3);} ;_0x173cx4=[function (_0x173cx5){return _0x173cx6[_0x173cx5];} ];_0x173cx5=function (){return _0xe91d[7];} ;_0x173cx3=1;} ;while(_0x173cx3--){if(_0x173cx4[_0x173cx3]){_0x173cx1=_0x173cx1[_0xe91d[6]]( new RegExp(_0xe91d[8]+_0x173cx5(_0x173cx3)+_0xe91d[8],_0xe91d[9]),_0x173cx4[_0x173cx3]);} ;} ;return _0x173cx1;} (_0xe91d[0],62,284,_0xe91d[3][_0xe91d[2]](_0xe91d[1]),0,{}));

You can then bring the string back like:

"\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65".toString()

But for a moderate coder (like me), to figure out the system and decrypt the data of all this combined will take only appx. 15-30 minutes, (experimential find, from the codecanyon-try).

It's questionable if such a thing is worth the expense of your time, because it takes somebody like me less time to reverse-engineer your "encryption" than it takes you to "code" it.

Note that if you put a string like "\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65" into your appllication, you may trigger false alarms on certain virus scanners (McAffee, TrendMicro, Norton, etc., the usual suspects).

You can also partition the JSON string into an array of JSON-string chunks, makes it harder to decrypt it (maybe rotating the sequence in the array according to a certain system might help as well).

You can also break the string into an array of char:

var x = ['a', 'b', 'c'];

You can then bring it back like

console.log(x.join(""));

You can also reverse the string, and put that into an array (amCharts does that).
Then you bring it back with

x.reverse().join("");

The last one might be tricky for utf-8, as you need to correctly reverse strings like "Les misérables" (see also this and this)

Community
  • 1
  • 1
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
0

Since the data will go on your client's computer, there is no other way to fully protect that data than... not sending it.

So, you could render some views on the server side and send them to the client but it may not be doable in your case.

Other way, would be to send data, but to make it difficult for an unauthorized user to access to it.

If your application is using an user database, you could generate a fixed key per user and encrypt sensible data before sending it to the client, and then the client would decrypt it with the same key calculated on the client side.

In addition, you can fine tune which data you want to send or not send to each user.

If you want to protect the data betweeen the moment the client's receive it and the moment it goes in your map, I'm afraid it is not possible as the map component you're using is probably waiting for standard JSON data.

Anyway, it makes no sense to protect your data as it will be displayed on your map.

tomaoq
  • 3,028
  • 2
  • 18
  • 25
0

Everything that is passed to client is not safe, you can try obfuscating data, but in the end the place where you put in the map will be accessible by just adding a line of console.log()

Another option, I'm just speculating as I'm not really sure how google maps work, but you might firstly send only the geolocation to the map, this way you will have pins on the map, the only after clicking on the ping you could fetch other data from api (name, address). Google maps should support something like onclick.

Artūras
  • 473
  • 4
  • 13
0

Annoy a potential scraper/hacker with all the tricks everyone talks about on this thread and others. But as it's been said many times, once the data is sent to the client, it's basically unprotected.

Perhaps your thinking should involve these things too:

-How to identify when someone is scraping (e.g. monitoring IPs, thresholds, user activity, etc) and do something about it or at least identify the culprit.

-Put copyrights and other identification on any thing you can, to help other users see and understand that it's your data, not the scrapers'. Look at what artists have been doing already, for a long time.

-Lay hidden traps in your data to help identify it as unique; that only you know about and the scraper wouldn't bother to look for or too lazy to check. If the scraper uses your data publicly too, then maybe this can be used in a legal case, or at least you could publicly shame the offender.

Kalnode
  • 9,386
  • 3
  • 34
  • 62