如何拦截上载的电子邮件附件与Firefox XUL扩展在outlook.com web邮件

How to intercept uploading of email attachments with a Firefox XUL extension in outlook.com web mail

本文关键字:扩展 XUL outlook com 邮件 web Firefox 上载 何拦截 电子邮件      更新时间:2023-09-26

我有一个XUL扩展,它使用本地Windows DLL和js-ctypes来加密本地文件系统中的文件。我已经测试了一个菜单驱动的版本,它似乎工作得很好。

现在我想做以下事情:当创建一个带有附件的新电子邮件时,能够"捕获"附件文件并在上传到合成的电子邮件消息之前对其进行处理(意思是:加密)。我想以一种透明的方式来做,这样用户就不必经历菜单驱动的过程,除了提供加密密码。

我想在基于outlook.com的web电子邮件(不是Office版本)中这样做。

我知道这是一个很长的机会,但是有人知道从哪里开始找吗?有人以前做过类似的事吗?

提前感谢!

一个很好的开始是一个插件已经做了你想要的(以一种通用的方式):

https://addons.mozilla.org/en-US/firefox/addon/tamper-data/

下载页面显示Use tamperdata to view and modify HTTP/HTTPS headers and post parameters。如果您对更改"post参数"感兴趣,那么这是一个很好的开始。


但是如果你只是想自己实现这个....

我已经无序地回答了这个问题,以便按照您可能在开发中构建解决方案的方式进行。

在最后的扩展中,您需要:

  1. 拦截请求
  2. 针对正确的请求
  3. 获取POST请求主体的访问权限
  4. 解析POST请求主体的表单数据(以获得真正的二进制文件数据)
  5. 执行加密步骤
  6. 重新编码二进制文件数据,重新组装表单数据,修改POST请求头
  7. 替换POST请求中的现有内容

拦截请求&替换现有的POST内容

最基本的是,你需要实现一个nsIObserver,传递一个nsIHTTPChannel作为观察的"主题"。您希望观察的"通知"称为http-on-modify-request

http-on-modify-request的文档中有拦截GET请求的简单示例(1,2),但是拦截POST请求要复杂得多。

获取POST请求正文:

有一个mozillazine论坛讨论这个确切的话题。 Kamelot9的第二个帖子在该线程中详细介绍了如何(1)在帖子主体:

var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
var uploadChannel = httpChannel.QueryInterface(Ci.nsIUploadChannel);
var uploadChannelStream = uploadChannel.uploadStream;
uploadChannelStream
    .QueryInterface(Ci.nsISeekableStream)
    .seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
var stream = Cc["@mozilla.org/binaryinputstream;1"]
    .createInstance(Ci.nsIBinaryInputStream);
stream.setInputStream(uploadChannelStream);
var postBytes = stream.readByteArray(stream.available());
var poststr = String.fromCharCode.apply(null, postBytes);

这里的aSubject作为http-on-modify-request通知的参数出现。然后,只需修改poststr即可。根据服务器的不同,您可能还需要修改Content-length头(或者您的帖子可能被截断)。

替换POST请求内容:

一旦您有了修改后的POST主体,您需要(2)用您自己的uploadChannel替换inputStream中现有的内容:

var inputStream = Cc["@mozilla.org/io/string-input-stream;1"]
    .createInstance(Ci.nsIStringInputStream);
inputStream.setData(poststr, poststr.length);
uploadChannel.setUploadStream(
    inputStream, 
    "application/x-www-form-urlencoded", 
    -1);
// do this last - setUploadStream resets requestMethod to PUT
httpChannel.requestMethod = "POST";

CcCi分别是Components.classesComponents.interfaces的简写。这些简写变量可能已经设置好了,或者您可以自己定义它们。

解析表单数据:

我认为通常对于文件上传,Content-type:将是multipart/form-data

要进入你感兴趣的特定"附件",你需要:

  1. 解析mime信封以获取文件附件
  2. 查找文件附件
  3. 删除已使用的任何文本编码(例如:BASE64)

在POST报头中,您将得到如下内容:

Content-Type: multipart/form-data; boundary=JGUOAeGT3Fjgjcdk6s35F2mPVVyTdzgR

其中'JGUOAeGT3Fjgjcdk6s35F2mPVVyTdzgR'为MIME边界。在POST的正文中,内容将以如下格式开始:

--[boundary]
CONTENT-PART #1
--[boundary]
CONTENT-PART #2
--[boundary]

上面的每个CONTENT-PART都有一些HTTP头,一个空白行,然后是特定CONTENT-PART的正文。

另一个stackoverflow问题的例子:

Content-Disposition: form-data; name="updates"; filename="update1353963418000.json"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: binary
{"collectionType":"activities","date":"2012-11-26","ownerId":"qw12er23","ownerType":"user","subscriptionId":"112233-activities"}]

在这种情况下,Content-Transfer-Encoding是二进制(原始的,编码的)UTF8,所以您不需要做任何更多的工作就可以读取CONTENT-PART主体中的JSON。

在您的示例中,浏览器将发送二进制文件,因此它可能将Content-Transfer-Encoding设置为base64,这意味着您需要对CONTENT-PART的主体进行base64解码以获得真正的二进制文件。如果base64data包含编码的内容,那么这将为您提供原始二进制数据:

var rawData = atob(base64data);

此时你可以在rawData上做任何你想做的加密。

请记住,您必须在加密后重新编码二进制数据(使用btoa),然后您需要在重新构造POST请求体之前重新组装多部分信封。(不要忘记获取最终请求正文的.length,以便您可以在请求头中替换Content-length。)

针对请求:

这是修改POST请求的基本机制。但是您仍然需要挑选出您的特定的POST请求(检查observer通知中的POST请求URL),以便允许其他POST请求正常进行,而无需调用修改代码。