Best way to prevent a user clicking 'like' multiple times - javascript

I'm implementing a like feature for a site I'm working on. A user doesn't have to be logged in and they can either like or dislike a particular page. At the end of it all I'd like to be able to populate a list of articles or stories with the most likes.
I have a very simple method that currently uses an onclick javascript function to update a database via ajax and a php function.
This is working ok. What I'd like to do is prevent a user from spamming the button.
At first I thought of maybe getting the IP address, storing that in the database and then running a check. Is there a better way?

Technically there isn't a bomb proof way to do so. You could get pretty close by allowing one vote per ip-useragent combination. You'd have to implement this on the server side.
PHP Example
$concienceKey = md5($_SERVER['REMOTE_ADDR'] . $_SERVER['USER_AGENT']);
$query = "SELECT COUNT(*) FROM clickConcience WHERE key = `" . $concienceKey . "`";
//run your query
//.....and get the $count;
//
//already voted!
if($count > 0){
echo 'already voted';
return false;
}
//remember entry
$insert = "INSERT INTO clickConcience (key, datetime) VALUES (`" . $concienceKey . "`, NOW())";
//run your query
//.....and insert
//
return true;

straight forward answer, you won't be able to do it.
If I really want to SPAM your "like" button, I will find a way to do so, especially if you're not forcing me to be signed in (I used to write pretty good bots and were pretty efficient spamming big link submission sites).
Javascript will only stop mediocre spammers or sock puppet account holders. As a spammer I can circumvent your Javascript pretty easily, either by programming a time-based robot to like your post, or by sending requests directly to your server (I will not even load your site).
What you need to do, if you really want to prevent spammers from spamming this feature efficiently (efficiency is the keyword here because spammers can still spam your feature, but their likes won't count) is to log every IP that likes a post along with its geographical information (it's not always 100% accurate, but it's a good start) and then run a process in the background that checks for suspicious origins and penalize such likes (either by assigning them less value, or just subtracting them from the total count).
For example if your main audience is people living in the United States, but one post gets a bunch of likes from Mexico, Salvador, India, Australia, Russia, then it's more than likely that there's a spammer behind a proxy or a network similar to TOR and he/she can change his/her IP address at his/her will.
After a few hundred thousand records, you'll have a good base to start blacklisting IP addresses. I usually use R programming language to get statistical information about my databases.
But then again, a good spammer could use a list of IP addresses of compromised computers coming from your audience's country or geographical location, and use those IPs to abuse the feature. Those bots are harder to spot, but you can analyze previous posts and come up with useful metrics as "Likes/comment ratio".
If one post has a huge number of likes, but low number of comments, then it's very probable that someone spammed it, but then again I can program my bot to like AND post a comment so the numbers look natural.
I'm not sure what kind of project you're working on, but if it's something similar to link submission, do not rank (whatever your users are liking) by the number of likes.
The number of likes should only be a factor, you can take a look at how HackerNews or Reddit rank the posts (those projects are open source), but it's a combination between multiple factors.

Just hide the button after it has been clicked for the first time.
It does even makes more sense, when using an AJAX handler for sending the click...

Use cookies. Lets say you have a button where the user can like article 123456789
<button id="like" articleID="123456789">Like</button>
script :
function setLike(articleID) {
document.cookie=articleID+'=y';
}
function hasLiked(articleID) {
var cookies=document.cookie.split(';');
for (var i=0;i<cookies.length;i++) {
var cookie=cookies[i].split('=');
if (cookie[0]==articleID) return true;
}
return false;
}
var button=document.getElementById('like');
button.onclick=function() {
var articleID=this.getAttribute('articleID');
if (!hasLiked(articleID)) {
//register the like in your system
//...
//
setLike(articleID);
} else {
alert('You cant like or dislike an article twice');
}
}
Of course the user can delete all his or hers cookies - but a user can also like the same page / article from 100 different computers. The above prevents the most common scenario : People repetetively clicking like or dislike a lot of times from the same computer in a short distant of time.

Related

Can you prevent users editing the chrome.storage.local values in a Chrome extension? What is the best way to store persistent values for an extension?

So I'm working on a Chrome extension for someone else. I don't want to give away specific details about the project, so for I'll use an equivalent example: let's assume it's an extension to run on an image/forum board. Imagine I have variables such as userPoints, isBanned etc. The later being fairly self-explanatory, while the former corresponding to points the user acquires as they perform certain actions, hence unlocking additional features etc
Let's imagine I have code like:
if(accountType !== "banned"){
if(userPoints > 10000) accountType = "gold";
else if(userPoints > 5000) accountType = "silver";
else if(userPoints > 2500) accountType = "bronze";
else if(userPoints <= 0) accountType = "banned";
else accountType = "standard";
}else{
alert("Sorry, you're banned");
stopExtension();
}
Obviously though, it becomes trivial for someone with the knowledge to just browse to the extensions background page and paste chrome.storage.local.set({'userPoints': 99999999}) in the console, hence giving them full access to all the site. And, with the Internet, someone can of course share this 'hack' on Twitter/YouTube/forums or whatever, then suddenly, since all they'd need to do is copy and paste a simple one-liner, you can have 1000s of people, even with no programming experience, all using a compromised version of your extension.
And I realise I could use a database on an external site, but realistically, it would be possible that I would be wanting to get/update these variables such as userPoints 200+ times per hour, if the user was browsing the extentions target site the entire time. So the main issues I have with using an external db are:
efficiency: realistically, I don't want every user to be querying the
db 200+ times per hour
ease-of-getting-started: I want the user to just download the
extension and go. I certainly don't want them to have to sign up. I
realise I could create a non-expiring cookie with for the user's ID
which would be used to access their data in the db, but I don't want
to do that, since users can e.g. clear all cookies etc
by default, I want all features to be disabled (i.e. effectively
being considered like a 'banned' user) - if, for some reason, the
connection with the db on my site fails, then the user wouldn't be
able to use the extension, which I wouldn't want (and just speaking
from experience of my parents being with Internet providers whose
connection could drop 10 times per hour, for some people, failed
connections could be a real issue) - in contrast, accessing data from
the local storage will have like a 99.999% success rate I'd assume,
so, for non-critical extensions like what I'm creating, that's more
than good enough
Still, at least from what I've found searching, I've not found any Chrome storage method that doesn't also allow the user to edit the values too. I would have thought there would be a storage method (or at least option with chrome.storage.local.set(...) to specify that the value could only be accessed from within the extension's context pages, but I've not found that option, at least.
Currently I'm thinking of encrypting the value to increment by, then obfuscating the code using a tool like obfuscator.io. With that, I can make a simple, like 30 character js file such as this
userPoints = userPoints + 1000;
become about 80,000...still, among all the junk, if you have the patience to scroll through the nonsense, it's still possible to find what you're looking for:
...[loads of code](_0x241f5c);}}}});_0x5eacdc(),***u=u+parseInt(decrypt('\u2300\u6340'))***;function _0x34ff36(_0x17398d)[loads more code]...
[note that, since it's an extension and the js files will be stored on the user's pc, things like file size/loading times of getting the js files from a server are irrelevant]
Hence meaning a user wouldn't be able to do something like chrome.storage.local.set({'userPoints': 99999999}), they'd instead have to set it to the encrypted version of a number - say, something like chrome.storage.local.set({'userPoints': "✀ເ찀삌ሀ"}) - this is better, but obviously, by no means secure.
So anyway, back to the original question: is there a way to store persistent values for a Chrome extension without the user being able to edit them?
Thanks

