How to make MediaWiki account registrations only allow unique emails? - mediawiki

How can I set it so that MediaWiki will not allow a single email address to create multiple accounts? A spambot just created 5 accounts with a single email.
I've looked for configuration settings or extensions, but haven't been able to find one.
A related issue to this is the annoying creation of spam accounts with usernames similar to JameeiohpbrxvlsHeadlon.
Spam prevention measurements work quite well, as no actual spam articles are created, but only spam accounts. I have TorBlock, ConfirmEdit and SimpleAntiSpam installed to prevent spam accounts from being created, but this appears to fail.

This is not likely to be a very effective anti-spam strategy. Most spambots smart enough to register accounts with valid e-mail addresses are likely also smart enough to try a new address if the registration fails.
Personally, I've found the most effective anti-spam solution for small wikis to be ConfirmEdit with QuestyCaptcha. Just configure ConfirmEdit to require a CAPTCHA for account creation, so that you won't get spam accounts. The questions don't need to be hard to answer — indeed, they can be absolutely trivial, as long as they're unique to your site.
That said, you could do what you suggest by writing an AbortNewAccount hook to look up the user's e-mail address in the database and fail if you find a match, something like this (untested!):
$wgHooks['AbortNewAccount'][] = 'disallowDuplicateEmails';
function disallowDuplicateEmails( $user, &$message ) {
$email = $user->getEmail();
if ( !$email ) return true; // allow empty e-mail
$dbr = wfGetDB( DB_SLAVE );
$name = $dbr->selectField( 'user', 'user_name',
array( 'user_email' => $email ),
__METHOD__ );
if ( $name !== false) {
$message = wfMessage( 'signup-dup-email', $email, $name )->text();
return false;
}
return true; // no match
}
You'll also need to create the system message page MediaWiki:signup-dup-email, with content something like this:
The e-mail address <tt>$1</tt> is already used by [[User:$2|$2]].
Note that there are at least two potential issues with such a check:
It can allow people to "fish" for e-mail addresses of your users (something that MediaWiki normally treats as private information) by trying to register a new account with an address they suspect might belong to an existing user. This could be somewhat mitigated by omitting the username from the error message, but that would still leak the information that someone is using the address.
The code above doesn't check whether the address has been confirmed or not (and checking that would rather defeat its purpose, unless you also require all users to confirm their e-mail address), and so a malicious person could prevent someone else from registering with their e-mail address by creating a dummy account with the same address.
However, getting around the check would actually be rather easy, since a) it checks for an exact match, so e.g. just changing the capitalization of the host name would be enough to make the check pass, and b) it doesn't prevent users from changing their e-mail address to whatever they want after registering, anyway. Both of these holes could be blocked, but it would require more effort.

Related

MediaWiki - Require confirmed emails before allowing read?

I'm trying to setup a MediaWiki for university students. Using the EmailDomainCheck, I prevent anyone except those with a university based email from creating accounts. Using $wgEmailConfirmToEdit, I can require that an email is confirmed before the user can edit files. However, as it is, a user can use a fake email from the correct domain to create an account. With the account they can view all pages (even though they cannot edit them). I do not want to grant them read access unless the email has been confirmed. Is this possible? Note, I want all confirmed emails of the correct domain to be automatically accepted. It should not require manual account creation acceptance.
You could try the following, as outlined in the Documentation
# Disable for everyone.
$wgGroupPermissions['*']['read'] = false;
# Disable for users, too: by default 'user' is allowed to read, even if '*' is not.
$wgGroupPermissions['user']['read'] = false;
# Make it so users with confirmed email addresses are in the group.
$wgAutopromote['emailconfirmed'] = APCOND_EMAILCONFIRMED;
# Hide group from user list.
$wgImplicitGroups[] = 'emailconfirmed';
# Finally, set it to true for the desired group.
$wgGroupPermissions['emailconfirmed']['read'] = true;
As Jenny Shoars has mentioned, you may wish to whitelist some pages such as:
$wgWhitelistRead = array("Main_Page", "Special:CreateAccount", "Special:ConfirmEmail");
So that non registered users can still create accounts and the like.
In theory,
$wgGroupPermissions['*']['read'] = false;
$wgGroupPermissions['emailconfirmed']['read'] = true;
should work. In practice, MediaWiki almost always used with an "everyone can read" or "you can read iff you are logged in" setup and others are not very well tested, so if that wiki had some highly sensitive private information I wouldn't do this, but I imagine for a university website that's not the case.
Alternatively, it should not be too hard to integrate an email confirmation step into account creation, but you'd have to write the code for that. EmailAuth (which does a similar check during login) might give you an idea of how that would look.

How to personalize a link to user's IP address

