1

I am new to XPATH and PHP. I am trying to achieve a user validation with the help of XPATH in PHP. My data is in xml file

<?xml version="1.0" encoding="UTF-8"?>

<customers>
    <customer>
        <id>1</id>
        <fName>sa</fName>
        <lastName>aa</lastName>
        <email>[email protected]</email>
        <password>a</password>
    </customer> 
    <customer>
        <id>2</id>
        <fName>bb</fName>
        <lastName>cc</lastName>
        <email>[email protected]</email>
        <password>b</password>
    </customer>
</customers>

My php code snippet is

if(file_exists('customer.xml'))
        {
            $doc = new DOMDocument();
            $doc->load('customer.xml');

            $xpathvar = new Domxpath($doc);

            //check if user exists and password matches
            $queryResult = $xpathvar->query("customers/customer[email= '".$userEmail."' and password= '".$password."']");

            var_dump($queryResult);
            if(count($queryResult)== 1)
            {
                //successful login
                echo "great";
            }
            else
            {
                echo "Invalid email address or password";
            }
        }

No matter what input I provide, count($queryResult) returns 1. Not sure why the correct matching is not done.

When I provide $userEmail="[email protected]" and the $password="a" the var_dump($queryResult); gives the following output

object(DOMNodeList)[3]
  public 'length' => int 0
2
  • its a typo. Corrected, thanks. Does it make any difference in using DomDocument or SimpleXML? I am not sure, I have used what I found first. Commented May 15, 2016 at 15:39
  • i've updated the answer Commented May 15, 2016 at 15:55

3 Answers 3

1

$queryResult is DOMNodeList object which property length is amount of found nodes. Test result using :

if ($queryResult->length == 1)

And change your Xpath because it don't see the root eelement:

customer[email= '".$userEmail."' and password= '".$password."']

see demo

Sign up to request clarification or add additional context in comments.

Comments

0

Change the code snippet to this

if(file_exists('customer.xml'))
{
    $doc = new DOMDocument();
    $doc->load('customer.xml');

    $xpathvar = new Domxpath($doc);

    //check if user exists and password matches
    $queryResult = $xpathvar->query("/customers/customer[email= '".$userEmail."' and password= '".$password."']");

    if($queryResult->length == 1)
    {
        //successful login
        echo "great";
    }
    else
    {
        echo "Invalid email address or password";
    }
}

Where

$queryResult->length is the number of all nodes that satisfy to the query

also put /-Slash on the begining of the query

$queryResult = $xpathvar->query("/customers/customer[email= '".$userEmail."' and password= '".$password."']");

Comments

0

You should not store the password itself in the XML, but a hash. So to fetch the password from the XML file you use an expression, but verify it using a PHP function.

You can generate the hash using the password_hash() method:

var_dump(password_hash('a', PASSWORD_DEFAULT));

Output:

string(60) "$2y$10$o.aLdT4.xF6DaSaZAE4/8./omHnN5p3hBpfgzxSRwnXwdcfR27ova"

DOMXpath::evaluate() supports Xpath expressions that return scalar values. So it is possible to cast the node list into a string directly. It will return an empty string if the node list was empty.

$userEmail = '[email protected]';
$password = 'a';

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

$userPasswordHash = $xpath->evaluate(
  sprintf(
    'string(/customers/customer[email = %s]/password)',
    quoteXpathLiteral($userEmail)
  )
);

$isValid = (
  $userPasswordHash  != '' &&   
  password_verify($password, $userPasswordHash)
);

var_dump($isValid);

You might notice that I used a function to quote the $userEmail value. This avoids an Xpath injection. Imagine someone provides a $userEmail value like "" or true(). Xpath 1.0 has no escaping it just disallows single quotes in double quoted strings and double quotes in single quoted strings. Here is the function.

public function quoteXpathLiteral($string) {
  $string = str_replace("\x00", '', $string);
  $hasSingleQuote = FALSE !== strpos($string, "'");
  if ($hasSingleQuote) {
    $hasDoubleQuote = FALSE !== strpos($string, '"');
    if ($hasDoubleQuote) {
      $result = '';
      preg_match_all('("[^\']*|[^"]+)', $string, $matches);
      foreach ($matches[0] as $part) {
        $quoteChar = (substr($part, 0, 1) == '"') ? "'" : '"';
        $result .= ", ".$quoteChar.$part.$quoteChar;
      }
      return 'concat('.substr($result, 2).')';
    } else {
      return '"'.$string.'"';
    }
  } else {
    return "'".$string."'";
  }
}

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.