Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Inbound message API: Difference between revisions

From MoodleDocs
m (Tsala moved page VERP to Inbound message API: API name)
(One intermediate revision by one other user not shown)
Line 1: Line 1:
{{Infobox Project
{{Infobox Project
|name = VERP
|name = Inbound Message API
|state = Development in Progress
|state = Complete
|tracker = MDL-3714
|tracker = MDL-46282
|discussion = https://moodle.org/mod/forum/discuss.php?d=269424
|discussion = https://moodle.org/mod/forum/discuss.php?d=269424
|assignee = Andrew Nicols
|assignee = Andrew Nicols
}}
}}
{{Moodle 2.8}}
{{Moodle 2.8}}
== VERP ==
== Summary ==


Variable Envelope Return Path (VERP) is a method typically used to manage bouncing e-mail addresses on mail systems, but
By encoding pieces of data within an e-mail address, it is possible to route a message within Moodle and to
more accurately describes a way of routing e-mail messages within an application.
handle it accordingly. With the encoding of appropriate data within the e-mail address, it is also possible to perform
 
By encoding pieces of data within an e-mail address, it is possible to route that message within the application and
handle it accordingly. With the encoding of appropriate data within that e-mail address, it is also possible to perform
some level of sender verification, and to store additional data to enable further routing.
some level of sender verification, and to store additional data to enable further routing.


To ensure that the solution remains practical, a single incoming e-mail account is used, making use fo the e-mail
To ensure that the solution remains practical, a single incoming e-mail account is used, making use of the e-mail
Subaddress extension defined in RFC 5233 (Sieve Email Filtering: Subaddress Extension). This RFC specifies that a '+'
Subaddress extension defined in RFC 5233 (Sieve Email Filtering: Subaddress Extension). This RFC specifies that a the '+'
can be used to separate the mailbox from any additional subaddress data.  For example, in the e-mail address
symbol can be used to separate the mailbox from any additional subaddress data.  For example, in the e-mail address
`bob+moodle@example.com, the e-mail would be delivered to `bob@example.com` but the recipient would be able to filter
`bob+moodle@example.com, the e-mail would be delivered to `bob@example.com` but the recipient would be able to filter
mail based on the +moodle extension.
mail based on the +moodle extension. This extension is referred to as the subaddress.


Note: The maximum length of the localpart of an e-mail address (including subadress) as defined in RFC 5232, is 64
The maximum length of the localpart of an e-mail address (including subadress) as defined in RFC 5232, is 64
characters. The current maximum length of the subaddress component is 48 characters, with a further character used for
characters. The current maximum length of the subaddress component is 48 characters, with a further character used for
the subaddress separator. This leaves space for 15 characters in the address component.
the subaddress separator. This leaves space for 15 characters in the address component.
Line 30: Line 27:
The main benefits of this kind of handling are the addition of new ways of interacting with Moodle for certain content:
The main benefits of this kind of handling are the addition of new ways of interacting with Moodle for certain content:


* The ability for users to reply to content received from Moodle
* the ability for users to reply to content received from Moodle; and
* The ability to e-mail new content in to Moodle
* the ability to e-mail new content in to Moodle.


This has other potential knock-on benefits for users who cannot use the Moodle mobile application, or to unusual
This has other potential knock-on benefits for users who cannot use the Moodle mobile application, or to unusual
scenarios where Internet access may be unusually limited.
scenarios where Internet access may be unusually limited. It also allows users to respond in a natural, and familiar environment which mirrors that of other systems.


== Components ==
== Components ==


The VERP Handling system is broken down into several main components:
The Inbound Message Handling system is broken down into several main components:


# Address Management
# Address Management
Line 45: Line 42:
# Message retrieval system
# Message retrieval system


In order to preserve user-verification, each message used in the VERP process must be unique to that user. Additionally,
In order to preserve user-verification, each address used in the process must be unique to that user. Additionally,
the message must identify how the message should be handled within Moodle.
the message must identify how the message should be handled within Moodle.


A handler is only passed a message following successful verification of that message and may decide how it will handle
A handler is only passed a message following successful verification of that message and may decide how it will handle
the message from that point on.
the message from that point on.
Handlers may define certain characteristics of the message handling system including:
# the extent of the verification which takes place; and
# the default validity period.


