Mailtrain-org / mailtrain

Self hosted newsletter app
GNU General Public License v3.0
5.51k stars 692 forks source link

Multiple users and XSS through custom forms and campaigns #296

Closed bures closed 3 years ago

bures commented 7 years ago

I'm trying to figure out the best possible way to prevent XSS while giving users freedom in defining custom forms and campaigns. Currently it is possible to insert arbitrary JS in custom forms and campaigns, which means that anyone who can create those can get admin privileges.

I doubt we could reliably prevent including JS in custom forms and campaigns. I'm afraid that such solution would be either too restricted or leaky.

To address this, I was thinking about splitting the content into two areas: (1) admin interface where we prevent XSS, (2) public interface, where we don't care about XSS. These two would be served on different ports or hosts - e.g. mailtrain.example.org (the admin interface that requires login), lists.example.org or mailtrain.example.org:8080 (public subscription forms and archive).

Since there are no protected endpoins in area 2, it should not matter if anyone injects XSS into the content which is displayed there.

The endpoints for the two areas would be configurable in config. For single-user deployments, they can be kept the same. For deployments where creation of custom forms and templates should be delegated to non-admin users, the two endpoints would have to be configured.

What do you think? Or do you have any easier / more elegant solution in mind? Thanks.

witzig commented 7 years ago

Oh yes, I was looking into this in March and nearly forgot about the problem.

Splitting the content into two areas would be a great solution if it weren't for live previews. Quickly tested in GrapesJS with <img src="404" onerror="alert()">.

For layouts, campaigns, and custom forms, it would be necessary to render every possible view without our scripts, then reliably detect any JS (maybe with a headless browser) before accepting the update.

I doubt we could reliably prevent including JS in custom forms and campaigns. I'm afraid that such solution would be either too restricted or leaky.

Hmm. I guess you're right.

bures commented 7 years ago

If I'm not mistaken the editor and the preview are run in an iframe which communicates with the host via 2-3 calls. We could serve the iframe from the insecure domain and decouple the communication between the iframe and the host by sending messages. It might be that we won't need any authenticated access for the iframe because it would be completely configured by the host document. I'll investigate it.

What do you mean by "our scripts" in the 3rd paragraph? My assumption was that the only things that need contain custom HTML are only those that are publicly accessible anyway. This it should be no harm to have them in the other area.

Of course, report templates can also contain custom HTML, but there we can see. We could again use some trick with iframe and one time auth link to open the report in the iframe.

What do you think? Thanks.

witzig commented 7 years ago

If I'm not mistaken the editor and the preview are run in an iframe which communicates with the host via 2-3 calls. We could serve the iframe from the insecure domain and decouple the communication between the iframe and the host by sending messages.

This will work. Great idea! GrapesJS and Mosaico already run in an iframe, just Summernote will need to be wrapped in an iframe too.

What do you mean by "our scripts" in the 3rd paragraph?

Primarily the subscription_footer_scripts partial for custom forms or custom scripts injected via config.

My assumption was that the only things that need contain custom HTML are only those that are publicly accessible anyway. This it should be no harm to have them in the other area.

Yes, this is my understanding too.

talheim-it commented 3 years ago

Close since it is v1 related. Maybe another feature we can rethink of it in future.