COMMUNITY

[Solved] The most secure way of operating with PHP

haxe-php

(Luke) #1

I am curious how best to use PHP in an AJAX setting. A lot of websites use AJAX to make it feel as if you are still on the same page, like in single-page web applications, and sending POST data to a php file on the server that generates data that is returned back to JavaScript context. I believe this is how RESTful APIs work.

What is the best way to do this in Haxe-generated php code?

Since Haxe generates an index.php file which sets up the environment then calls Main::main(), the POST data by that point is no longer valid as PHP is stateless (it doesn’t share the POST data with other PHP files), and there doesn’t seem to be a way to pass that onto the Main constructor call through which you can handle such requests.

Does the Haxe PHP target only work if you only use it to generate HTML5 pages, or is it indeed possible to use it with AJAX in JavaScript context? The only workarounds I have found are very insecure, which either involves using Cookies (which I would rather avoid) or sessions. Is there any other way I can use the Haxe PHP target for AJAX calls? Is it possible, perhaps, to generate PHP code that is procedural and not object-oriented where these problems no longer arise?


(Allan Dowdeswell) #2

In the .hxml file you can set up multiple entry points by giving different -main and --php-front directives. An example is here: https://github.com/ConfidantCommunications/JoomlaHaxe/blob/master/php.hxml

You would want to set up both a Client and an API to call. The tink library seems to be popular nowadays, and there’s some good docs here:
https://haxetink.github.io/tink_web/

Does that help?


(Alexander Blum) #3

You can use Ajax just fine with the PHP target. Simply because there is just one entrypoint index.php, doesn’t mean you cannot use Ajax or Rest or whatever. Granted for a Rest interface you need a .htaccess file so everything points to your index.php, but if you look at popular PHP based CMS like Joomla and Wordpress, they all use the same trick - and one single entrypoint.

How it usually works:

  1. your index.php calls your Main::main() function
  2. which instantiates your Main class
  3. your Main constructor sets up logging, starts a session, initialises your data structures, opens a database connection, etc. - whatever your application needs
  4. it also invokes some kind of a routing mechanism which can be as simple as a switch / case statement, e.g.:
var route:String = php.Web.getParams().get("route");
switch (route) {
    case "home": return new HomePage();
    case "contact": return new ContactPage();
    default: return new ErrorPage();
}
  1. HomePage, ContactPage or whatever you call your modules / request handlers, can then deal with your specific request. You can either use php.Web to get GET and POST parameters or access PHP’s global _GET or _POST arrays directly.
    If e.g. your ContactPage receives a POST request it ‘knows’ that your user has filled out a contact form and submitted it, so it can look for required fields, validate data and send it on to a database or mail server or generate an error message.

You can use Cookies if you want, but there is no real requirement for them. If your application can live without, you don’t have to use them. If your application requires some kind of authentication, then Cookies is the obvious choice for session management especially when crossing page boundries. If your application only uses Ajax, then you can skip Cookies and send some security or auth tokens instead. It works just like it would in plain PHP.

I’m not sure I can follow, when you say procedural PHP code doesn’t have these problems that you see in object oriented code. Yes, control flow is a bit different, and maybe hard to follow with OOP, but what your code does with a request and what it sends back doesn’t really change. It’s just that your statements are spread over a wider range of files and they are a bit more compartmentalised (and sometimes cleaner).


(Luke) #4

Thanks, but I’m not sure I quite get it.

I’ve been playing around, and testing with the following entry point in PHP context:

import php.Web;
import php.Lib;

class Main
{
    
    static function main()
    {
        var random = Web.getParamValues("random");
        
        Lib.print(random);
    }
    
}

So, in JavaScript context I have the following XMLHttpRequest being sent to the server:

static function requestData(data:String, cb:String -> Void)
    {
        var xml = new XMLHttpRequest();
        xml.onreadystatechange = function()
        {
            if (xml.status == 200 && xml.readyState == 4)
            {
                cb(xml.responseText);
            }
        };
        
        xml.open("GET", "includes/index.php");
        xml.send(data);
    }

and later I have the following call to the above:

requestData("random=50", function(value:String)
{
      trace(value);          
});

But the value I get back is not 50, which is what I would expect, it is null.

So, I’m not sure I quite understand your logic. The reason I mention procedural is if index.php was generated such that all our entry-point code was generated there and called specific classes as and when, it would allow GET and POST to function as normal instead of trying out some workaround.

I tried the --php-front mechanism but all that does is generate the same code as index.php, so I’m not sure why I would want to use a different name that does the exact same thing as before.

I could use Cookies, but that is very insecure. If a background script knows the cookie variables being sent to the server, even if short-lived, usernames and passwords sent to the server this way is very compromising.


(Alexander Blum) #5

First of all you need to use Web.getParams().get("random");, getParamValues only works on arrays like random[]=50.

I still don’t understand where you see a workaround when using OOP in comparison to procedural.
In a procdural index.php you would have something like:

function route() {
    switch ($_GET["action"]) {
        case "home":
            printHome();
            break;
        case "random":
            printRandom();
            break;
        :
        :
    }
}
function printRandom () {
    echo $_GET["random"];
}

route();

In a OOP Haxe version you might have:

