0
\$\begingroup\$

I would very much appreciate it if someone could review the php script below for any security risks.

I have a live website using shared hosting. There's a page that accepts text submissions from users. The message is sent to a phpmyadmin database table; nothing is printed immediately to HTML. The messages are stored in the table for my review, and I manually put the text onto the page if I choose to.

I spent several months monitoring a user who would submit <script>alert('xssvuln');</script> every Monday morning around 6AM. The JavaScript alert has never shown up on the page itself, but it is instead stored in the table as a regular string. While I'm confident that there are no xss attack vulnerabilities in my scripts, I have disabled the input form for the time being. I assume the user ip is spoofed and it is automatically making these submissions. What purpose does this repetition serve if the alert is not injected to the html? Is there anything I'm missing?

About the PHP Script...

Along with the message, I also store the user's IPv4 address and the date-time that the submission was made. This is for archival purposes. I prefer the yyyymmdd-hhmm format, which is why I have it done automatically in the script. I'm aware that htmlspecialchars() is only effective for when you are printing directly to HTML. The same can be said for the trimming of whitespace. The filter_var() string sanitization is included just to be safe. While mutating the script like this is not preferred, I would like to for cosmetic reasons. I'm primarily concerned about whether including these things in the script opens it up for any type of attack.

Table Variables:

  • id, int(11), AI
  • IPv4, varbinary(34)
  • datetime, varchar(18)
  • message, text

HTML

<form action="script.php" method="POST" autocomplete="off">
    <textarea placeholder="*Type here" type="text" maxlength="20000" required wrap="soft" name="message" autocomplete="off"></textarea><br/>
    <br/>
    <div>
        <button id="button02" type="submit" value="Send" style="cursor: pointer;">Send</button>
    </div>
</form>

PHP

<?php
//1---DATABASE CONNECTION---
$bHost = "localhost";
$bName   = "x";
$bUser = "x";
$bPassword = "x";
$bport = "x";
$bcharset = "x";

$boptions = [
    \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
    \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
    \PDO::ATTR_EMULATE_PREPARES   => false,
];

$bdsn = "mysql:host=$dbHost;dbname=$bName;charset=$bcharset;port=$bport";
try {
     $bpdo = new \PDO($bdsn, $bUser, $bPassword, $boptions);
} catch (\PDOException $b) {
     throw new \PDOException($b->getMessage(), (int)$b->getCode());
}
//1---END---

//2---ADD INPUT TO DATABASE---

date_default_timezone_set('America/Los_Angeles'); //set timezone
$datetime = date('Ymd-Hi', strtotime('NOW')); //date and time variable

$userIP4 = $_SERVER['REMOTE_ADDR']; //ipv4 address variable

if($_SERVER['REQUEST_METHOD'] == 'POST') {
    $Tmessage = trim(preg_replace(['/^\s+|\s+$/u', '/\s{2,}/u'], ['', ' '], htmlspecialchars($_POST["message"]))); //trim whitespace from message
    $sanitizedMessage = filter_var($Tmessage, FILTER_SANITIZE_STRING);

    //prepare info and submit into table
    $statmnt = $bpdo->prepare("INSERT INTO table (IP4, datetime, message) VALUES (:IP4, :datetime, :message)");
    $statmnt->execute(['IP4' => $userIP4, 'datetime' => $datetime, 'message' => $sanitizedMessage]);
    header("Location: success.html");
    exit (0);
}
header("Location: failure.html");
exit (0);
//2---END---
?>

Please let me know in the comments if there is any further information I can provide.

Edit: Please see the comments below before submitting an answer. While I've made posts about this subject before, the answers that I received on them derailed from the original inquiry. I understand that this is not the way to do things, but I would prefer to unless this opens up a security risk. If this is the case, please let me know. Otherwise, I would appreciate an answer regarding the <script>alert('xssvuln');</script> submissions that I was receiving.