=== Address Manager ===
=== Address Manager ===


An address and routing manager exists to:
An address and routing manager exists to:
# Generate unique address;
# generate unique addresses;
# Process received messages, and extract the data stored within them;
# process received messages, and extract the data stored within them;
# Validate addresses; and
# validate addresses; and
# Verify sender authenticity.
# verify sender authenticity.


Both an encode, and a decode stage exist.
Both an encode, and a decode stage exist.
Note: It is up to the developer to use the generated address in the correct fashion. Typically this will be as a 'reply-to' address when sending new messages.


==== Creating an e-mail address (Encode stage) ====
==== Creating an e-mail address (Encode stage) ====


When creating a new e-mail address, the following information is required:
When creating a new e-mail address, the following information is required:
# the system which will handle any e-mail received to the generated address;
# the '''handler''' - the system which will handle any e-mail received to the generated address;
# a single numeric piece of data which the received message relates to; and
# the '''data value''' - a single integer data which the received message relates to; and
# the id of the user that a message relates to.
# the '''user id''' - the id of the user that a message relates to.
 
A '''data key''' is generated for the supplied data value.


In the following example, an e-mail address is generated when sending a new forum post notification to a user. This
In the following example, an e-mail address is generated when sending a new forum post notification to a user. This
address is used in the reply-to field when sending the message, thereby allowing a user to reply to a post by e-mail.
address is used in the reply-to field when sending the message, thereby allowing a user to reply to a post by e-mail.


A new handler has been written for the forum '\mod\forum\verp\handler' which is aware of how it should handle any
A new handler has been written for the forum <code php>\mod\forum\message\inbound\reply_handler</code> which is aware of how it should handle any
received information.
received information.
The forum post notification will be sent to the user described by $recipient with a unique user id of $recipient->id.
The forum post notification will be sent to the user described by $recipient with a unique user id of <code php>$recipient->id</code>.
The forum post notification describes a new post described by $post and with a unique post id of $post->id.
The forum post notification describes a new post described by $post and with a unique post id of <code php>$post->id</code>.




<pre>
<code php>
// Create a new instance of the address manager.
// Create a new instance of the address manager.
$generator = new \core\verp\address();
$generator = new \core\message\inbound\address_manager();


// Specify the handler which will process any incoming messages for the generated address.
// Specify the handler which will process any incoming messages for the generated address.
$generator->set_handler('\mod\forum\verp\reply_hander');
$generator->set_handler('\mod\forum\message\inbound\reply_hander');


// Specify the post that is being replied to.
// Specify the post that is being replied to.
Line 88: Line 93:


// Generate an e-mail address to use for the reply-to, unique to this recipient.
// Generate an e-mail address to use for the reply-to, unique to this recipient.
$generator->generate($recipient->id);
$replyaddress = $generator->generate($recipient->id);
</pre>
 
$eventdata = new stdClass();
$eventdata->component          = 'mod_forum';
$eventdata->name                = 'posts';
$eventdata->userfrom            = $userfrom;
$eventdata->userto              = $userto;
$eventdata->subject            = $postsubject;
$eventdata->fullmessage        = $posttext;
$eventdata->fullmessageformat  = FORMAT_PLAIN;
$eventdata->fullmessagehtml    = $posthtml;
$eventdata->notification        = 1;
$eventdata->replyto            = $replyaddress;
</code>


The e-mail address generated will be of the nature:
The e-mail address generated will be of the nature:
Line 102: Line 119:


The following example address can be parsed to determine the sender, the handler, and the item of data to which the
The following example address can be parsed to determine the sender, the handler, and the item of data to which the
message refers.
message refers. Note that this has been implemented as core functionality and developers are not expected to need to implement this section themselves.
<pre>
<pre>
incoming+AAAAFXrQKf8AA3QP92///wABUl2Ukn//uCbIXwoNLnXy94kV@example.com
incoming+AAAAFXrQKf8AA3QP92///wABUl2Ukn//uCbIXwoNLnXy94kV@example.com
Line 113: Line 130:


// Create a new instance of the address manager.
// Create a new instance of the address manager.
$parser = new \core\verp\address();
$parser = new \core\message\inbound\address_manager();


// First parse the envelope data.
// First parse the envelope data.
Line 119: Line 136:


