Ticket #2220 (closed New Feature: fixed)

Opened 6 years ago

Last modified 4 years ago

Encode email "mailto:" links

Reported by: asuter Owned by:
Priority: Normal Milestone: FCKeditor 2.6.3
Component: UI : Dialogs Version: FCKeditor 2.6
Keywords: Confirmed HasPatch Review+ Cc:

Description

So far the email address typed in the link dialog gets packed as a href-attribute unencoded into the html source. This is easy for spambots to be scanned and so it is necessary to at least obscure the mail address. This proposal provides a possibility to encode the email address using the simple String.fromCharCode() technique.

We modify the file "editor/dialog/fck_link/fck_link.js" such that the method oParser.CreateEMailUri returns the email href-attribute not in plain text but encoded.

Replace the return statement of the oParser.CreateEMailUri method

  return sBaseUri + sParams ;

by

  var uri = sBaseUri + sParams ;
  var urj = '' ;
  for ( var i = 0; i < uri.length; i ++ ) {
    if ( i > 0 ) { urj += ','; }
    urj += uri.charCodeAt(i) ;
  }
  return 'javascript:location.href=String.fromCharCode(' + urj + ')' ;

As the "protocol" of the link is no more "mailto", we should implement also a decoder that from the encoded href-attribute gets back the email address as well as the subject and the body. This is important if the CKEditor user wants to edit again an encoded email link. This is not yet implemented.

Attachments

fck_link.js.tar.gz (7.0 KB) - added by asuter 6 years ago.
Patch for "editor/dialog/fck_link/fck_link.js.tar.gz"
fck_link.2.patch (24.5 KB) - added by asuter 6 years ago.
2220.patch (9.9 KB) - added by asuter 6 years ago.
Patch
2220_1.patch (9.3 KB) - added by asuter 6 years ago.
Minor bugs removed and code cleaned

Change History

comment:1 Changed 6 years ago by asuter

Pattern technique

I have written another, more complex and customizing, version. This version does not use the String.fromCharCode() technique.

This new technique would simply accept a pattern for the href-attribute of an email link. The pattern has some tags (placeholders) that will be used by the method oParser.CreateEMailUri to replace the given elements like "name", "domain", "subject" and "body". The placeholder "name" would be defined by the part before the @-symbol of the email address, and the placeholder "domain" would be defined by the part after the @-symbol.

There are two possibilities for the placeholders: Either you write {placeholder}, which gets replaced by the value of the placeholder, or {{placeholder}} which gets replaced by the value of the placeholder surrounded by single quotes.

For example, the pattern to build the standard "mailto:" href would look like

mailto:{name}@{domain}?subject={subject}&body={body}

The user should know, that he can implement his own javascript function that handles the mail link. That is, he can for example write the function

function mto(domain,name,subject,body) {
  location.href = 'mailto:' + name + '@' + domain + '?subject=' + subject + '&body=' + body;
}

and put the corresponding pattern

javascript:mto({{domain}},{{name}},{{subject}},{{body}})

Note that the function has to be present in the frontend, that is where he finally outputs the content.

Implementation

Add a new configuration value by simply writing somewhere in the fckconfig.js (or in your FCKConfig.CustomConfigurationsPath file) the following line

FCKConfig.MailLinkPattern = 'javascript:mt({{domain}},{{name}},{{subject}},{{body}})' ;

Replace the method oParser.CreateEMailUri by

oParser.CreateEMailUri = function( address, subject, body )
{
  var pattern = FCKConfig.MailLinkPattern ;
  if ( pattern == null ) {
    pattern = 'mailto:{name}@{domain}?subject={subject}&body={body}' ;
  }
  var atPosition = address.indexOf('@') ;
  var name = '' ;
  var domain = '' ;
  if ( atPosition >= 0 ) {
    name = address.substring(0, atPosition) ;
    domain = address.substring(atPosition + 1) ;
  }

  // Replace the single quoted placeholders, i.e. {{placeholder}}
  pattern = pattern.replace(/{{name}}/g, "'" + name.replace(/'/g, '\\\'') + "'") ;
  pattern = pattern.replace(/{{domain}}/g, "'" + domain.replace(/'/g, '\\\'') + "'") ;
  pattern = pattern.replace(/{{subject}}/g, "'" +
    encodeURIComponent( subject ).replace(/'/g, '\\\'') + "'") ;
  pattern = pattern.replace(/{{body}}/g, "'" +
    encodeURIComponent( body ).replace(/'/g, '\\\'') + "'") ;

  // Replace the non quoted placeholders, i.e. {placeholder}
  pattern = pattern.replace(/{name}/g, name) ;
  pattern = pattern.replace(/{domain}/g, domain) ;
  pattern = pattern.replace(/{subject}/g, encodeURIComponent( subject )) ;
  pattern = pattern.replace(/{body}/g, encodeURIComponent( body )) ;

  return pattern ;
}

