The other day, I found myself debugging an issue reported by one of our clients. They noticed that upon submitting a form, the application returned an empty page with a “403 Forbidden” message. I quickly turned to the application logs in search of an answer. To my surprise, there were no errors or exceptions related to the client’s issue.
Determined to find the problem, I traced the client’s steps to replicate the issue in my local development environment. Unfortunately, this showed no results. Even when using the data submitted by the client, the application processed the request without any issues.
Suspecting a network-related problem, I considered possibilities such as the load balancer or a misconfiguration in Nginx. The fact that I couldn’t replicate the issue locally seemed to support this theory. However, I wasn’t ready to give up. I decided to try reproducing the issue on one of our sandbox environments.
It didn’t take long before I encountered the same “403 Forbidden” error on the sandbox. However, the situation remained unclear. Although I managed to replicate the issue with the client’s data, other strings submitted in the form resulted in successful submissions. It was only when I considered that something specific in the client’s data was triggering the error that things began to make sense.
Taking a closer look in our form handler, we used html_escape
to sanitise data in the request before inject it.
This method will escape HTML entities, which will help prevent any potential XSS (Cross-Site Scripting) attacks when
rendering user-provided content in views. Here’s how you can use html_escape
to escape user input:
# Assuming user_input contains the user-provided string
escaped_input = ERB::Util.html_escape(user_input)
it does that by escaping HTML tag characters. However, this is not the case with shell commands. When our client
submitted their form, a long string was part of the data submission which included a shell command system()
. And
that was the cause of the issue. The solution is simple, using the Shellwords
module, which provides methods for
escaping shell commands. Here’s an example of how to use Shellwords.escape
:
require 'shellwords'
# Assuming user_input contains the user-provided string
escaped_input = Shellwords.escape(user_input)
By using Shellwords.escape
, you ensure that any special characters in the user input are properly escaped, reducing
the risk of command injection vulnerabilities. Always sanitize and validate user input before using it in shell commands to
maintain the security of your application.