// We can check that the data validation passed.
// We can check that the data validation passed.
if ($result !== \core\verp\address::VALIDATION_SUCCES) {
if ($result !== \core\message\inbound\address::VALIDATION_SUCCESS) {
   echo "Some part of the data could not be verified\n";
   echo "Some part of the data could not be verified\n";
} else {
} else {
Line 125: Line 142:
   $data = $result->get_data();
   $data = $result->get_data();


   // Get the instance of the handler - this will be the \mod_forum\verp\reply_hander used to define the message originally.
   // Get the instance of the handler - this will be the \mod_forum\message\inbound\reply_hander used to define the message originally.
   $data->get_handler();
   $data->get_handler();


Line 140: Line 157:
Once a message has been processed by the address manager, it is passed to the handler specified in the address data.
Once a message has been processed by the address manager, it is passed to the handler specified in the address data.


Each Handler belongs to a specific component, and comprises of a class extending \core\verp\handler, which specifies a
Each Handler belongs to a specific component, and comprises of a class extending \core\message\inbound\handler, which specifies a
function to handle any received e-mail messages.
function to handle any received e-mail messages.


Line 146: Line 163:


To create a new handler, you must:
To create a new handler, you must:
# create a handle class; and
# create a class extending the handler class; and
# specify it's default values in that component's db/verp_handlers.php file.
# specify it's default values in that component's db/messageinbound_handlers.php file.


Each new handler must have a unique class name within Moodle and must be loadable using the Moodle class autoloader.
Each new handler must have a unique class name within Moodle and must be loadable using the Moodle class autoloader.
Line 161: Line 178:
|-
|-
! scope="row" | Namespace
! scope="row" | Namespace
| mod_forum\verp
| mod_forum\message\inbound
|-
|-
! scope="row" | Class Name
! scope="row" | Class Name
| mod_forum\verp\reply_handler
| mod_forum\message\inbound\reply_handler
|}
|}


In mod/forum/db/verp_handlers.php, define the available handlers.
In mod/forum/db/messageinbound_handlers.php, define the available handlers.
<pre>
 
<code php>
<?php
<?php


Line 175: Line 193:
$handlers = array(
$handlers = array(
     array(
     array(
       'classname' => '\mod_forum\verp\reply_handler',
       'classname' => '\mod_forum\message\inbound\reply_handler',
       'enabled' => false,
       'enabled' => false,
       'validateaddress' => true,
       'validateaddress' => true,
     ),
     ),
);
);
</pre>
</code>
 
And define the handler itself in /mod/forum/classes/message/inbound/reply_handler.php:
And define the handler itself in /mod/forum/classes/verp/reply_handler.php:
<code php>
<pre>
<?php
<?php


namespace mod_forum\verp;
namespace mod_forum\message\inbound;


defined('MOODLE_INTERNAL') || die();
defined('MOODLE_INTERNAL') || die();


class reply_handler extends \core\verp\handler {
class reply_handler extends \core\message\inbound\handler {
   // Place the content of the handler here.
   // Place the content of the handler here.
}
}
</code>


</pre>
A handler class must define three abstract functions:
 
# get_name() - Typically a string returned by get_string() to display as a name in the admin interface;
A handler class must define two abstract functions:
# get_description() - Typically a string returned by get_string() to display in the admin interface; and
# get_description() - Typically a string returned by get_string() to display in the admin interface; and
# process_message() - The function called after successful validation of a message, which takes information including the recipient, sender, headers, body, and a list of attachments.
# process_message() - The function called after successful validation of a message, which takes information including the recipient, sender, headers, body, and a list of attachments.
Line 204: Line 221:
# allow_enabled_change() - To prevent a Moodle administrator from enabling, or disabling the handler
# allow_enabled_change() - To prevent a Moodle administrator from enabling, or disabling the handler


A new handler must also be defined within the component in lib/db/verp_handlers.php. This array of VERP handlers
A new handler must also be defined within the component in lib/db/messageinbound_handlers.php. This array of handlers
includes the default settings for the handler used at creation time.
includes the default settings for the handler used at creation time.
==== Removing quoted text from email messages ====
If you want the quoted text to be removed from email messages you can call the method remove_quoted_text from inside your handler as below:-
<code php>
list ($message, $format) = self::remove_quoted_text($messagedata);
</code>
The method will make an educated guess and remove quoted text from the message and return the format and message contents. This method doesn't work perfectly at the moment and MDL-50058 is supposed to improve this.