I am wondering how big of a job it is to be able to do the following:
I want to have certain web pages on a site only accessible by specific computers. Of course, I can always provide usernames and passwords, but I want it restricted even further.
To illustrate:
I give User-A access to page-A.
I give User-A a username and password to access page-A.
User-A tries to share the username and password with a friend (User-B).
User-B tries to access page-A with User-A's credentials, but it does
not work because User-B needs to be on User-A's computer to do so.
I know that this is possible to accomplish, since financial institutions employ this kind of security, but can I implement it on my my own? If so, how?
--Edit--
Yani mentioned that filtering by IP would not be wise, since user IP addresses often change. My question now turns to the use of sessions or localstorage/webstorage to control access to certain webpages.
What local data would you need to pull? I would imagine that a database would be required to store computer data for future reference by the system.
IP addresses are, for most home users, temporary only. The ISP will change them every few weeks/months, unless the user has a static IP (which usually costs more).
In addition, a user can take his laptop to a coffee shop and immediately log in from a different IP.
Therefore, IP address filtering is a good idea only if you want to geo-block users (country, state, etc.), but, to my honest opinion, not a good idea for authenticating a user over a long period of time.
You may just need to implement a cookie/session/localstorage with Javascript or server side technology such as PHP, which will be browser & computer specific.
Cookies + IP Address
Combining cookies/localStorage technology ALONG WITH IP address can actually be a good idea for having a 2nd level of securiy (i.e when IP changes, having an alert such as 'it seems as you are loging in from a different IP address, please answer security question...').
Also - when a user will login from a different browser but same computer (and same IP) you can have an extra verification question.
You can even implement a IP address history, such as gmail's.
However, if you had to choose only 1 of the methods - I'd definitely go with cookies/localStorage.
Examples of how to set and get local data in Javascript.
With localStorage (HTML5):
localStorage.setItem('userAuthenticated', '1');
localStorage.getItem('userAuthenticated');
With cookies:
function setCookie(cname,cvalue,exdays)
{
var d = new Date();
d.setTime(d.getTime()+(exdays*24*60*60*1000));
var expires = "expires="+d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
function getCookie(cname)
{
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++)
{
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
Hope this helps!
You probably will need to use .htaccess to do this. Try this and see if it works for what you are trying to do.
.htaccess: how to restrict access to a single file by IP?

PHP: view unsuccessful emails

I'm trying to figure out how to display a list of unsuccessful emails sent and a way to test it out.
I can display a list of the emails that were sent, but I'm unsure on how to retrieve the list of emails that were unsuccessfully sent out.
Here is what I'm using to retrieve it from the mysqldb:
//get the email address list
$query = "SELECT email FROM users
WHERE id IN (SELECT participant_id FROM roster AS ur WHERE ur.roster_id=".$roster['roster_id'].")";
$result = mysql_query($query);
$emailstring2 = "";
$email2 = $result;
while ($row = mysql_fetch_object($email2)){
$emailstring2 .= $row->email. "\n ";
}
In the message section, I retrieve it via:
$message .="Successful emails: \n".$emailstring2." \r\n";
How would I achieve this?
One keyword will get you the ones not sent: NOT
WHERE id NOT IN
You can't do this from PHP in a simple manner like this.
Think of it as a letter going through the postal service. All you can do is give the letter to the postman and hope that it reaches its destination. The postman will not come back and tell you if delivery was successful, or if the letter was actually read by the recipient. PHP's mail() function (and its derivatives) return TRUE to indicate that the message was accepted for delivery attempts, not that it was successfully delivered. (Delivery may not happen for hours or days.)
As a result, the best you can do is approximate delivery notification. There are a few ways you can do this:
Use a tracking pixel in the email that gets pinged when the user
opens the message. However, given that most email clients nowadays
default to "do not show images," I think this technique is rather
unreliable.
Send each message with a bounce address unique to the recipient. If
the message can't be delivered, it will return to the custom address
-- and that return message can be used to indicate that the original recipient's email address is no good. This is probably the most accurate method but is not simple to configure.
Use return receipts. Like tracking pixels, I think most email clients
default to never send these, so this is likely unreliable as well.
Use delivery status notification. This will require using a server
that supports it and sending to a server that supports it.
Send your mails through a service that will do this sort of tracking for you. (E.g. MailChimp or Constant Contact)

How to setup an SMS alert to website owner when a customer fills in a form?

Been doing some research on how to setup an SMS alert straight to the website owner's (our customer) phone when a website visitor fills in a form on his website. Obviously we're happy to sign up to a provider and pay a few cents for each message - not expecting this to be a freebie service.
A lot of the google results you get for this deal with bulk texting out to the many recipients which isn't what we're looking at here.
So what's the simplest small-scale way to achieve this via HTML and simple scripting?
This daniweb forum post offers clear and simple instructions using Coldfusion and that's fine but as it's 7 years old we wonder if things have moved on and are there better suggestions?
We'd be happy to script it very simply through ASP.NET or PHP or something else, but hoping to keep it lightweight, since this is a very simple website belonging to a small business.
I am working on a similar thing for my company.
We use a GSM modem such as: ZTE MF668A
It is a USB adapter which you can insert a SIM card into.
We purchased an SMS only plan, which is like $15 a month and you can send unlimited SMS. However the set up might be a bit more complicated than just using a bulk SMS service.
You can set up Diafaan SMS Server (http://www.diafaan.com/) on the computer with the GSM Modem to accept emails for a particular domain. (e.g. ours is #sms.com). Our exchange server forwards email to the Diafan Server. Diafaan will take the email and convert it to an SMS and send it using the GSM modem.
In you php code, you can use PHPMailer (http://phpmailer.worxware.com/) to construct an email and set the mail to address to be #sms.com.
You use it something like this:
ini_set("sendmail_from","user#company.com");
ini_set("SMTP","smtp.company.com");
require("PHPMailer/class.phpmailer.php");
$mail = new PHPMailer();
$mail->IsSMTP(); // set mailer to use SMTP
$mail->Host = "smtp.company.com"; // specify main and backup server
$mail->SMTPAuth = false; // turn on SMTP authentication
//$mail->Username = "name"; // SMTP username
//$mail->Password = "password"; // SMTP password
$mail->From = "user#company.com";
$mail->FromName = $salesPerson;
$mail->AddAddress("user2#company.com", "Pricing");
$mail->AddReplyTo("user#company.com", "User2");
$mail->AddBcc($salesPersonEmail, $salesPerson);
$mail->WordWrap = 50; // set word wrap to 50 characters
$mail->IsHTML(true); // set email format to HTML
$mail->Subject = $subject;
$mail->Body = $body;
if(!$mail->Send())
{
echo "Message could not be sent. <p>";
echo "Mailer Error: " . $mail->ErrorInfo;
exit;
}
There are also companies that offer SMS services such as PCSMS where you get 100 SMS per month for a fee. Then you just construct an email and send it to #pcsms.com.au. This is probably the easiest way to do it and is the way we used to do it, but the way we do it now is much cheaper.
Disclaimer, I do developer evangelism part time at Nexmo.
While you can setup a GSM modem to send SMS messages, it's likely easier (and depending on your usage, cheaper) to use a SMS API. Here are a few providers - pricing is in cents per message (depends on the API and destination, but hovers around a cent or two). You may be required to provision a virtual number - pricing there is generally dollars/month (again, depends on API and destination, hovers around a dollar or two).
Nexmo
Twilio
Tropo
If you're sending internationally, I think your best would be Nexmo. Or if you see any need to accept incoming messages in the future, Nexmo's pricing model will help you there.
All three APIs are straight forward REST/HTTP APIs - as long as whatever language you're using server side can make a web request, you should be able to send an SMS.
Eventually we used Esendex. Good support from them, decent code samples and plenty of mileage in the free trial (which in fact they were happy to extend when we asked).

Best way of retrieving lost password

What is the best method to reset a user password when password is hashed:
Reset a password to a random string and send that string to their registered mail?
Create a unique hash link for resetting password which is valid for an hour and sending that link to mail?
Any other method?
Create a unique hash link for resetting password which is valid for an hour and sending that link to mail
This is the method that I prefer. It allows you only to reset the password if and only if the user visits the link. This way, if someone is maliciously trying to reset passwords, the user can simply delete the email and be unaffected (not have to enter a new password).
Also, you should give the reset link some sort of longer expiration date (like 12 to 24 hours).
2 is the best method. Never ever mail a password in plain form. Even better, don't keep it in your system this way. Always have it hashed and salted.
Follow-up to comments: Emailing hashes instead of plain passwords may also be insecure but you are pursuing a different goal through this. Many people use the same password for all sites, from Facebook up to online-banking. A particular hash may get compromized, but not the password itself, which is the point.
#2 is preferable to #1 if only because sending a password in plain text via email exposes it unnecessarily.
Other options are:
use password hint questions
use OpenID and punt the entire problem to the user's OpenID provider.
It depends on the sensitivity of the information you are protecting...
There is a fine balance between security and usability, and you need to decide where it is, and what assets you are protecting.
What I would normally do (assuming to financial data is involved) is option 2, minus the 1 hour limit.
I found a really interesting method on some websites: they are sending you a new password via SMS. This is awesome because the e-mail can be hacked but the phone... I don't think can be easily hacked.