class Main {
    public function new() {
        route();
    }
    function route() {
        switch (Web.getParams().get("action")) {
            case "home":
                new HomePage();
            case "random":
                new Random();
            :
            :
        }
    }
    public static function main() {
        new Main();
    }
}

class Random {
    public function new() {
        Lib.print(Web.getParams().get("random"));
    }
}

Both perform the same steps and produce the same results, it’s just split into different bits. Some of those bits may happen to be in different files, but you can split up procedural code just as well. So there is no workaround, it’s just a different way to look at a problem.
(Caution: my code is simplified and doesn’t do proper checks and validation to account for errors)

I don’t know your threat model, but cookies should not be used to store or transmit usernames and passwords. They usually hold a session id, that identifies a single session on the serverside. If someone gets hold of your cookie, they could technically take over your session, but that’s why you use HTTPS. If you have malicious scripts running clientside, able to read cookies or intercept communication, then I am not sure what technical countermeasure would help you there.


#6

Just to be sure … did you test index.php in browser? php server is running? why ‘includes’ in request? …

passwords are usually send only once during login process, they aren’t stored in cookies … shouldn’t be stored at all - they’re hashed/encoded and stored in encrypted form, on server only - login process (if hash match stored) returns token or session id whitch is used to recognize subsequent calls from the same user - it can be stored, it’s not unsafe as its validity quickly expires


(Luke) #7

The idea to use AJAX is to request data from a php file on request, so instead of loading it as a page that displays on-screen, I send the request with whatever data I am asking for and get back a result which I would later parse into HTML (likely using Markdown). I haven’t got that far yet, because I am still testing and figuring out why things aren’t working as expected.

@ablum I think the reason why it isn’t working like in your examples is because as mentioned above, I am requesting data from the server using XMLHttpRequest (AJAX), and not loading the PHP page directly from the server on-screen.

I tried Web.getParams().get("random"); but I am getting the same result as before.

As I mentioned in my OP:

Since Haxe generates an index.php file which sets up the environment then calls Main::main(), the POST data by that point is no longer valid as PHP is stateless (it doesn’t share the POST data with other PHP files), and there doesn’t seem to be a way to pass that onto the Main constructor call through which you can handle such requests.

The only workaround I can think of is to manually edit index.php to add a mechanism that shares the request with the Main class, so that by the time it reaches that point it still has the data and knows it exists.

I’m getting back null in the console view in the web browser likely because the $_GET["random"] no longer exists at that point. Just goes to show that PHP is difficult to work with.

I will keep trying to find a solution, but I don’t see one that is immediately obvious.


(Alexander Blum) #8

Whether you use Ajax or not doesn’t matter. The script gets called, processes the request and sends an answer. Of course you have to figure out what kind of a request you have to handle. So if you have a contact form, then you would send out an empty form for a GET request and for a POST request, you would process form data and present a thank you page. It’s the same kind of distinction you would need when building a contactform.php with procedural PHP (unlesss of course you split it even further and have a contactform.php that only serves an empty form and a sendcontactform.php that only deals with posted contact data).
You might have to set your MIME-type when sending data back to your client (holds true for both plain PHP, as well as Haxe).
And don’t get hung up on the fact, that you don’t want to send plain HTML, but rather Markdown or XML data. In the end all of them are just text. Text that gets sent depending on whether you send a GET or POST request, or whether you send index.php?action=random&random=50 or index.php?module=random&random=50, etc.

I tried it myself - the server part not the client part, and I only used GET, but it should work for both. So maybe something else is wrong.

First of all the global arrays _GET and _POST are not lost after the first index.php. That’s an assumption that doesn’t hold up in practice. Go ahead and try it, even in plain PHP. You will see that you can access those values everywhere, as long as you are handling the same request. You can include and require as many files as you like from your initial entrypoint e.g. index.php, they can access all global variables, they can access the current session, there is no workaround needed.
However, if you start a second HTTP request from PHP, then that second request will only see what you send it, but not what your client sent. But that should be obvious.


#9

You’re probably thinking that I’m writing about some obvious, basic things … but it looks like you’re trying to run too much things at start … probably geting 2 things going wrong, 1 server side and 1 client side :wink:

  1. starting from first version … get server side running with getParams().get(“random”);

  2. check it with browser with index.php?random=50 1st FIXED
    No strange haxe loosing data, not calling/passing, not need sth … it just works.

  3. look at browser debugger, network, request, headers, parameters

  4. going to javascript parts - if earlier was no error - from code I assume index.php is placed under includes route

  5. compare JS-invoked request, headers, parameters
    Probably there will be a difference - NO PARAMETERS passed or sth passed but not recognized by server as parameters.

  6. insert index.php?random=50 in xml.open() and try again -2nd FIXED

GET parameters MUST be passed in url (REQUEST_URI in php) - send() parameter isn’t used.
Changing method to POST on the other side is not enough to be interpreted as php _POST[] variables - you HAVE TO xml.setRequestHeader("Content-Type", "multipart/form-data"); AND pass data in FormData object… without that you’re JUST sending string 'random=50' - php server won’t recognize this and won’t pass it to index.php - NO HAXE FAULT :wink:

Look at this haxe example

Probably it’s possible to parse it manually/custom way - you must only know how to get this, passed_whole_string. You can always insert phpinfo() in index.php to see what and how thing are passed.