Home » Php » php – PayPal IPN Security

php – PayPal IPN Security

Posted by: admin July 12, 2020 Leave a comment


PayPal IPN sends a POST request with a variable number of fields to the notify URL, in order to confirm that the POST request is legit we need to resubmit the same request along with a additional cmd=_notify-validate field to PayPal, which then replies VERIFIED or INVALID.

My question is, why do we need to resend the request to PayPal? Wouldn’t something like this suffice?

if (preg_match('~^(?:.+[.])?paypal[.]com$~i', gethostbyaddr($_SERVER['REMOTE_ADDR'])) > 0)
    // request came from PayPal, it's legit.

Iff we can trust the server to correctly resolve IPs, I assume we can trust all requests from PayPal, no?

How to&Answers:

I know this question is quite old, but:

The attacker does not even need to spoof his ip or perform any sort of MITM to pass your validation:

  1. He connects from his own machine with IP address x.y.z.t.
  2. Your server calls gethostbyaddr(“x.y.z.t”) which sends a dns query for the name t.z.y.x.in-addr.arpa.
  3. If x.y.z.t belongs to the attacker, chances are he controls (at least) the dns domain z.y.x.in-addr.arpa as well (since that contains his own ip). So he can return “paypal.com” in response to that query.
  4. Your server receives “paypal.com” from the attacker’s dns server, and your validation check succeeds.

This attack is defeated by sending a request to paypal as recommended by Lobos.


This is the easiest way I have found to do it, also as per PayPal suggests. I use http_build_query() to construct the url from the post that was sent to the site from paypal. Paypal docs states that you should send this back for verification and that is what we do with file_get_contents. you will note that I use strstr to check if the word ‘VERIFIED’ is present and so we continue in the function, if not we return false…

$verify_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate&' . http_build_query( $_POST );   

if( !strstr( file_get_contents( $verify_url ), 'VERIFIED' ) ) return false;


PayPal is simply enforcing a higher standard of security for your own site/application’s benefit.

IP spoofing can easily fool your example function, as relying on REMOTE_ADDRESS alone is quite prone to attack.

When you are working with financial transactions security is of great importance. If I can spoof an IPN request, I can trick your site/application into performing false transactions. By sending an additional request to a known and trusted location, we obtain a much higher standard of credential upon which to act. The entire original IPN request is sent in this confirmation so that PayPal may verify that all of the transaction details are in fact valid, thereby preventing a Man-in-the-Middle Attack whereby an attacker modifies details (say, changing a price or quantity) of an otherwise valid request as it is sent from PayPal to your server.


the whole thing falls apart if someone manages to alter the hosts file on the machine running your ipn listener;

  • bad person sends false payment notification

  • your compromised server sends duplicate to ‘paypal.com’ which is actually pointed to bad persons’ machine

  • bad person replies VERIFIED, receives goods as if they had paid.

this isn’t so much of a problem as if a person has r/w access to your hosts file they could probably just put the payment record into your database by hand, or do many other bits of damage.

just a thought.


Here is a reason to reply to the POST, from the IPN Guide:

Your listener must respond to each
message, whether or not you intend to
do anything with it. If you do not
respond, PayPal assumes that the
message was not received and resends
the message. PayPal continues to
resend the message periodically until
your listener sends the correct
message back, although the interval
between resent messages increases each

IMPORTANT: PayPal expects to receive a
response to an IPN message within 30


Here is another solution – https://stackoverflow.com/a/4988281/2165415

(because your IPN page may need to check from where the call comes)