That's all! The reverse function (for re-editing the link) is not implemented yet.

Changed 6 years ago by asuter

Patch for "editor/dialog/fck_link/fck_link.js.tar.gz"

comment:2 Changed 6 years ago by w.olchawa

  • Keywords Confirmed HasPatch added; link dialog, email, encode removed

comment:3 follow-up: ↓ 4 Changed 6 years ago by fredck

Both proposals look interesting. The first doesn't require additional work on the front end, which is a positive thing for a first level encryption.

The second solution is the ideal thing, but involves wider site changes.

To note that both approaches require JavaScript to be enabled, so we should, by default, avoid any kind of protection. We could have the following settings for that:

  • EMailProtection : 'None' / 'Encode' / 'Function'
  • EMailProtectionFunction : the function pattern. Simpler keys cold be used here, like uppercased words with no special brackets.

We still need the reverse engineering code, which transforms the encoded value to the original one.

comment:4 in reply to: ↑ 3 Changed 6 years ago by asuter

The reverse engineering code for the second solution is already implemented in the patch. It degrades well, i.e. a default mailto: email link would be recognized as such. The idea of the settings and simpler keys (placeholders) have to be implemented. The setting "EMailProtectionFunction" does already exist but is called "MailLinkPattern". I really like the idea of "EMailProtection".

How about also

  • EMailProtectionEncodeType : 'basic' / 'reverse' / 'fromCharCode' / ...

The encode type further defines the javascript possibilities like

// basic
location.href='mailto:'+['m','a','i','l','@','e','x','a','m','p','l','e','.','c','o','m'].reverse().join('')

// reverse
location.href='mailto:'+['m','o','c','.','e','l','p','m','a','x','e','@','l','i','a','m'].reverse().join('')

// fromCharCode
location.href='mailto:'+String.fromCharCode(109, 97, 105, 108, 64, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109);

// etc.

Changed 6 years ago by asuter

comment:5 Changed 6 years ago by asuter

I've just implemented the new ideas and uploaded a new patch. You can set the following configurations

FCKConfig.EMailProtection = 'function' ; // 'none' | 'encode' | 'function'
FCKConfig.EMailProtectionFunction = 'mt(DOMAIN,NAME,BODY,SUBJECT)' ;

The reverse engineering code works for non protected email addresses as well as protected email addresses using the 'function'. The reverse code would not take the EMailProtection setting into account as it should reverse all three possibilities. I mean, if you edited some content in no-protection mode, and then you switch protection on, you should still be able to detect the non-protected email links.

So far, the reverse code is implemented and works for 'none' and 'function'. If should also be implemented for 'encode'.

Changed 6 years ago by asuter

Patch

comment:6 Changed 6 years ago by asuter

The reverse engineering code is now working for all three types of protection 'none', 'encode' and 'function'!

comment:7 Changed 6 years ago by fredck

  • Milestone set to FCKeditor 2.6.1

@asuter, nice job! I'm targeting it to the 2.6.1 for verification and to possibly include it on that release.

Before that, I need you to sign (edit the page, adding your name) our CLA, so we can properly use your code: http://dev.fckeditor.net/wiki/cla

Thanks!

comment:8 Changed 6 years ago by asuter

@fredck, done!

Thanks to be part of this great project.

How do you think about a combobox (or radio button) in the dialog where the user can select for each email link individualy, which method ('none', 'encode', 'functino') to use? Does anything like this make sense?

comment:9 Changed 6 years ago by fredck

@asuter, thank *you*! You did it in the right way; if you want something, put your hands at work and you'll have it ;) Usually people just ask for.

I've also thought about making it configurable by the end user. But, the link dialog is already full of stuff, so it is better avoid introducing yet another thing.

In any case, a generic developer configured setting seems to be enough for it. We can always enhance it in the future, if needed.

comment:10 Changed 6 years ago by martinkou

  • Keywords Review? added

I've tested it and it seems to be working well. I still haven't read and understood all the code though. Putting up a Review? flag so it can be reviewed later.

comment:11 Changed 6 years ago by martinkou

  • Keywords Review- added; Review? removed

