Til kamp mod phishing på lnk.dk: del 2

Mit første naive forsøg på at forhindre kriminelles brug af lnk.dk til at pege på diverse phishing-sider lykkedes, mildt sagt, ikke.

Nu har jeg taget skrappere midler i brug.

I min models.py i min Django-applikation, tilføjer jeg et felt til at gemme ip-adresse på den, der har oprettet et link, og et felt til at markere, om et link er usikkert:

class Link(models.Model):
    # Short link is only lower case
    def save(self, *args, **kwargs):
        self.shortlink = self.shortlink.lower()
        return super(Link, self).save(*args, **kwargs)

    destination = models.URLField('Destinationslink', max_length=65535, validators=[validate_destination])
    shortlink = models.SlugField('Kort link', max_length=100, unique=True, allow_unicode=False, validators=[validate_shortlink])
    LINK_TYPE_CHOICES = (
        ('automatic', 'Automatisk'),
        ('manual', 'Manuelt'),
    )   
    submitter_ip = models.GenericIPAddressField(null=True)
    unsafe_link = models.BooleanField(default=False)
    type = models.CharField('Type', max_length=10, choices=LINK_TYPE_CHOICES)
    date = models.DateTimeField(default=timezone.now, editable=False)

Derudover tilføjer jeg tabeller til at kunne blokere for ip-adresser og domæner, der ikke skal kunne oprette brugbare links:

class Ban(models.Model):
    banned_ip = models.GenericIPAddressField(unique=True)

class BanDomain(models.Model):
    banned_domain = models.CharField(max_length=255, unique=True)

Med det på plads tilpasser jeg min logik til oprettelse af links i views.py sådan, at:

  • kun brugere med ip-adresser kan oprette links,
  • links bliver tjekket med Google Safe Browsing (efter råd på Twitter)
  • links til domæner og fra ip-adresser, der er blokeret, bliver automatisk markeret som usikre

Her er funktionen til at tjekke links op mod Google Safe Browsing:

# Google safe browsing API check function
def is_url_google_safe_browsing_safe(url):
    params = {
        'key': ''
    }
    json  = {
        "client": {
            "clientId":      "lnk.dk",
            "clientVersion": "1.0"
        },
        "threatInfo": {
            "threatTypes":      ["MALWARE", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE"],
            "platformTypes":    ["ANY_PLATFORM"],
            "threatEntryTypes": ["URL"],
            "threatEntries": [
                {"url": url}
          ]
        }
     }
    api_url = 'https://safebrowsing.googleapis.com/v4/threatMatches:find'
    try:
        response_json = requests.post(api_url, params=params, json=json).json()
        if response_json:
            return True
        else:
            return False
    # If something unexpected is returned from Google, link creation is allowed
    except:
        return False

Og her er min nye logik til at tjekke modtagne links. Læg mærke til, at brugere uden ip-adresse automatisk bliver rickrolled

if form.is_valid():
	client_ip, is_routable = get_client_ip(request)
	# Hiding your IP seems illegit, so user is rickrolled
	if client_ip is None:
		return HttpResponseRedirect('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
	else:
		destination = form.cleaned_data['destination'] # Submitted destination
		shortlink = form.cleaned_data['shortlink'] # Submitted slug
		
		# Google Safe Browsing check
		unsafe_url = is_url_google_safe_browsing_safe(destination)
		
		# Ban domain check
		domain_info = extract(destination)
		domain = domain_info.domain + '.' + domain_info.suffix                    
		domain_ban = BanDomain.objects.filter(banned_domain=domain)
		if len(domain_ban) > 0:
			banned_domain = True
		else:
			banned_domain = False
		
		# Ban ip check
		ip_ban = Ban.objects.filter(banned_ip=client_ip)
		if len(ip_ban) > 0:
			banned_ip = True
		else:
			banned_ip = False

Til sidst har jeg forsøgt at narre phisherne ved at links til usikre sider virker, for den, der selv har oprettet linket. Alle andre bliver rickrollet, hvis de klikker på et usikkert link:

# Short link redirect to destination URL
def redirect(request, shortlink):
    # Query the database for short link, if there is a hit, redirect to destination URL
    # In case of uppercase characters in user input shortlink, the link is made lowercase
    # Also, check if ip is banned
    try:
        client_ip, is_routable = get_client_ip(request)
        link = Link.objects.get(shortlink=shortlink.lower())
        # Legit users are rickrolled when an unsafe link is visited from an IP that is not banned
        if link.unsafe_link == True and not client_ip == link.submitter_ip:
            return HttpResponseRedirect('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
        # For safe links AND for unsafe links visited from banned IPs, user is redirected to destination
        else:
            # If there's a referer and it's the same as the destination link, show a 404 to avoid an endless loop
            if 'HTTP_REFERER' in request.META and link.destination == request.META['HTTP_REFERER']:
                return render(request, 'links/404.html', status=404)
            return HttpResponseRedirect(link.destination)
    # In case of an error, show 404 page
    except:
        return render(request, 'links/404.html', status=404)