Regardless of framework or language, there are some things you should consider:
- What functionality do I need (or want)?
- How can I balance the features with Principle of Least Privilege?
- What resources do I have that can be limitations?
My response is going to be language-agnostic because these principles are going to apply regardless of whatever language or framework that you are using.
From your post, you want to have the ability for users to add comments (such as what's here on SE) and upload images. There are a few common methods of doing this.
- HTML controls for users (bad idea)
- BBCode or similar markup (can be effective, but annoying for users)
- Markdown or RST (both are built to be more natural)
- Plaintext (won't allow for UI formatting)
As Wordpress and other applications have demonstrated, allowing users to post HTML code is a Bad Idea(tm). If you want users to have UI formatting, plaintext is the safest, but won't allow for UI formatting. So that leaves BBCode and Markdown as the two remaining common solutions. SE uses Markdown. It's more natural for users to type than it is
[b]Blah[/b] because for emphasis, users would type
**blah** for bold text anyway. This of course is not the only method, but it does greatly reduce (but not eliminate!) XSS. So why not eliminate? If you aren't careful with your Markdown (or whatever markup language you choose) parsing, you can end up with XSS attacks in links. Most use cases only need
https protocol handlers in URLs, so you have an opportunity to exercise Principle Of Least Privilege there.
As for uploading images, this can be a bit more difficult. This is where the resources I mentioned come in. Do you have the ability to purchase a sandbox domain? If you do, in addition to processing images, you can serve them from a sandbox domain so that you limit exposure for XSS, RCE, and other attacks. A sandbox domain is one that removes content from the origin of the application. For example, www.google.com applications (Gmail, Google Docs) use googleusercontent.com as the sandbox domain. This domain's sole purpose is to serve user-provided content. Google cookies and DOM are not accessible because the origin is different. Further, this domain should serve content from a server that does not have the ability to do anything other than serve static content, so PHP code cannot be embedded in to the images, or a vulnerability in image processing would not impact your database and application code.
Image processing? Another best practice is to pass image uploads through image processing. If you only expect JPG and PNG for example, this is easy. You can resize an image 1:1, and it will fail on broken images, removing much of the XSS vectors and RCE vectors.
As you've no doubt noticed, my response has been about layering defenses. When you consume user data, there's no panacea. You have to weigh the risks, wanted features, and how to best limit the exposure of the features you want to give users.