== Validation and Verification ==
== Validation and Verification ==


The VERP system performs a step of both validation, and verification.
The Inbound Message system performs a step of both validation, and verification.


Note: E-mail is never a secure method of communication, and it is easy to `spoof` the `sender` field when sending an
Note: E-mail is never a secure method of communication, and it is easy to `spoof` the `sender` field when sending an
e-mail very easily. As a result, administrators should be fully aware of the consequences when enabling any VERP
e-mail very easily. As a result, administrators should be fully aware of the consequences when enabling any Inbound Message
handler.
handler.


Line 244: Line 269:


As an additional prevention method, the handler could define a further e-mail based verification check whereby on
As an additional prevention method, the handler could define a further e-mail based verification check whereby on
reception of a VERP e-mail, the handler system would:
reception of an inboune e-mail, the handler system would:
# store the retrieved message (park it);
# store the retrieved message (park it);
# send a new e-mail to the user described in $user containing a link or a further reply message; and
# send a new e-mail to the user described in $user containing a link or a further reply message; and
Line 251: Line 276:
==== Spoofed e-mail address ====
==== Spoofed e-mail address ====


As mentioned above, it is incredibly easy to spoof the Sender field in an e-mail. If the full VERP address is captured
As mentioned above, it is incredibly easy to spoof the Sender field in an e-mail. If the full address is captured
by an attacker, then it would be possible to send e-mail as that user and the VERP system would be unable to distinguish
by an attacker, then it would be possible to send e-mail as that user and the Inbound Message system would be unable to distinguish
the message from a valid address.
the message from a valid address.



Revision as of 05:55, 6 May 2015

Inbound Message API
Project state Complete
Tracker issue MDL-46282
Discussion https://moodle.org/mod/forum/discuss.php?d=269424
Assignee Andrew Nicols

Moodle 2.8

Summary

By encoding pieces of data within an e-mail address, it is possible to route a message within Moodle and to handle it accordingly. With the encoding of appropriate data within the e-mail address, it is also possible to perform some level of sender verification, and to store additional data to enable further routing.