Password protection for a page with a simple function - what are the downsides?

I am doing work on an e-commerce platform, and I was asked to come up with a solution so that a certain group of customers could enter a password protected page on the site. The platform doesn't allow for this, as in the functionality is not available, so according to customer support, it's something you would have to create a custom template for and build from scratch. It doesn't need to be fancy, or hacker proof, just secure enough. So instead of doing that, I dropped the script below into the body of the page.
My first version: I use a prompt to ask for an input (password). If you click "prevent this page from creating additional dialouges", it creates sort of an infinite reload loop for that tab (not ideal, but problem?). Are there other serious problems? Easy hacks for your average person?
$("body").hide();
var passwordCheckFunction = function() {
var testPassword = window.prompt("YOU SHALL NOT PASS");
if (testPassword === "thisPredefinedPassword") {
$("body").show();
} else {
location.reload();
}
};
passwordCheckFunction();
Any advice would be much appreciated, and thank you for your time.
Create your secret page as a category.
Customize it to your heart's desire by choosing a custom template
file for it.
Finally, restrict it to only the authorized customer group
by removing it from view from guests and every group except the
authorized one.
Using this method, the customer only has to sign into his/her own customer account. BigCommerce will prevent access to the page by reading the assigned customer group of the customer.
I realize this isn't your desired method, but you might consider instead just making your page inactive in the admin area of your BC store, then instead of a password provide the direct url for users that are able to see that page.
I'm not sure about the implications for google indexing with an inactive page, but I would assume that they are set not to index it, and if not you could set it in robots.txt