The patch is almost perfect but it still has a minor bug, and some of the code can be simplified.

  1. With FCKConfig.EMailProtection='function', if I enter a email message body with two single quotes, e.g. "Hi there, have you seen the movie 'Indiana Jones'?", and save the link; then the second single quote would be broken when you examine that link again. (e.g. the above example would turn to "Hi there, have you seen them movie 'Indiana Jones###SINGLE_QUOTE###?" when you open the link dialog again).
  2. The lines 144-154 can be condensed to:
    func = func.replace( /([/^$*+.?()\[\]])/g, '\\$1' ) ;
    
  3. The lines 240-248 can be simplified a bit with String::split().
  4. The lines 257-276 can be simplified to something like the following, because you can eliminate some "if" statements by liberal use of Array::join(); also a "break" is not needed after "return":
            case 'encode' :
                var aParams = [] ;
                var aAddressCode = [] ;
    
                if ( subject.length > 0 )
                    aParams.push( 'subject='+ encodeURIComponent( subject ) ) ;
                if ( body.length > 0 )
                    aParams.push( 'body=' + encodeURIComponent( body ) ) ;
                for ( var i = 0 ; i < address.length ; i++ )
                    aAddressCode.push( adress.charCodeAt( i ) ) ;
    
                return 'javascript:location.href="mailto:"+String.fromCharCode(' + aAddressCode.join( ',' ) + ')+"?' + aParams.join( '&' ) + '"' ;
    

Still, the patch is excellent, good job. ;)

Changed 6 years ago by asuter

Minor bugs removed and code cleaned

comment:12 Changed 6 years ago by asuter

The following has been done

  1. There was missing a g-modifier in the regular expression that replaces those ###SINGLE_QUOTE### placeholders. Therefore, only the first one has been replaced. Fixed.
  2. Of course, this is much shorter. Fixed.
  3. I have implemented this code part by using the split function (Fixed). So far, it is only possible to set one email address. But maybe in a future version it should be possible to let the user enter more than one email address, separated by a comma. This may be an important note!
  4. Right. I have just replaced the code block with what you have suggested, changed the undefined variable adress into address :-), and surrounded the "mailto;" and the params part by escaped single quotes instead of double quotes as the parser would look for
    javascript:location.href='mailto;'
    and not
    javascript:location.href="mailto;"
    (with : instead of ; - I had to write ; as otherwise Wiki would have transformed the text into a mail-link).

That's it. Hope, everything is working now. Let me know if there are still some bugs.

comment:13 Changed 6 years ago by martinkou

  • Keywords Review? added; Review- removed

Putting the new patch up for review.

comment:14 Changed 6 years ago by martinkou

  • Keywords Review+ added; Review? removed

The patch works. Review+.

I'll commit the patch to the trunk later today.

comment:15 Changed 6 years ago by martinkou

  • Status changed from new to closed
  • Resolution set to fixed

Fixed with [2157].

Click here for more info about our SVN system.

comment:16 Changed 6 years ago by aspenwebdesign

  • Status changed from closed to reopened
  • Resolution fixed deleted

This "fix" causes problems in Internet Explorer. When you click on an encoded (by FCK) mailto link on the frontend of a website, it causes IE6 and IE7 to do two things:

  1. Open a new email window (what we want)
  2. Take the browser to a new page with mailto:... in the content (what we don't want)

This problem exists in IE, but not in Firefox or Safari (what else is new). It's possible this has been resolved in 2.6.3 (we're running 2.6.3 beta) but there is no mention of it.

Can someone kindly resolve this?

comment:17 Changed 6 years ago by fredck

  • Status changed from reopened to closed
  • Resolution set to fixed

@aspenwebdesign, can you kindly open a dedicated ticket for this? This ticket is a released feature so issues related to it should go on new tickets.

Thanks in advance.

comment:18 Changed 6 years ago by surfacepatterns

This is a silly solution for a few reasons:

1.) This is a temporary, cheap obfuscation solution. It prevents unevolved bots from harvesting email addresses, but it doesn't stop bots from implementing trivial solutions to harvest email addresses encoded in this manner. If I can write 2-3 lines of code to parse the link, then a bot can be taught to do the same thing. 2.) Some browsers allow JavaScript to be disabled. The "mailto" links no longer work when JavaScript is disabled. 3.) Some clients that read HTML are not browsers! This "fix" assumes that the viewer is not an e-mail client, a documentation viewer, or anything else besides a browser that has JavaScript enabled. In my own developments using FCKEditor, I've allowed clients to edit the HTML content of email messages. The "mailto" links they attempt to add to their email messages don't work because they're encoded in this manner.

I suggest you remove this "fix".

comment:19 Changed 5 years ago by fredck

@surfacepatterns, we're offering by default some basic level of protection. The recommended setting though is using the "function" option, which is supposed to be unknown by the bots.

You can also disable this setting, if you really need to support browsers with JavaScript disabled.

comment:20 Changed 4 years ago by manu37

A very nice solution, at least for the moment. It's a bit off topic but it would be perfect, if I could also obscure the text inside the <a href..> tag, which is often the e-mail address too. Does anybody have a clue?

Note: See TracTickets for help on using tickets.
© 2003 – 2012 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy