1

I'm currently writing an API in PHP which uses a JWT for authentication, and I'm running into a strange issue in my try-catch blocks. If I pass in no JWT or a malformed JWT with my HTTP request, my serverside code will throw an exception as expected and then execute code in the relevant catch block, sending back a 401: Access Denied response. However, the sever appears to then continue execution of the remaining code in the try block, executing a SQL query and sending the results as if nothing had happened.

Here's an excerpt of my code:

try {
    $decoded = JWT::decode($jwt, $access_key, array('HS256')); // Exception thrown here

    ...

    setResponseCode(200);
    echo json_encode($results);
} catch (Exception $e) {
    setResponseCode(401, "Access denied: " . $e->getMessage());
}

As far as I can tell, this is the order of events: First an exception is thrown at the first line in the try block. Then the code in the catch block executes. Then the remaining code in the try block executes.

This is obviously a huge security flaw, but I can't figure out why it might be happening. What exactly is going on?

EDIT: Additional context

This is the only code in the PHP file that sits above the try-catch block, and no code exists below it. I don't see how it would be likely to interfere with the try-catch block.

<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once 'config/core.php';
use Firebase\JWT\JWT;

foreach (getallheaders() as $name => $value) {
    if (strcmp($name, "Authorization") == 0) {
        $jwt = $value;
    }
}

And this is how the API is being called from Javascript:

axios
  .get(API_URL + "foo.php")
  .then((response) => {
    console.log(response.data.body);
  })
  .catch((error) => {
    console.log(error.response.statusText);
  });

Since no JWT is supplied, an error should be printing to the console. Instead, I get an automatic warning from the browser that my request received a 401 response, but then the object I requested prints out to the console as if I supplied correct authentication information.

Screenshot of Chrome console output

Sean
  • 11
  • 2
  • 1
    Continuing execution in the block after a `throw` is simply something that PHP _will not do_. The likelihood that this is a PHP bug is very low, and it's far more likely that there is a bug in the surrounding code. Additionally, remember that a catch block does not halt execution unless you put something in it that does. – Sammitch Aug 23 '21 at 22:13
  • Do you need an `exit();` afer you set the response code? With ajax and with setting a `Location: ` header, not including the `exit();` afterwards means things are overwritten and there are unexpected results (like other functions returning values that they shouldn't be). – AaronJ Aug 23 '21 at 22:23
  • When you catch an exception, you stop the automatic termination, you are basically saying I can handle this situation. So you have to handle it. If you want the code (after the `catch` )not to run, you need an exit in the `catch` – RiggsFolly Aug 23 '21 at 22:27
  • @Sammitch @RiggsFolly There is no additional code after the `catch`, so nothing else should be happening afterwards. I've added some additional context to the original post that might be helpful. – Sean Aug 23 '21 at 22:39
  • @AaronJ I'm not using a location header, I'm using axios instead of ajax, and there's no code remaining after the catch block. Would it still be necessary to include an `exit();` statement? – Sean Aug 23 '21 at 22:41
  • @Sean I'm not sure, but you are setting a header. What happens when you try it? – AaronJ Aug 23 '21 at 22:56
  • @AaronJ no change, still getting the buggy behavior – Sean Aug 23 '21 at 23:24
  • Fro the screenshot we can't tell where the data that's being output is coming from. Maybe the variable that has data has already been set before the 401 message. – AaronJ Aug 24 '21 at 17:09

0 Answers0