In short: For API security in PHP, five points matter above all: HTTPS only, authentication at every non-public endpoint, an authorization check for every single object, strict limits on method, content type and size of the request, and brief error messages without internal details. Rate limiting and a log of security-relevant events come on top. This does not help against vulnerabilities in the PHP interpreter itself such as PHP2Shell; that takes the PHP update.
The Interface Behind the Website
In the week after 21 October 2026, many companies checked their websites. The interfaces behind them were less often on the list. Yet it is precisely there that sensitive processes run: the customer portal's app fetches invoices, the merchandise management system writes stock levels into the shop, a partner reports orders via webhook. These APIs were often built quickly, for a single purpose, and never looked at again.
An API has no user interface that makes errors visible. A missing authorization check goes unnoticed in everyday use as long as all clients query only what they are supposed to. An attacker, by contrast, tries exactly what is not intended.
The first step is therefore an inventory. Which endpoints are there at all? Often one finds old versions such as /api/v1/ that were never switched off after the move to /v2/, test endpoints from development, or a publicly retrievable interface description that lists internal functions. Every endpoint that is no longer needed should be switched off. Every one that stays needs someone responsible.
API Security in PHP: The Basic Rules
The OWASP REST Security Cheat Sheet summarizes proven rules. For PHP projects, they can be translated into a short list:
- HTTPS only. Tokens and credentials must never be transmitted unencrypted.
- Every endpoint checks for itself. Authentication can happen centrally; the decision on access is made at the endpoint.
- Authorization per object. Not just "is logged in" but "may access exactly this record".
- Define allowed methods. Whatever is not expressly allowed is rejected with 405.
- Check content type and size. A wrong type yields 415, requests that are too large 413.
- Validate input. Type, length, format and value range, on the server side.
- No secrets in URLs. Tokens and keys belong in headers, not in the query string.
- Brief errors. No stack traces, paths or SQL messages to the client.
- Rate limiting. Too many requests are answered with 429.
- Shield administration endpoints. Not publicly reachable or only with strong login.
Object Authorization: The Most Common Mistake
An example makes the problem tangible. An endpoint delivers invoices under /api/rechnungen/{id}. It checks whether a valid token is present. It does not check whether the invoice belongs to the token's customer. Anyone who raises their own invoice number by one sees the neighbor's.
The solution is simple and yet has to be implemented at every spot: the query itself restricts to the authorized scope.
<?php
// Allow only the GET method
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
header('Allow: GET');
exit;
}
$kundeId = $auth->kundeId(); // from the verified token
$id = filter_var($routeId, FILTER_VALIDATE_INT);
if ($id === false) {
http_response_code(400);
exit;
}
// Authorization is part of the query: own invoices only
$stmt = $pdo->prepare('SELECT nummer, betrag, datum FROM rechnungen WHERE id = ? AND kunde_id = ?');
$stmt->execute([$id, $kundeId]);
$rechnung = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$rechnung) {
http_response_code(404); // do not reveal whether the ID exists
exit;
}
header('Content-Type: application/json');
echo json_encode($rechnung, JSON_THROW_ON_ERROR);
The query with placeholders also protects against SQL injection, see Preventing SQL Injection in PHP. How PHP provides request data such as method, headers and body is explained in Superglobals: $_GET, $_POST, $_FILES and More.
Verifying Tokens Correctly
Many APIs work with JSON Web Tokens. The OWASP cheat sheet gives clear rules for them: tokens must be signed, unsigned tokens are rejected, and the algorithm for verification is set on the server, not taken from the token. Issuer, audience and expiry time are also part of the verification. Use a maintained library for this instead of your own implementations.
Your own tokens, for example for webhooks or one-time links, must come from a cryptographically secure random generator and be compared in constant time. How to do this in PHP is shown in Secure Random Numbers and Tokens in PHP.
Give each client and each partner a separate key with exactly the rights it needs. Then a leaked key can be blocked individually without interrupting all other connections.
Setting Limits: Size, Type and Pace
An API should accept only what it expects. That starts with the content type: an endpoint that processes JSON rejects everything else. You limit the maximum size of a request at several levels, in the web server and in PHP via post_max_size. This limit is also among the interim measures that php.net named for PHP2Shell. How to choose it sensibly is described in Sensibly Limiting post_max_size and upload_max_filesize.
Rate limiting protects against guessing identifiers and against overload. It can be implemented in the web server, in an upstream proxy or in the application. It is important that limits apply per key or user and not only per IP address.
For APIs that are called directly from the browser, CORS comes into play, that is, the rules about which foreign websites may make requests. Set these headers only if such calls are really intended, and name the permitted origins individually instead of allowing every origin. A wildcard that allows all websites is almost always a mistake for endpoints with login.
Finally, the log: failed logins, rejected tokens and denied accesses belong in the log, tokens themselves do not. Without these entries, an attack on the API remains invisible.
How does PHP SOS help?
- We record all interfaces of your PHP systems, including forgotten and internal ones, and check which are publicly reachable.
- We read the authorization logic and specifically test whether other people's records can be retrieved.
- We set limits for size, type and pace in the web server and PHP and coordinate them with your clients.
- For a comprehensive review from outside, we combine this with a security audit and penetration test.
Conclusion: An API is only as secure as its most careless endpoint. Anyone who checks authorization per object, sets clear limits and keeps errors brief closes the most common gaps long before anyone looks for them specifically.
Frequently asked questions
Is an API key enough as protection?
Not for sensitive data. An API key usually identifies an application, not a user, and is easily passed on. The OWASP REST Security Cheat Sheet advises against using API keys as the sole protection for sensitive resources.
What is object authorization and why is it so often missing?
It means checking whether the logged-in user may view or change exactly this record. It is often missing because developers only check whether anyone is logged in at all. Then it is enough to change an identifier in the URL to retrieve other people's data.
Does a well-secured API protect against PHP2Shell?
No. PHP2Shell sits in the PHP interpreter and takes effect in affected operating modes before your code even runs. Only a PHP update with a restart helps against it. Size limits for requests still make sense and are also among the interim measures from php.net.
Which status codes should an API return for security errors?
Usual are 401 without valid login, 403 for missing authorization, 405 for a method that is not allowed, 413 for a request that is too large, 415 for a wrong content type and 429 for too many requests. The response itself stays brief and without internal details.