VBScript Locate user in Directory; Return results (Found or Not Found)

I want to begin by apologizing for not including any code. VBScript is completely out of my realm of knowledge and I have been asked to use it for a small program here at work.
The goal: I have been asked to search part of the active directory (I am told that it is an OU in our active directory) and compare the user that is attempting to login to the computer with the OU. If that person DOES exist in the specified OU, I need to do something. If the person DOES NOT exist, I need to do something else.
The specifics: I work at a hospital. The point of this is to keep nurses from using the laptops designated for the physicians on the floors. When a nurse badges in to said computer, if the person logging in exists in the "Physicians" OU (which is my OU), then continue with the login, else log the person out (which would be to key an F4 in Imprivata). This being said, we use a program called Imprivata that logs the user in when they swipe their badge. Imprivata can also perform keystrokes like keying in the user's password, clicking buttons, etc. The Imprivata program allows for code to be written in the background in VBScript, WSH, and I believe JavaScript and then executed when a condition exists (a person logging on a computer). Unfortunately, I know NOTHING about any of these languages.
I have been searching the web and trying different things for a week now with no luck. In fact, I have even got close to anything working so any help would be greatly appreciated and again I apologize for the lack of code.
The easiest thing would be getting the current users OU, which can be done with
Set objSysInfo = CreateObject("ADSystemInfo")
strUserName = objSysInfo.UserName
Now you can check if your physicians OU is part of that. That is of course not the exactly same thing as checking whether that user is in the physicians OU because there could be cases where the same username exists in both OUs. However if the same user in both OUs is something that happens you have problems either way.
After you got the users OU just do a simple string compare. I always use a little helper function for that
Function contains(sourceStr, checkStr)
contains=InStr(1, sourceStr, checkStr, vbTextCompare) > 0
End Function
That does nothing more than a case insensitive check if a string is substring of another. so
contains(strUserName, "OU=Finance,OU=North America,OU=Pacific Coast,dc=fabrikam,dc=com")
with the DN of your OU might already be enough.
If you are new to vbscript a good point to start would be the old "Hey Scripting Guy" blogs where everything is explained in a lot of detail. This one for example is about checking a user's OU.
Windows has settings for this.
Allow log on locally
This logon right determines which users can interactively log on to this computer. Logons initiated by pressing CTRL+ALT+DEL sequence on the attached keyboard requires the user to have this logon right. Additionally this logon right may be required by some service or administrative applications that can log on users. If you define this policy for a user or group, you must also give the Administrators group this right.
Default on workstations and servers:
Administrators
Backup Operators
Users.
Default on domain controllers:
Account Operators
Administrators
Backup Operators
Print Operators
Server Operators.

How to block the link from malicious bot visitors?

