sbrl / Pepperminty-Wiki

A wiki in a box
https://peppermint.mooncarrot.space/
Mozilla Public License 2.0
177 stars 20 forks source link

Obfuscate Administrator Email #205

Closed SeanFromIT closed 3 years ago

SeanFromIT commented 3 years ago

The e-mail for the Administrator in the footer appears to be a plain mailto link, ready for bot harvesting. I would suggest implementing something like hide-email() to obfuscate.

sbrl commented 3 years ago

Hey there! Oh, great point. I've already got a hide_email method that tried to obfuscate email addresses by randomly inserting html entities, but indeed a more intelligent approach is needed. I'm hesitant to go for an approach which requires Javascript though - since at the moment JS isn't required for Pepperminty Wiki anywhere (although it will improve your experience).

To that end, I'm tempted to say that a better solution would be to proxy administrator contact requests. As I've blogged ahout, I have implemented an extremely effective "comment key system" as I call it that catches out 99.99% of comment spammers, and it doesn't require Javascript.

That's rather drastic to avoid JS for hiding email addresses though, so I suspect it's probably better to use a JS-based solution like the one you mention.

Still, it would be nice to have some sort of graceful fallback if the user doesn't have JS enabled. Specifically, the browser I want to support is elinks, a command-line text mode web browser that doesn't have JS support by default (I use it as my backup web browser, and have it installed on most servers that I manage just in case. It's actually come in handy more than once). The other scenario that bothers me is accessibility - I'm concerned that using a JS-based solution may hurt accessibility.

What do you think?

SeanFromIT commented 3 years ago

Trouble with fallback will be most bots probably come via text browsers. Understand (and appreciate) the hesitancy to add a JS dependency; maybe this could be an admin option/switch? Alternatively, the fallback could be something that's not a mailto link. Plaintext with stylized at instead of @ for example, or even something configurable.

sbrl commented 3 years ago

Hrm, good idea - let's do that.

sbrl commented 3 years ago

Update: I've implemented something inspired by the page you've linked to. My rationale is that a) innerHTML should be avoided if at all possible, and b) if everyone uses different techniques then it's harder for spammers to crack :P

For reference, here's said implementation:

/**
 * Hides an email address from bots. Returns a fragment of HTML that contains the mangled email address.
 * @package core
 * @param   string  $str            The original email address
 * @param   string  $display_text   The display text for the resulting HTML - if null then the original email address is used.
 * @return  string  The mangled email address.
 */
function hide_email(string $email, string $display_text = null) : string
{
    $enc = json_encode([ $email, $display_text ]);
    $len = strlen($enc);
    $pool = []; for($i = 0; $i < $len; $i++) $pool[] = $i;
    $a = []; $b = [];
    for($i = 0; $i < $len; $i++) {
        $n = random_int(0, $len - $i - 1);
        $j = array_splice($pool, $n, 1)[0]; $b[] = $j;
        // echo("chose ".$enc[$j].", index $j, n $n\n");
        $a[] = $enc[$j];
    }
    $a = base64_encode(implode("|", $a));
    $b = base64_encode(implode("|", $b));
    $span_id = "he-".crypto_id(16);
    return "<a href='#protected-with-javascript' id='$span_id'>[protected with javascript]</span><script>(() => {let c=\"$a|$b\".split('|').map(atob).map(s=>s.split('|'));let d=[],e=document.getElementById('$span_id');c[1].map((n,i)=>d[parseInt(n)]=c[0][i]);d=JSON.parse(d.join(''));e.textContent=d[1]==null?d[0]:d[1];e.setAttribute('href', 'mailto:'+d[0])})();</script>";
}

...now all I need to do is work it into Pepperminty Wiki and replace the old implementation. This isn't as easy as sounds though, because the old one just obfuscates the email address itself, and the new one generates a HTML fragment.....

sbrl commented 3 years ago

Done! That should do it. I'm just going to go with the JS solution for now.

For future reference if this is a problem for someone for whatever reason, please comment here or open a new issue. I take accessibility very seriously.