0

I run a website that creates digitally generated content for free. My visitors don't need an account to start their downloads either. It works simple enough: the end-user fills in a form, the form sends a request to a PHP-script (an 'API') and the PHP-script returns the result.

                 ?                  
+---------+              +---------+
|         | -----------> |         |
|FORM.html|              | API.php |
|         |<-------------|         |
+---------+              +---------+
               digital              
               content              

At the moment, the PHP script will send an answer to anything that calls it with right arguments. This makes it easy for other websites to just steal my form and hijack my service. In addition, a skilled enough person could request this 'digital content' through CURL, WGET or any automated script. So, my question is:

Q: How can I make sure that a request has been send from a form on my website before answering?

What I tried so far is adding an extra PHP-file on my server that would 'sign' the request (authenticate.php), before sending it through to the API. That worked, but of course, it only moved the problem to this new file. This script would, hapilly and stupidly, sign everything thrown at it and pass it through to API.php

I have also considered adding extra variables to the form, but this is wouldn't help either. A hacker would only need to identify and copy these variables.

I do not need ultimate security as it's 'only' a drawing. But any check is better then none. What would be sensible?

edit:

I'm mostly concerned with other sites using my API. As the content is free anyway, I'm less concerned with bots or scripts accessing the API. Although it would be nice to prevent that too.

Ideogram
  • 1,265
  • 12
  • 21
  • 4
    You can't. This is basically equivalent to DRM, which has never worked. No matter what you ask the client to try to check this, it can just lie to your server. – Joseph Sible-Reinstate Monica Sep 02 '19 at 13:13
  • 2
    @JosephSible Thanks for your answer/comment! @ "all the others", why the downvotes? Not everybody is an expert... I feel my question is concrete, I show I've done my research and tried to solve it. Is this realy such a low quality question? If so: why? – Ideogram Sep 02 '19 at 13:20
  • 3
    _“I have also considered adding extra variables to the form, but this is wouldn't help either. A hacker would only need to identify and copy these variables.”_ - you can make that harder, by not using (and checking for) the _same_ value every time, but by using tokens that can only be used once and for a limited time. This would basically be what is called a CRSF token … https://stackoverflow.com/questions/5207160/what-is-a-csrf-token-what-is-its-importance-and-how-does-it-work – misorude Sep 02 '19 at 13:24
  • 2
    And another way would of course be to use a CAPTCHA. Those are not very user-friendly, but “bots” still have a hard time getting around them for the most part. – misorude Sep 02 '19 at 13:26
  • @misorude CRSF sounds indeed by far the best solution. Thank you, I did not know the name for this technique and I can now look into it further. And indeed, a CAPTCHA is a very obvious start, too. – Ideogram Sep 02 '19 at 13:52
  • Note that a CSRF token would only prevent the specific case of other websites making the user's browser send a request to your website. It wouldn't prevent other websites making the request through their own servers, or things that aren't a browser making the request from the user's computer. – Joseph Sible-Reinstate Monica Sep 02 '19 at 15:27
  • @misorude Could you post "CSRF" as an answer? It might help others and it stays a bit invisible when burried in comments – Ideogram Sep 02 '19 at 18:37
  • @Ideogram sure, no problem. – misorude Sep 03 '19 at 06:37
  • I believe I found an answer. The form contains a (hidden) variable generated by the server that contains an encripted version of the client's IP address. After receiving a request, the server checks if the IP address of the client is still the same as the one received through this hidden variable. Yes, IP addresses can be spoofed, but this would prevent other websites from using my API with their forms. – Ideogram Sep 03 '19 at 12:24
  • @Ideogram no, that doesn't work. A website could just proxy the requests through itself, so the IP will still be right. – Joseph Sible-Reinstate Monica Sep 05 '19 at 12:55

2 Answers2

1

I have also considered adding extra variables to the form, but this is wouldn't help either. A hacker would only need to identify and copy these variables.

You can make that harder, by not using (and checking for) the same value every time, but by using tokens that can only be used once and for a limited time. This would basically be what is called a CRSF token.

Some links with a bit more info on the topic:

What is a CSRF token ? What is its importance and how does it work?
https://en.wikipedia.org/wiki/Cross-site_request_forgery
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

The typical attack / problem you try to counter with a CSRF token is someone triggering actions a logged-in user is allowed to perform in their name; but in a situation like you have here you can implement it in a very similar way, to try and prevent people or bots sending data to your endpoint without using your form.

misorude
  • 3,381
  • 2
  • 9
  • 16
  • A CSRF token doesn't help here. The asker wants to defend against things that aren't websites (such as scripts on a client's local machine that call wget/curl) and so aren't bound by the same-origin policy. Such scripts could just fetch the form, scrape the values, then use it when they submit them. – Joseph Sible-Reinstate Monica Sep 05 '19 at 12:54
  • @JosephSible as said, it makes it _harder_, bots won’t be able to analyze the form structure just once, they would have to request a fresh form every time to get a valid token. That this can not prevent things, if someone really puts in the effort, is clear. CAPTCHA was suggested as an alternative to limit it to humans, but considered to intrusive. – misorude Sep 05 '19 at 12:58
-1
   Use google Captcha in your form
   https://www.google.com/recaptcha
   //take a leaf out of payment gateway validation
    $acceptfrom = array(
        'www.yourserver.com',
        'yourserver.com'
    );



    if( !in_array( $_SERVER['REMOTE_ADDR'], $acceptfrom ) )
    {
        //break and redirect to another page here
    } else {
    //include Captcha validation here
    //validate form variables and send to API

    } 
Suzie
  • 1
  • 1
  • This (badly) attempts to ensure that a human was in the loop, but makes no attempt at all to verify that the request came from the website's own form. – Joseph Sible-Reinstate Monica Sep 03 '19 at 00:25
  • @Suzie It would be good if you'd explain that your script is an example implementation of an API. – Ideogram Sep 03 '19 at 05:06
  • @Suzie no, your answer is still as bad now as it was before the edit. It should be obvious to any reader of your code how they could completely bypass all "security" it provides. – Joseph Sible-Reinstate Monica Sep 05 '19 at 12:52
  • Please help us. I actually use the SERVER_NAME example on my own website, I wrap all my page content using it. I always want to improve, give us an example, I see that you're a brilliant programmer yourself. – Suzie Sep 06 '19 at 13:16