{"id":13527534,"url":"https://github.com/dandrews/nefarious-linkedin","last_synced_at":"2025-04-10T11:58:32.575Z","repository":{"id":138631619,"uuid":"97492500","full_name":"dandrews/nefarious-linkedin","owner":"dandrews","description":":shipit: A look at how LinkedIn spies on its users.","archived":false,"fork":false,"pushed_at":"2019-01-08T20:24:31.000Z","size":514,"stargazers_count":827,"open_issues_count":3,"forks_count":40,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-01T09:36:10.865Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://chrome.google.com/webstore/detail/nefarious-linkedin/mpkhbmjfapljfhjopagghpfgbmghjpah","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dandrews.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-07-17T15:36:05.000Z","updated_at":"2025-03-27T23:38:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"f2a42c6a-6a53-409b-9032-0eb9bb0a204d","html_url":"https://github.com/dandrews/nefarious-linkedin","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dandrews%2Fnefarious-linkedin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dandrews%2Fnefarious-linkedin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dandrews%2Fnefarious-linkedin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dandrews%2Fnefarious-linkedin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dandrews","download_url":"https://codeload.github.com/dandrews/nefarious-linkedin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248215204,"owners_count":21066622,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:50.531Z","updated_at":"2025-04-10T11:58:32.536Z","avatar_url":"https://github.com/dandrews.png","language":"JavaScript","readme":"# Nefarious LinkedIn \n\nLinkedIn violates their own users' privacy in an effort to detect the usage of \nbrowser extensions. At the time of writing this, LinkedIn is scanning visitors \nfor 38 different browser extensions. \n\nI will dive into how LinkedIn detects extensions and what extension developers \ncan do to prevent detection. \n\nI have built an open source browser extension that lists which extensions \nLinkedIn is currently scanning your browser for.\n\n![Demo](images/demo.gif?raw=true \"Nefarious LinkedIn Extension Demo\")\n\n### Installation \n\nYou can click [here](https://chrome.google.com/webstore/detail/nefarious-linkedin/mpkhbmjfapljfhjopagghpfgbmghjpah)\nto install the extension via the Google web store, or you can download any \n[release](https://github.com/prophittcorey/nefarious-linkedin/releases) from this \nrepository and load it via Chrome's `chrome://extensions` page.\n\nIf you are unsure how to load an unpacked extension, you can see how [here](https://developer.chrome.com/extensions/getstarted#unpacked).\n\n### How does LinkedIn detect extensions?\n\nThere are two common ways to detect the usage of a browser extension. The first \nmethod is by scanning public resources available in the extension, the other is\nby analyzing the target web page for abnormal behavior. \n\nLinkedIn is currently using _**both**_ of these methods. \n\n###### Public Resources\n\nPublic resource detection is by far the simplest method to detect the usage of \nan extension. It is also the easiest to detect as an end user. In fact, it is \nwhat turned me on to LinkedIn's nefarious activities to begin with.\n\nIf you browser LinkedIn with your developer console open you may occasionally \nnotice a series of network request errors. If you look at the requests you will\nnotice they are not external requests, rather they are requests to local files.\nThese local files begin with `chrome-extension://` which indicates the web page\nis making requests to files located in your browser itself. \n\nI have attached a little animated gif showing what these requests look like on \nLinkedIn. \n\n![LinkedIn Spying](./images/spying.gif \"LinkedIn Spying\")\n\nIn this case, the presence of an error message indicates the resource was not \navailable and therefore the extension they were looking for is not installed on\nyour browser. If the request succeeded LinkedIn would then know you do have the \nextension installed. \n\n###### Behavioral Patterns\n\nThe second method to detect the usage of an extension is by examining the web \npage itself and identifying any side effects of an extension. LinkedIn is \nactively using CSS selectors to locate identifying elements on the page. \n\nFor instance, they may look for an ID or class used by the extension, etc. \n\nLinkedIn uses your browser's local storage to store a JSON file that contains \na list of extensions to detect. Each extension contains one or more public \nresources to look for as well as one or more CSS selectors to use to try an\ndetect the extension. \n\nLinkedIn tries to obfuscate the JSON document but doesn't do a very good job. \n\nThe file itself is located in the local storage of your browser under the key \n`C_C_M`. If you look at the contents of the key, you'll see gibberish. The \ncontents are base64 encoded. \n\nOnce decoded the contents are further obfuscated. It's clearly a JSON document \nbut the characters are all converted to their unicode byte patterns which makes \nreading it as a human difficult. If you parse the JSON you can retrieve a human \nfriendly object to examine. \n\nTry it yourself, visit a LinkedIn page, open the inspector and run:\n\n```javascript \nJSON.parse(window.atob(localStorage.getItem('C_C_M')))\n```\n\nYou can see the output here: \n\n![Local Storage Contents](./images/localstorage.png?raw=true \"Local Storage Contents\")\n\nNotice the `selector` attribute in the JSON? It's clear they are searching for \nan element with the id `#daxtra-info-div`. \n\nIf you examine the path list in the JSON you can see which public resources they\nare looking for. In this case: `ombdgbngokkngdbcahjbeimfcfimdole/magnet/ChromePlugin/inject/daxtra_info.html`\n\nClearly, they are trying to identify users with the \n[Daxtra](https://chrome.google.com/webstore/detail/daxtra-magnet/ombdgbngokkngdbcahjbeimfcfimdole?hl=en) \nextension installed.\n\nFunnily enough they try to obfuscate the name of each extension. If you look at\nthe name in the screenshot you can see, `wOmysO` which maps to `Daxtra`. It is \nfairly clear the encoded name has gone through a substitution cipher.\n\nThere is enough information in the file to reverse engineer the substitution \ncipher (which is what I have done in the extension above). \n\nThe other information in the JSON is not so interesting. There's mainly book\nkeeping information such as how often to scan your browser, the current version\nof the JSON document, which URLs to scan for each extension, etc.\n\nI have kept an eye on the document over the last few weeks and I have noticed \nwithin the last two weeks the number of extensions LinkedIn is looking for has \ngone up from 28 to 38.\n\n### Tips for Extension Developers\n\n1. Don't use web accessible resources.\n\nOut of the 38 extensions LinkedIn is currently looking for, 28 are using web \naccessible resources. This is by far the easiest thing to avoid which greatly\nhelps your users' privacy. \n\nFurthermore, there's no good reason to use web accessible resources in an \nextension! You can always find a solution to your problem that does not require\nthem. \n\n2. Limit content script activity to simple reads. \n\nThis is harder to do because it reduces the usefullness of many extensions, but\nif you want to isolate your extension from the web page you must do this. \n\n3. Don't display your extension using a content script, try a browser action.\n\nConsider using a browser action instead of modifying the page via content \nscripts. Browser actions run in an isolated environment and makes detecting them\nmuch harder if not impossible. \n    \n### Credit \n\nThe squirrel icon used by the extension was found on \n[Flaticon](http://www.flaticon.com/) and is credited to Freepik. \n","funding_links":[],"categories":["JavaScript","Tools"],"sub_categories":["VPN"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdandrews%2Fnefarious-linkedin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdandrews%2Fnefarious-linkedin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdandrews%2Fnefarious-linkedin/lists"}