Since I started building websites few years ago, I’ve created a few projects. Some of them never saw daylight, some of them were deleted upon creation, some of them still reside in my project archive. When I was trawling through the archive a few days ago, I came across one of them. It was a ticket system I first made back in April 2016 – the whole project was dubbed a “Secure Ticket System” so it instantly caught my attention. I just had to take a look..
Here’s how the system looks like:
As compared to other ticket systems, there are a few things that can be noticed:
- The system did not let the user choose a priority of how his ticket should be handled.
- A user was unable to choose a ticket category.
- Ticket submission file (Index.php) – a file which let the user submit a ticket.
- Ticket response file (Respond.php) – a file which allowed the administrators submit a response to a ticket in question.
- A file which displayed the contents of tickets (Tickets.php).
- Ticket management file (View.php) – a file which allowed administrators to view information about submitted tickets: it displayed the ticket ID, the ticket name, the name and email of the person who submitted the ticket, displayed the ticket status and allowed administrators to click a button to respond to the ticket.
The system did not use any relational (MySQL) or non-relational (NoSQL) database – as I wanted to try something new at the time, I’ve made it so that the entire system would be based on a flat file database.
I’ll try to make an in-depth analysis of the security of each of the scripts beginning from the top and moving towards the bottom.
The submission process was relatively simple:
- After the “Send” button would be clicked, the system would filter all of the input fields using htmlentities() to ensure any and all XSS attack attempts would be halted.
- If the length of the subject is not greater than 20 characters and file with the same ID did not exist in the ticket archive, the ticket would be flagged as “Open” and submitted. A text file in the “tickets” directory would be created that would contain the contents of the ticket.
- The ticket then would also be submitted to View.php which allowed administrators to respond to it.
- Finally, a message would be displayed – if the submission process was successful, it would say ‘Ticket submitted successfully.” If it was not, the message would say “Ticket failed to submit.”
When the ticket would be submitted, it would be flagged as “Open” and thus, displayed on the viewing page:
The View.php script would scan the “tickets” directory, get the total count of files which then would be displayed. When the ticket would be submitted and flagged as “Open”, the Index.php script would also write some of the ticket contents to View.php. The script would also allow people who are privileged to view it to respond to the ticket in question:
If the “Close this ticket” checkmark was not checked, the response would be added to the ticket – if it was checked, the ticket would be deleted. This is another downside because with the majority of ticket systems on the web today, when the ticket is closed, a person has a chance to re-open it. My ticket system did not include this feature.
The security of the system was not as bad as I expected it to be: the input of the user was sanitized and thus, it could not trigger an XSS attack, the “tickets” directory was protected by .htaccess. That being said, there were some caveats such as this one:
Wait – is that an SQL error? Yes.
The system operated on a flat file basis, but we still got an SQL error. How? The answer is very simple – it was intentional. As my Web Application Firewall’s SQL injection detection rules were still under heavy development at the time, I wanted to see how effective they were. With that out of the way, we should now probably look at the actual flaws that the system had. One of them was Cross-Site Request Forgery (CSRF):
Since I did not set a CSRF token to validate that the form sent from the browser matches a token on the server, a CSRF attack was possible. The system required the user to be logged in to access the ticket submission page, and, if the appropriate POST parameters could be provided, a CSRF attack could be triggered – an end user could be forced to execute unwanted actions: in this case, submit a ticket.
Spam all the things!
I failed to protect the form against spam too – this means that anyone could create an automated script to automatically submit tickets.
Now imagine that, for example, tickets #145, #148 and #157 would be created by a legitimate user requesting support, but tickets #146, #147 and #149-#156 would be created by an automated script to spam the system, how would the administrators remove the spam while leaving the legitimate tickets intact?
Since the system was based on a flat-file database, the administrators would have to accomplish this task manually – if the system had hundreds of tickets, the accomplishment of such a task would become almost impossible.
- The ticket system lacked a few important features – most notably, a ticket re-opening feature.
- The form was sufficiently protected against Cross-Site Scripting (XSS), but it was not protected against Cross-Site Request Forgery (CSRF) – I could have implemented CSRF tokens which would have gotten rid of this problem.
- The system was not protected against spam either – I could have implemented a (re)CAPTCHA which would have fixed this issue.