To ensure that the solution remains practical, a single incoming e-mail account is used, making use of the e-mail Subaddress extension defined in RFC 5233 (Sieve Email Filtering: Subaddress Extension). This RFC specifies that a the '+' symbol can be used to separate the mailbox from any additional subaddress data. For example, in the e-mail address `bob+moodle@example.com, the e-mail would be delivered to `bob@example.com` but the recipient would be able to filter mail based on the +moodle extension. This extension is referred to as the subaddress.

The maximum length of the localpart of an e-mail address (including subadress) as defined in RFC 5232, is 64 characters. The current maximum length of the subaddress component is 48 characters, with a further character used for the subaddress separator. This leaves space for 15 characters in the address component.

Benefits

The main benefits of this kind of handling are the addition of new ways of interacting with Moodle for certain content:

  • the ability for users to reply to content received from Moodle; and
  • the ability to e-mail new content in to Moodle.

This has other potential knock-on benefits for users who cannot use the Moodle mobile application, or to unusual scenarios where Internet access may be unusually limited. It also allows users to respond in a natural, and familiar environment which mirrors that of other systems.

Components

The Inbound Message Handling system is broken down into several main components:

  1. Address Management
  2. Message Handling
  3. Handler Management
  4. Message retrieval system

In order to preserve user-verification, each address used in the process must be unique to that user. Additionally, the message must identify how the message should be handled within Moodle.

A handler is only passed a message following successful verification of that message and may decide how it will handle the message from that point on.

Handlers may define certain characteristics of the message handling system including:

  1. the extent of the verification which takes place; and
  2. the default validity period.

Address Manager

An address and routing manager exists to:

  1. generate unique addresses;
  2. process received messages, and extract the data stored within them;
  3. validate addresses; and
  4. verify sender authenticity.

Both an encode, and a decode stage exist.

Note: It is up to the developer to use the generated address in the correct fashion. Typically this will be as a 'reply-to' address when sending new messages.

Creating an e-mail address (Encode stage)

When creating a new e-mail address, the following information is required:

  1. the handler - the system which will handle any e-mail received to the generated address;
  2. the data value - a single integer data which the received message relates to; and
  3. the user id - the id of the user that a message relates to.

A data key is generated for the supplied data value.

In the following example, an e-mail address is generated when sending a new forum post notification to a user. This address is used in the reply-to field when sending the message, thereby allowing a user to reply to a post by e-mail.

A new handler has been written for the forum \mod\forum\message\inbound\reply_handler which is aware of how it should handle any received information. The forum post notification will be sent to the user described by $recipient with a unique user id of $recipient->id. The forum post notification describes a new post described by $post and with a unique post id of $post->id.


// Create a new instance of the address manager. $generator = new \core\message\inbound\address_manager();

// Specify the handler which will process any incoming messages for the generated address. $generator->set_handler('\mod\forum\message\inbound\reply_hander');

// Specify the post that is being replied to. $generator->set_data($post->id);

// Generate an e-mail address to use for the reply-to, unique to this recipient. $replyaddress = $generator->generate($recipient->id);

$eventdata = new stdClass(); $eventdata->component = 'mod_forum'; $eventdata->name = 'posts'; $eventdata->userfrom = $userfrom; $eventdata->userto = $userto; $eventdata->subject = $postsubject; $eventdata->fullmessage = $posttext; $eventdata->fullmessageformat = FORMAT_PLAIN; $eventdata->fullmessagehtml = $posthtml; $eventdata->notification = 1; $eventdata->replyto = $replyaddress;

The e-mail address generated will be of the nature:

incoming+AAAAFXrQKf8AA3QP92///wABUl2Ukn//uCbIXwoNLnXy94kV@example.com

Parsing an e-mail address (Decode and validation stage)

When an e-mail is received, the 'To' address field is parsed to determine the way in which the e-mail should be processed.

The following example address can be parsed to determine the sender, the handler, and the item of data to which the message refers. Note that this has been implemented as core functionality and developers are not expected to need to implement this section themselves.

incoming+AAAAFXrQKf8AA3QP92///wABUl2Ukn//uCbIXwoNLnXy94kV@example.com
// The message was sent by bob to the incoming mailbox:
$recipient = 'incoming+AAAAFXrQKf8AA3QP92///wABUl2Ukn//uCbIXwoNLnXy94kV@example.com';
$sender = 'bob@example.com';

// Create a new instance of the address manager.
$parser = new \core\message\inbound\address_manager();

// First parse the envelope data.
$result = $parser->process_envelope($recipient, $sender);

// We can check that the data validation passed.
if ($result !== \core\message\inbound\address::VALIDATION_SUCCESS) {
  echo "Some part of the data could not be verified\n";
} else {
  // We can retrieve the data stored in the message.
  $data = $result->get_data();

  // Get the instance of the handler - this will be the \mod_forum\message\inbound\reply_hander used to define the message originally.
  $data->get_handler();

  // The user key is the user record of the user who the original message was sent to.
  $data->user;

  // The datavalue is the integer value that was specified when the message was originally sent.
  $data->datavalue;
}

Handlers

Once a message has been processed by the address manager, it is passed to the handler specified in the address data.

Each Handler belongs to a specific component, and comprises of a class extending \core\message\inbound\handler, which specifies a function to handle any received e-mail messages.

Creating a new Handler

To create a new handler, you must:

  1. create a class extending the handler class; and
  2. specify it's default values in that component's db/messageinbound_handlers.php file.

Each new handler must have a unique class name within Moodle and must be loadable using the Moodle class autoloader.

As an example, to create a forum post reply handler called reply_handler:

Component Name mod_forum
Handler Name reply_handler
Namespace mod_forum\message\inbound
Class Name mod_forum\message\inbound\reply_handler

In mod/forum/db/messageinbound_handlers.php, define the available handlers.

<?php

defined('MOODLE_INTERNAL') || die();

$handlers = array(

   array(
     'classname' => '\mod_forum\message\inbound\reply_handler',
     'enabled' => false,
     'validateaddress' => true,
   ),

); And define the handler itself in /mod/forum/classes/message/inbound/reply_handler.php: <?php

namespace mod_forum\message\inbound;

defined('MOODLE_INTERNAL') || die();

class reply_handler extends \core\message\inbound\handler {

 // Place the content of the handler here.

}

A handler class must define three abstract functions:

  1. get_name() - Typically a string returned by get_string() to display as a name in the admin interface;
  2. get_description() - Typically a string returned by get_string() to display in the admin interface; and
  3. process_message() - The function called after successful validation of a message, which takes information including the recipient, sender, headers, body, and a list of attachments.

It may optionally define some additional functions:

  1. allow_validateaddress_change() - To prevent an Moodle administrator from changing whether E-mail address validation is required. Typically this is used to force sender validation.
  2. allow_enabled_change() - To prevent a Moodle administrator from enabling, or disabling the handler

A new handler must also be defined within the component in lib/db/messageinbound_handlers.php. This array of handlers includes the default settings for the handler used at creation time.

Removing quoted text from email messages

If you want the quoted text to be removed from email messages you can call the method remove_quoted_text from inside your handler as below:-

list ($message, $format) = self::remove_quoted_text($messagedata);

The method will make an educated guess and remove quoted text from the message and return the format and message contents. This method doesn't work perfectly at the moment and MDL-50058 is supposed to improve this.

Validation and Verification

The Inbound Message system performs a step of both validation, and verification.

Note: E-mail is never a secure method of communication, and it is easy to `spoof` the `sender` field when sending an e-mail very easily. As a result, administrators should be fully aware of the consequences when enabling any Inbound Message handler.

When an address is generated, it will be composed of a base64-encoded binary set containing: [handlerid][userid][datavalue][verifiationstring]

  1. the handlerid is the database ID of the handler used to process the message;
  2. the userid is the database ID of the user associated with the message;
  3. the datavalue is an integer value associated with the message; and
  4. the verifiationstring is part of an md5 hash of a user key, and data key.

A new key will be generated for each individual user. This is unique to the user. A new key will be generated for each piece of data for a handler. This is unique to the handler and datavalue combination.

The keys generated for both the user, and for the handler-data pair are never exposed in the string and are always verified. The userid specified in the address must match the key stored for that user in Moodle; and similarly the datavalue specified in the address must match the key stored for the handler-pair in moodle.

This combination of three pieces of plain-text information, with two pieces of hidden additional information should make it harder for any potential attacks.

Additionally, an administrator can choose whether to compare the e-mail address of the sender against the e-mail address held against a user record. This is enabled by default, but can be disabled by an administrator.

As a further protection, it is possible to specify an expiry timestamp for each handler-datavalue keypair.

Potential attack vectors

Several potential attack vectors exist, and handler authors should determine the optimum solution required. At the very least, it is advisable to recommend sender address comparison, but this is not foolproof on it's own.

As an additional prevention method, the handler could define a further e-mail based verification check whereby on reception of an inboune e-mail, the handler system would:

  1. store the retrieved message (park it);
  2. send a new e-mail to the user described in $user containing a link or a further reply message; and
  3. await for approval from the e-mail.

Spoofed e-mail address

As mentioned above, it is incredibly easy to spoof the Sender field in an e-mail. If the full address is captured by an attacker, then it would be possible to send e-mail as that user and the Inbound Message system would be unable to distinguish the message from a valid address.

Mitigations =

Prevention
  1. When creating keys, specify an appropriate expiry time for that key pair
  2. When sending e-mail within Moodle, only use the Reply-To field as this is not typically exposed when a message is forwarded to a third-party
  3. Provide information to users advising against forwarding of e-mail
Damage Control
  1. Expire any affected keys

Brute force attack of e-mail accounts

As with any system of this nature (including passwords), it is possible to apply a brute force attack. It would be possible to reverse engineer the e-mail address for a known user, and then recompile it with a range of combinations.

With the proposed length of the verification has (24 characters), this provides for 79,228,162,514,264,337,593,543,950,336 combinations of hash.

Prevention
  1. When creating data keys, specify an appropriate expiry time for that key pair
Damage Control
  1. Expire any affected keys
Other notes

It is difficult to increase the length of the verification hash. At present the hash is 24 characters which makes the maximum length of the subaddress 48 characters, leaving space for 15 characters in the mailbox part of the address. Although it would be possible to increase the length of an address, doing so would invalidate all previously used addresses.


verp-prototype-screen-examples.png