\$\endgroup\$
10
  • \$\begingroup\$ I've always leaned to the idea of letting the raw data be raw data, and to ensure that the presentation filters according to the medium used (therefore, applying htmlspecialchars() when getting the data to present as html). This requires a CRUD object that handles all things going in and out of the database, so you have a single place to apply htmlspecialchars(). But, perhaps it's better to prevent junk from getting into the database. Hoping to see some others weigh in on this. \$\endgroup\$
    – Tim Morton
    Commented Dec 29, 2020 at 20:26
  • \$\begingroup\$ wait...they already have weighed in, hence your comment about htmlentities and trim. What purpose does this repetition serve...? It’s probably a bot, probing for unfiltered response, just patiently waiting for you to stop paying attention. It’s also probably waiting in vain... especially if you’re not confirming submitted data by repeating it back. \$\endgroup\$
    – Tim Morton
    Commented Dec 29, 2020 at 21:28
  • 2
    \$\begingroup\$ The fact that someone is trying a vulnerability doesn't mean there is one. Of course, there might be one. But you shouldn't assume there is one just because someone is trying to exploit it. Literally every server on the internet gets these "attacks" - hackers have automatic programs that try to exploit everything they can think of. \$\endgroup\$
    – user20574
    Commented Dec 30, 2020 at 18:16
  • 1
    \$\begingroup\$ @CodeLoveGuy I was saying the “attack” is probably coming from a bot. It’s going to keep probing, but you passed the test, and it learned nothing. In addition, you’re manually screening submissions. Are you paranoid enough? Yeah, I think so! \$\endgroup\$
    – Tim Morton
    Commented Dec 30, 2020 at 19:22
  • 1
    \$\begingroup\$ @Cod please revisit my review on your earlier question (mentioned by Tim in the secons comment here). You will notice that the /^\s+|\s+$/u pattern that I recommended ONLY removes multibyte whitespace characters from the start and end of the string. I think you misunderstood what it does. This is why I did not use trim() in my suggested snippet. ^ means the start of the string and $ means the end of the string. \$\endgroup\$ Commented Dec 30, 2020 at 21:13

1 Answer 1

1
\$\begingroup\$

It is commendable to be concerned about security. But don’t let the fear of the unknown cripple you.

The attack

First, hackers typically go for the low-hanging fruit. They will use known exploits and search for code that is vulnerable. They’re looking for careless mistakes.

This particular one is probing to see if they can inject javascript and have it executed. For example, this code would be vulnerable:

if($_POST['message']) {
    $message = $_POST['message'];
    $db->query("insert into table (message) values('$message')");
    echo "You have submitted: $message";
}

While this code actually has several vulnerabilities (a notice might be generated for missing key, not using prepared statement, and unsanitized echo), the only one being targeted (at this point) is the unsanitized echo.

The probe would be considered successful if an alert popped up in the echoed message. The payload is negligible, except that it serves as a signal that more devious things are possible.

Danger to your code from this probe

Practically none. You are using prepared statements, and don’t even appear to be echoing the message back to the user. Furthermore, you’re even applying htmlentities before saving to the database, and htmlentities is probably sufficient to stop it in its tracks. And if that weren’t enough, you’re approving each one and manually pasting what is shown.

Validation vs Sanitation

As you’ve been told before, you should validate input and sanitize output. There is absolutely no danger to your database by applying htmlentities before you save the data. The database doesn’t care; it’s just a string.

So what’s the issue? Why does everyone object to it?

  • Your data is not being stored accurately. This would completely mess up binary and xml data.
  • This discourages using htmlentities when showing content. (The browser would show the htmlentity code, not the actual entity). So in practice, it discourages best practice.
  • Back in the php3 era, before prepared statements were available, you had to addslash data before saving it, then stripslash it before presenting it. Sometimes it would accidentally get addslashed twice (magic quotes) and then you had a real mess on your hands. The validate/sanitize advice comes from a long history of learning from hard knocks.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.