0

The Objective

To fetch the data from the XML "database" as a simple (and I know, very insecure) way of verifying login data.

The code here is that responsible for the database and for retrieving the values. Option one is what I originally tried, so it still contains the code which cross-references the input data of the username and password with the data in the XML database. Option two is where I tried a different method. Neither seem to be working. Screenshots attached.

Option One:

login.php:

<?php
if( !isset($_SESSION['user']) ) { ?>
<div>Please login to view your message log, and see whether anyone has left you a message.</div>
<form action="php/authenticate.php" method="POST" >
    <input type="text" name="Username" />
    <input type="password" name="Password" />
    <input type="submit" value="Login" />
</form>
<?php };
$passwordValid = $_GET['error'];
if( $passwordValid == 1 ) {
    echo "Falsches Password";
};
?>

authenticate.php:

<?php
session_start();

    // Load Credential Database
    $xml = new DOMDocument();
    $xml->load('../logins.xml');
    $xpath = new DOMXPath($xml);

    // Variable Declarations
    $user = $_POST['Username'];
    $password = $_POST['Password'];

    // XPath Query for Correct Credential
    $queryUsers = '//authentication/client/user[. = "' . $user . '"]';
    $userActual1 = $xpath->query($queryUsers);
    $userActual = $userActual1->nodeValue; // Output: Anton
    $passwordActual1 = $userActual1 . 'following-sibling::*[1]';
    $passwordActual = $xpath->query($passwordActual1); // Output: damn

    // Authentication Checker
    if($user == 'Arend' && $password == 'damn') {
        $userLogin = true;
        $_SESSION['user'] = $user;
        header('Location: ../index.php');
    } else {
        $userLogin = false;
        header('Location: ../index.php?error=1');

    };

    if($userLogin == true) {
        header('Location: ../index.php');
    };
?>

XML "logins.xml":

<?xml version="1.0" encoding="UTF-8"?>
<authentication>
    <client>
        <user>Arend</user>
        <password>damn</password>
    </client>
    <client>
        <user>Felipe</user>
        <password>damned</password>
    </client>
    <client>
        <user>Paula</user>
        <password>damnest</password>
    </client>
</authentication>

Option Two:

XML code "test.xml":

<?xml version="1.0" encoding="UTF-8"?>
<authentication>
    <user name="Arend">
    </user>
        <password>damn</password>
    <user name="Paula">
    </user>
        <password>damnest</password>
</authentication>

php code:

<?php
    $xml = simplexml_load_file('test.xml');
    $nodes = $xml->xpath('//authentication/user[@name="Arend"]');

    // Variable Declarations
    $user = "Arend";
    $password = "damn";

    echo 'second test' . '<br>';
    print_r ($nodes);
    echo '<br>';
    print_r ($nodes[0]['name']);
    echo '<br>';
    echo $nodes[0]['name'];
?>

Result:

As you can see, it grabs the XML data and the array and how it is structured cen be viewed in the screenshot. I am able to get the username, but if I try to specify the password (echo $nodes[0]['password'];) as the thing I want, it comes up with the second image below:

Saborknight
  • 164
  • 2
  • 9
  • I'm just going to take this opportunity to point out that this is in fact NOT secure and is in fact downright neglectful of your responsibilities as a system designer. You should NEVER save plain text passwords. Look into bcrypt, especially if you're going to be saving them out in the open like that. – MiDri Nov 08 '15 at 04:46
  • Agree with MiDri. With his points in mind, I don't think your XML is in the best format for this. The password nodes should be children of the users, or possibly attributes instead, e.g. `asdf` or `` – rjdown Nov 08 '15 at 05:05
  • Option 2 is definitely ill-advised (ignoring that the entire logins.xml is a bad idea) due to them not having a shared parent. Passwords and users should be grouped somehow. So I want to say Option 1 is the only worthwhile option when doing this. – JPMC Nov 08 '15 at 05:16

2 Answers2

2

Don't store plain text passwords, use password_hash() to create hashes and password_verify() to verify them.

With DOMXpath::evaluate() you can fetch the password for a user directly:

$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<authentication>
    <client>
        <user>Arend</user>
        <password>$2y$10$WjYhm/JoG3Pn.nT7C91cweOCrshRSZOgYFoYvrG8Ry1SS/tohGfA.</password>
    </client>
    <client>
        <user>Felipe</user>
        <password>$2y$10$PlUKayvRmTy61sFMtAhlxePKGAkTL8LU84gvP1EjUn/3e0E2jHMgi</password>
    </client>
    <client>
        <user>Paula</user>
        <password>$2y$10$3XDNia4TqVJ5QlqH/zoX/e9.t7YJF5K1hNVZnnuoslI/hjvv0St1W</password>
    </client>
</authentication>
XML;

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

$passwordHash = $xpath->evaluate(
  'string(/authentication/client[user = "Arend"]/password)'
);

var_dump(password_verify('damn', $passwordHash));

Output:

bool(true)
ThW
  • 19,120
  • 3
  • 22
  • 44
  • huh, that is handy! Didn't know about `evaluate`! Though you can just use that XPath in query or simpleXML and cast the element to a string to get the same thing. In either case, I want to raise awareness that this prevents `$password` from being an injection vector, but `$name` would need to be sanitized still. But your solution is still better from a security standpoint. +1 – JPMC Nov 08 '15 at 17:04
  • 1
    True, sanitize or quote the values to avoid Xpath injection: http://stackoverflow.com/a/27440520/2265374 – ThW Nov 09 '15 at 07:46
  • That is a good link. I'm probably going to look deeper into santization. You think either of us should add santization to our answers? Or just leave the functional results and acknowledge what the flaws are without going off topic to fix them? – JPMC Nov 09 '15 at 08:33
1

You acknowledge that this is insecure, so I won't badger or comment anymore on that.

As for using logins.xml, as in your first example, I had a proper XPath and solution for it. I used simpleXML though, which comes with PHP.

//load credentials
$xml = simplexml_load_file(__DIR__.'/logins.xml');
// Variable Declarations
$user = $_POST['Username'];
$password = $_POST['Password'];
$search = $xml->xpath("//authentication/client[./user = '{$user}'][./password = '{$password}']");

if(count($search))
{
    //authenticated!
}
else
{
    //DENIED
}

It does a match of a <client> node having BOTH a child <user> equal to $user and a <password> equal to $password, not either-or, but both.

count($search) returns 0 (false) if there are none, and then anything else is cast to true, which means they exist in your dataset.

  • Disclaimer, I used __DIR__ because the XML and PHP were in the same folder for me when I tested my XPath expression. I want to say NEVER have your sensitive credentials within the web root, or somewhere not protected by some form of HTAccess or other restriction.

For using DOMDocument and DOMXPath, they have instead of a countable set a length parameter. You instead would have this for checking IF/ELSE

$xmlpath= $xpath->query("//authentication/client[./user = '{$user}'][./password = '{$password}']");
if($xmlpath->length > 0)
{
    //authenticate
}
else
{
    //not
}

One final security warning, you use direct $_POST variables, and I want to add awareness that there is such a thing as XPath Injection, much like SQL injection. Your function isn't used to display data, so it wouldn't be leaking information from the document. BUT they could use it to make a query that always returns true and lets them log in.

See for examples, including login credentials in XML like yours!: https://www.owasp.org/index.php/XPATH_Injection

JPMC
  • 1,043
  • 1
  • 13
  • 16
  • The first solution worked perfectly thanks, I'm reading into the OWASP page about injection (which inevitably lead to many more pages about injection) so thanks a ton for both, I'll try the second solution in a bit. – Saborknight Nov 08 '15 at 16:28