It should be well understood that you should never trust user input in your application. As an application developer, I always try my hardest to enforce very strict rules when accepting and outputting user inputted data. You can never really be too careful when it comes to handling data that you do not know (or can’t trust) the source of. Fortunately, PeopleCode is very robust in terms of providing built-in functions to safely handle the input and output of data. I would like to demonstrate an example of how a malicious user can execute a stored cross-site scripting attack on an insecure custom application within my PeopleSoft system. I will then show how to mitigate this attack by hardening the security of my custom application with a built-in PeopleCode function.
The Custom Application:
The custom application is very simple. All it does is say hello to the user. The code gets the user’s preferred first name from the database and outputs “Hello <name>!”. The text is outputted in an HTML area on the page so that that the text can be made bold and blue. Here is the page activate PeopleCode:
And here is an example of the output for a user named John:
The Malicious User:
Let’s suppose John is a malicious user and he notices that the application is displaying his name to him. He wants to attempt a stored cross-site scripting attack on the PeopleSoft application and he wants to use his first name as the driver of his payload. John will simply use self-service to change his preferred name.
He will input a script as his preferred first name.
And then the application will save his input.
The Bad Data:
In this example, a non-malicious script was injected into the application. The script will just output an alert box with the text “123”. It should be clear that this script could’ve just as easily been crafted in a way to cause harm. Regardless, the script is now stored in the database at this point.
This is half of the battle when it comes to performing a stored cross-site scripting attack. Since the script is now in the database, there is the possibility of the script being executed in the application if proper measures are not taken when outputting the data. It is also worth noting that if this data gets fed to third-party systems, then the third-party systems will be at risk as well.
Executing the Script:
At this point, John will navigate back to the custom application that outputs his name to see if his stored script will execute. To his delight, he found that the script executes as soon as the page loads.
It is obvious that this example is not very threatening because the script will only run for John and it is just outputting “123”. Anytime someone else navigates to this page, they will see their own name and not John’s name. Therefore, the script shouldn’t get executed on other users. To make this more interesting, John could’ve set PeopleSoft meta-HTML in his name to try to extract information from the system, but that will not be demonstrated here.
Securing the Custom Application:
Now I will demonstrate how this type of attack can be mitigated with the use of the delivered EscapeHTML function in PeopleCode. When outputting the name extracted from the database, the EscapeHTML function can be used to prevent the string from being interpreted as actual HTML in the HTML area that is displaying it.
This will result in the following output when a script (or any other HTML) is stored in the name field.
I realize that this was a brutally oversimplified example to demonstrate the idea of not trusting user inputted data. However, it has been my experience that safely handling user inputted data is not exactly a common practice for some application developers. Much like the EscapeHTML function, PeopleCode offers many great delivered functions to help protect our applications. No matter how overkill it may seem, we should always use these provided functions when developing custom applications. Another takeaway is that you should never assume that data has been pre-sanitized. The original custom application code wrongfully assumed that the preferred names were being stored in a safe state. This was unfortunately not the case, and this assumption is what ultimately opened up the application to the attack.