Hi there! You might know that I am hosting a (heavily customized) gitlab instance at gitlab.bixilon.de. I created this instance 6 years ago (Jan 24 2019) when I was still a kid. It works fine, I had to switch the server twice since then (performance reasons).
I am using it everyday and I am happy so far, moderation overhad was really low…so far.
Spam stats
In summer last year the whole hell started farly slowly:
(Unconfirmed users; red marks: new spam waves; green: me deleting users)
It started pretty slow, but it the spammers are getting faster and faster. I tried enabling honeypots or even google recaptcha and it did not slow down anything. E-Mail verify did not slow them down, 2FA also did not.
(Google recaptcha console, 100% fraud requests)
Reason
First I did not get why they want to create thousands of accounts of my - kind of low traffic - used website. It suddenly hit me weeks later when I looked at some profiles:
(Profile page of @oladegraves718; account deleted).
I think some SEO company creates those accounts for their clients and they will get more and more attention from google. Not supporting that.
Initial “prevention”
I wrote a ruby script to detect such accounts and automatically delete them, it looks like this (sorry, not a ruby developer at all):
1#!/usr/bin/gitlab-rails runner
2$admin = User.find_by(id: 1)
3
4
5$deactivate = ::Users::DeactivateService.new($admin, skip_authorization: true)
6$delete = DeleteUserWorker.new()
7
8$trusted_domains = [
9 "bixilon.de",
10 "gmail.com", ...
11]
12
13$count = 0
14
15def gravatar_exists?(email)
16 email_hash = Digest::MD5.hexdigest(email.strip.downcase)
17 gravatar_url = "https://www.gravatar.com/avatar/#{email_hash}?d=404"
18 uri = URI(gravatar_url)
19 response = Net::HTTP.get_response(uri)
20 response.code == '200'
21end
22
23
24def delete_user(user)
25 print "Deleting " + user.name + " (id=" + user.id.to_s + ", email=" + user.email + ")\n"
26
27 $deactivate.execute(user)
28 $delete.perform($admin.id, user.id)
29 $count = $count + 1
30end
31
32print "Iterating over all users and deleting some of them :)\n\n"
33
34MIN_USER_ID = 726
35
36User.where('id > ' + MIN_USER_ID.to_s).each do |user|
37 next if user.id < 300 # additional check, to not fuck up legit users.
38 next if user.id <= MIN_USER_ID
39 trusted = false
40 $trusted_domains.each do |suffix|
41 if user.email.end_with?(suffix)
42 trusted = true
43 break
44 end
45 end
46 next if trusted
47
48 print "Checking: " + user.name + "\n"
49
50 next if user.sign_in_count > 10
51 next if user.two_factor_enabled?
52 next if user.gpg_keys.length() > 0
53 next if user.all_ssh_keys.length() > 0
54 next if user.verified_emails.length() >= 3 # 2+ verified email
55 next if user.projects.length() > 0
56 next if user.issues.length() > 0
57 next if user.merge_requests.length() > 0
58
59 next if gravatar_exists?(user.email) # check last, this takes a get request
60
61
62 if user.bio.match?("\d* (year|years|yrs|yr)[ -]old [A-Z].*")
63 delete_user(user)
64 next
65 end
66
67 if user.name.match?(/^[A-Z][a-z]*\s[A-Z][a-z]*$/) # "Pre Postname"
68 if user.verified_emails.length() <= 1 # no verified emails
69 delete_user(user)
70 next
71 end
72 next if user.sign_in_count > 1
73 # next if user.bio.to_s.strip.empty?
74 # next if user.location.to_s.strip.empty?
75 delete_user(user)
76 next
77 end
78end
79
80print "Finished: Deleted " + $count.to_s + " users.\n"
It works pretty well and it deleted almost all of them, some accounts had to be manually removed. Several days ago those users started to create repositories and it pretty much fucked up the script. I am no longer willing to moderate on my private gitlab.
The ultimate prevention
Just don’t allow any new accounts. From now on (23. Jan 2025) the login page will show you this banner:
I am sorry, but I am forced to it.