I'm producing an event registration website. When someone click on a link:
Reserve id=10 event
The system is doing a "lock" on this event for ten minutes for this visitor. In that case no one else can reserve this event in next ten minutes. If the payment is done in that time, everything is OK, else the event is unlocked again. I hope the idea is clear.
PROBLEM: When bot (google bot, malicious bot, or angry customer script :P) visits this page, he see this link. Then he enters the page. Then the lock is done...
Also if someone visit recursive: /reserve/1, /reserve/2, /reserve/3, ... He can lock all the events.
I thought about creating a random md5 string for each event. In that case, every event has (next to id) unique code, for example: 1987fjskdfh938hfsdvpowefjosidjf8243
Next, I can translate libraries, to work like this:
<a href="/reserve/1987fjskdfh938hfsdvpowefjosidjf8243" rel="nofollow">
Reserve
</a>
In that case I can prevent the "bruteforce" lock. But the link is still visible for bots.
Then I thought about entering the captcha. And that is the solution. But captchas are... not so great in case of usability and user experience.
I saw few websites with reservation engine working like this. Are they protected? Maybe there is a simple ajax / javascript solution to prevent the bots from reading this as a pure text? I thought about:
Reserve
<script type="text/javascript">
$('#reserve').click(function(e) {
e.preventDefault();
var address = ...;
// something not so obvious to follow?
// for example: md5(ajaxget(some_php_file.php?salt=1029301))
window.location('/reserve/' + address);
});
</script>
But I'm not sure what shall I do there to prevent bots form calculating it. I mean stupid bots will not be able even to follow javascript or jquery stuff, but sometimes, someone wants to destroy something, and if the source is obvious, it can be broken in few lines of code. And whole database of events will be locked down with no reservation option for noone.
CRFS + AJAX POST + EVENT TOKEN generated on each load.
Summary: don't rely on GET requests especially through a elements.
And better if you add some event block rate limits (by IP for instance).
EDIT: (this is a basic sketch)
replace all the href="..." with data-reservation-id=ID
delegate click on the parent element for a[data-reservation-id]
in the callback, simply make a POST ajax call to the API
in the API's endpoint check rate limits using IP for instance
if OK, block the event and return OK, if not return error.
IP-Specific maximum simultaneous reservations
Summary: Depend on the fact that many simple bots operate from one host. Limit the number of simultaneous reservations for a host.
Basic scetch:
Store the requesting IP alongside the reservation
On reservation request count the IP's which have a non-completed reservation.
SELECT Count(ip) FROM reservations WHERE ip=:request_ip AND status=open;
If the number is above a certain threshold, block the reservation.
(this is mostly an expansion of point 4 given in avetist's excellent answer)

Requiring a Password / Passphrase to view contents of category with Wordpress

I'm trying to set something up where when a user clicks on a category link within the navbar, it asks them for a password / passphrase to view that categories posts. I've done a lot of digging, and I can't seem to find a solution. Can anyone point me in the right direction? I figured I would be able to solve this with a little bit of script work, but I can't even find something to get me started. Help?
I don't think this is something that would require a plugin. In this case I would write something along the lines of this.
This script is assuming the user is making a http POST request to this page, probably by submitting a form somewhere on your website.
If any of this looks foreign to you feel free to ask and I'll be happy to clarify :)
<?php
//Create new database connection
$idForPassword = 5;
$mysqli = new mysqli("localhost", "DBusername", "DBpassword", "DBName");
//Create new prepared statement
$stmt = $mysqli->prepare("SELECT password FROM sometable WHERE id = ?");
$stmt->bind_param("i", $idForPassword);
// execute query
$stmt->execute();
// bind result variables
$stmt->bind_result($result);
$stmt->fetch();
// Hash the password so we aren't storing a password as plain text in the database
// ideally you also add a salt to your password but since this is just an example
// I'll leave that part out
$password = md5($_POST['password']);
if($password == $result)
{
//allow user access
}
else
{
//deny user access
}
edit: A little more info on Salting and Hasing passwords. I'd recommend reading it whenever you get the chance since its a fairly easy way to implement basic level of security if you plan on storing passwords in a database.
The security issue with simple hashing (md5 et al) isn't really the
speed, so much as the fact that it's idempotent; two different people
with the same password will have the same hash, and so if one person's
hash is brute-forced, the other one will as well. This facilitates
rainbow attacks. Simply slowing the hash down isn't a very useful
tactic for improving security. It doesn't matter how slow and
cumbersome your hash algorithm is - as soon as someone has a weak
password that's in a dictionary, EVERYONE with that weak password is
vulnerable.
Also, hash algorithms such as md5 are for the purpose of generating a
digest and checking if two things are probably the same as each other;
they are not intended to be impossible to generate a collision for.
Even if an underlying password itself requires a lot of brute forcing
to determine, that doesn't mean it will be impossible to find some
other bit pattern that generates the same hash in a trivial amount of
time.
As such: please, please, PLEASE only use salted hashes for password
storage. There is no reason to implement your own salted hash
mechanism, either, as crypt() already does an excellent job of this.

Categories

Resources