#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2010
    Posts
    95
    Rep Power
    0

    Exclamation How to protect AJAX file on server that is called from API output?


    Hello,

    I am integrating my API's to other sites. To make it easier to understand: Let us say my main site which produces API's is www.main.com

    Another site (www.apiUser.com) will need to use my main site API. I am adding the below code in the index page of it (www.apiUser.com/index.php)

    $showApi = file('http://main/api.php?apiId=44');
    foreach ($showApi as $output){
    echo $output;
    }

    Now, on the html comes from the page: http://main/api.php, I trigger Javascript event to reach AJAX page on server which is : http://main/ajax.php

    Users will set values through the javascript when they click on the link, they will get directed to the ajax.php as below:

    button.onclick=function(){
    callAjax("http://main/ajax.php","x=5&y=9");
    };

    Now, the above function, will call the page: "ajax.php" as request: ajax.php?x=5&y=9

    My question! How can I protect the page ajax.php from browser bar setting values? I need users to only access the page through the javascript (html in the API html we got from the server). Someusers can enter the link in browser and set values in database as much as they need: http://main/ajax.php?x=10000&y=98989!! They will corrupt my database in this way. I even need to restrict the client of the API from direct access as well!! Just through the Javascript to be accessed!

    I tried to generate md5(uniqid()) and to add it to session in the output of api (inside http://main/api.php) which is viewed on the site: www.apiUser.com, but the problem the session does not work on the http://main/ajax.php It seems different sessions! I added this variable to hidden html input and wanted to compare between the session on ajax.php and the hidden input value but the sessoin did not work! I tried session_start() on the top of page http://main/ajax.php but does not work! Also added it on top of http://main/api.php!

    Anyhow! I need to find any way to protect unauthorized access to the page http://main/ajax.php! Specially we are getting API's from http://main/api.php and send it to client site (www.apiUser.com)... Using $_SERVER['http_referer'] might not work as I am not controlling inputs from the site www.apiUser.com

    Any good solution? Will be so thankful!



    Thanks a lot for your help! Appreciate.
  2. #2
  3. Transforming Moderator
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    14,113
    Rep Power
    9398
    1. Require credentials so you can track who is sending what
    2. Require a signature of the request: essentially a hash of some private information and the request itself
    3. Don't make people use the API client-side (eg, in AJAX) unless it's absolutely necessary. Protip: it probably isn't.
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2010
    Posts
    95
    Rep Power
    0
    Originally Posted by requinix
    1. Require credentials so you can track who is sending what
    2. Require a signature of the request: essentially a hash of some private information and the request itself
    3. Don't make people use the API client-side (eg, in AJAX) unless it's absolutely necessary. Protip: it probably isn't.
    Thanks for your response. Please could you clarify?

    1. Require credentials so you can track who is sending what

    My site does not apply user login (api clients login). Whenever any client generates new API to be pasted on his site, then a security number will be generated to access the API. So the AJAX calls will be without authorizations





    2. Require a signature of the request: essentially a hash of some private information and the request itself:::

    Could you please give me a simple example? I never did that. Will it solve the problem 100%?




    3. Don't make people use the API client-side (eg, in AJAX) unless it's absolutely necessary. Protip: it probably isn't.

    As I know, the only ways to submit requests to servers are the forms and the AJAX. Is there any alternate way? I am in need to send information from the www.userApi.com page to my ajax page (http://main/ajax.php).


    Thanks a lot.
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2010
    Posts
    95
    Rep Power
    0
    To make things easier to understand.

    Clients get tokens to request the API from the Server.

    Users world wide will access clients website API and click on a button located on the API HTML to go to my server AJAX page (http://main/ajax.php).

    So, just I need to protect my ajax.php page from clients and world wide users

    Thanks a lot.
  8. #5
  9. No Profile Picture
    Lost in code
    Devshed Supreme Being (6500+ posts)

    Join Date
    Dec 2004
    Posts
    8,317
    Rep Power
    7170
    It seems different sessions!
    Sessions use cookies to track the session ID, so they won't work across domains either.

    Using $_SERVER['http_referer']
    Using the http referer for authentication is never a good idea.

    I even need to restrict the client of the API from direct access as well!! Just through the Javascript to be accessed!
    This type of restriction is impossible to implement.

    As I know, the only ways to submit requests to servers are the forms and the AJAX. Is there any alternate way?
    No other way.

    3. Don't make people use the API client-side (eg, in AJAX) unless it's absolutely necessary. Protip: it probably isn't.
    Based on the OP's other recent posts, I think it probably is necessary in this case.

    2. Require a signature of the request: essentially a hash of some private information and the request itself
    This is probably what you need to do. This assumes that:
    * the values (ex: x and y) are being generated by one server (serverOrig)
    * the values are being sent from serverOrig to the client
    * the values are then being sent from the client to serverTerm (a second server)
    * you need serverTerm to be able to verify that the values are coming from serverOrig and were not changed by the user

    Start by defining a secret key. This needs to be shared on both servers. It should never be sent to a client.
    PHP Code:
    $secret_key "xckjpZXlkcjposSRA82i3jpaju0s98das90duskjkasDJPOAIdjkw"
    To "sign" a message, you compute a hash of the secret key plus the message. This is performed server-side on serverOrig.
    PHP Code:
    $message "x:1235;y:84534";
    $hash hash('sha256'$message $secret_key); 
    The message and hash are then sent to the client. Now, the client transmits both values to serverTerm:
    Code:
    http://serverTerm/api.php?hash=660b764423f30cb04ce334a0bb4fde3e1f0272d56231022249098ab9804d771f&message=x:1235;y:84534
    To validate the message, serverTerm re-computes the original hash using the secret key and compares it to the hash sent by the client:
    PHP Code:
    $message $_GET['message'];
    $hash $_GET['hash'];
    $check_hash hash('sha256'$message $secret_key);

    if(
    $hash === $check_hash) {
      die(
    "Message validated!");
    } else {
      die(
    "Message was modified");

    If the two hashes are equal, then you know that the user did not modify message at all.

    Obviously, you can sign and validate the API response in exactly the same manner as well.


    Note though that this does not prevent the user from sending the signed API request more than once if they want to; If you need to prevent that, then you need to add something called a nonce to it as well.
    Last edited by E-Oreo; October 30th, 2012 at 10:51 PM.
    PHP FAQ

    Originally Posted by Spad
    Ah USB, the only rectangular connector where you have to make 3 attempts before you get it the right way around
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2010
    Posts
    95
    Rep Power
    0
    Originally Posted by E-Oreo
    Sessions use cookies to track the session ID, so they won't work across domains either.


    Using the http referer for authentication is never a good idea.


    This type of restriction is impossible to implement.


    No other way.


    Based on the OP's other recent posts, I think it probably is necessary in this case.


    This is probably what you need to do. This assumes that:
    * the values (ex: x and y) are being generated by one server (serverOrig)
    * the values are being sent from serverOrig to the client
    * the values are then being sent from the client to serverTerm (a second server)
    * you need serverTerm to be able to verify that the values are coming from serverOrig and were not changed by the user

    Start by defining a secret key. This needs to be shared on both servers. It should never be sent to a client.
    PHP Code:
    $secret_key "xckjpZXlkcjposSRA82i3jpaju0s98das90duskjkasDJPOAIdjkw"
    To "sign" a message, you compute a hash of the secret key plus the message. This is performed server-side on serverOrig.
    PHP Code:
    $message "x:1235;y:84534";
    $hash hash('sha256'$message $secret_key); 
    The message and hash are then sent to the client. Now, the client transmits both values to serverTerm:
    Code:
    http://serverTerm/api.php?hash=660b764423f30cb04ce334a0bb4fde3e1f0272d56231022249098ab9804d771f&message=x:1235;y:84534
    To validate the message, serverTerm re-computes the original hash using the secret key and compares it to the hash sent by the client:
    PHP Code:
    $message $_GET['message'];
    $hash $_GET['hash'];
    $check_hash hash('sha256'$message $secret_key);

    if(
    $hash === $check_hash) {
      die(
    "Message validated!");
    } else {
      die(
    "Message was modified");

    If the two hashes are equal, then you know that the user did not modify message at all.

    Obviously, you can sign and validate the API response in exactly the same manner as well.


    Note though that this does not prevent the user from sending the signed API request more than once if they want to; If you need to prevent that, then you need to add something called a nonce to it as well.
    Dear E-Oreo,

    Thanks for your kind response and your effort.

    Actually I have voting system, I need users to use the (mouse click) to vote ONCE from each machine and the "Javascript" cookies will prevent them from voting same time to same option. I do not want users to use the browser address bar to vote there was no way to use the PHP cookies as we have different domains as you know!

    Your solution is working out in case of messaging system, but in case of voting system, users simply can enter in the browser this link: http://serverTerm/api.php?hash=660b764423f30cb04ce334a0bb4fde3e1f0272d56231022249098ab9804d771f&message=x:1235;y:84534 &votingOption=2,, as users will be able to see the hash when it is getting sent to html from server.

    Users can vote million times!! While each user has to vote 1 time from each machine (according to cookies).

    So how can I prevent from voting from the browser address bar based on what I mentioned before?

    Note: I am using anonymous voting, I do not have user accounts to get signed in into the system. Just cookies as an identifier for the person who votes. I have no problem if they delete their cookies and vote again! Just I do not need them to use the browser address bar and enter the AJAX link and vote!

    Thanks a lot.
  12. #7
  13. No Profile Picture
    Lost in code
    Devshed Supreme Being (6500+ posts)

    Join Date
    Dec 2004
    Posts
    8,317
    Rep Power
    7170
    As I mentioned, if you don't want the user to be able to re-send the API request more than once then you just need to implement a nonce.

    A nonce is simple a random value that you only allow to be used once. Used nonces are stored in a database table. The nonce is included in the hash for validation purposes.

    PHP Code:
    $secret_key "xckjpZXlkcjposSRA82i3jpaju0s98das90duskjkasDJPOAIdjkw";
    $nonce hash('sha256'crypt(microtime(true))); // this just generates a random string
    $message "x:1235;y:84534";
    $hash hash('sha256'$message $nonce $secret_key); 
    Then the AJAX request is sent to:
    PHP Code:
    "http://serverTerm/api.php?message={$message}&nonce={$nonce}&hash={$hash}
    Now on the check side, you just need to perform some additional database steps:
    PHP Code:
    $message $_GET['message'];
    $hash $_GET['hash'];
    $nonce $_GET['nonce'];
    $check_hash hash('sha256'$message $nonce $secret_key);

    if(
    $hash === $check_hash) {
      if(
    check database table to see if $nonce already exists in it)
      {
        die(
    "Nonce already used, request denied");
      }
      else
      {
        
    insert nonce into database table
        
    die("API request successful");
      }
    } else {
      die(
    "Message was modified");

    Now it is only possible for the user to visit each signed URL once.


    However, for a simple voting system this isn't needed if you use JavaScript to cast the vote. For example, to vote you could use the JavaScript code:
    Code:
    // jQuery
    $('body').append('<script src="http://mainSite.com/vote.php?votingOption=2" type="text/javascript"></script>');
    You would use this same voting code on mainSite.com and all of your sub sites. All of them would just submit the vote to mainSite.com/vote.php. Because vote.php is located on mainSite.com, it will be able to access the user's mainSite.com cookies and can use one of them to track voting status. There is no need to have a separate cookie on each site.
    PHP FAQ

    Originally Posted by Spad
    Ah USB, the only rectangular connector where you have to make 3 attempts before you get it the right way around
  14. #8
  15. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2010
    Posts
    95
    Rep Power
    0
    As I mentioned, if you don't want the user to be able to re-send the API request more than once then you just need to implement a nonce.

    A nonce is simple a random value that you only allow to be used once. Used nonces are stored in a database table. The nonce is included in the hash for validation purposes.

    PHP Code:
    $secret_key "xckjpZXlkcjposSRA82i3jpaju0s98das90duskjkasDJPOAIdjkw";
    $nonce hash('sha256'crypt(microtime(true))); // this just generates a random string
    $message "x:1235;y:84534";
    $hash hash('sha256'$message $nonce $secret_key); 
    Then the AJAX request is sent to:
    PHP Code:
    "http://serverTerm/api.php?message={$message}&nonce={$nonce}&hash={$hash}
    Now on the check side, you just need to perform some additional database steps:
    PHP Code:
    $message $_GET['message'];
    $hash $_GET['hash'];
    $nonce $_GET['nonce'];
    $check_hash hash('sha256'$message $nonce $secret_key);

    if(
    $hash === $check_hash) {
      if(
    check database table to see if $nonce already exists in it)
      {
        die(
    "Nonce already used, request denied");
      }
      else
      {
        
    insert nonce into database table
        
    die("API request successful");
      }
    } else {
      die(
    "Message was modified");

    Now it is only possible for the user to visit each signed URL once.


    However, for a simple voting system this isn't needed if you use JavaScript to cast the vote. For example, to vote you could use the JavaScript code:



    Originally Posted by E-Oreo
    Code:
    // jQuery
    $('body').append('<script src="http://mainSite.com/vote.php?votingOption=2" type="text/javascript"></script>');
    You would use this same voting code on mainSite.com and all of your sub sites. All of them would just submit the vote to mainSite.com/vote.php. Because vote.php is located on mainSite.com, it will be able to access the user's mainSite.com cookies and can use one of them to track voting status. There is no need to have a separate cookie on each site.
    Hi man, thanks for your nice answers always.

    Actually I am unable to get the best understand from the quoted text above I will explain to you my problem.

    1. Clients are getting html output (API) into their sites using function file of PHP as I mentioned above, clients let us say like: www.dell.com, www.HP.com, www.bmw.com

    2. When users world wide access clients websites (dell,hp,bmw), they will access the API generated by my main site which has the link: www.mainSite.com/api.php?apiId=44

    3. Users world wide will vote on (dell, hp, bmw, ..) sites. Their voting will go to the link: www.mainSite.com/vote.php?votingOption=2

    4. In the file www.mainSite.com/vote.php, cookies is not working at all! If you try to set cookies million times you will get EMPTY (var_dump($_COOKIE)) is EMPTY always! While cookies works fine on (dell,hp,bmw) sites..

    As you mentioned: they will be able to use same cookies on www.mainSite.com, but honestly I tried it many times and unable to set cookies there why? I need central cookies to track.

    Also, I do not need any body to access the page: www.mainSite.com/vote.php from browser address bar AT ALL, voting should be done by javascript clicks, it is not a matter of nonce because nonce will allow them to access the page once from browser address bar and then will disallow them.

    I hope you can understand my problem.

    Thanks a lot.

IMN logo majestic logo threadwatch logo seochat tools logo