<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: subhash periyasawmy</title>
    <description>The latest articles on DEV Community by subhash periyasawmy (@subhash_periyasawmy_7a201).</description>
    <link>https://dev.to/subhash_periyasawmy_7a201</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3550915%2F26db5fd6-375a-4de5-914e-9e146101f1ab.jpg</url>
      <title>DEV Community: subhash periyasawmy</title>
      <link>https://dev.to/subhash_periyasawmy_7a201</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/subhash_periyasawmy_7a201"/>
    <language>en</language>
    <item>
      <title>Secure PHP API</title>
      <dc:creator>subhash periyasawmy</dc:creator>
      <pubDate>Tue, 07 Oct 2025 09:37:31 +0000</pubDate>
      <link>https://dev.to/subhash_periyasawmy_7a201/secure-php-api-2cgc</link>
      <guid>https://dev.to/subhash_periyasawmy_7a201/secure-php-api-2cgc</guid>
      <description>&lt;p&gt;demonstrating essential security measures like HTTPS enforcement, custom JWT stub, basic input validation, and a PDO-based prepared statement&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Configuration and Utility Stubs (config.php and utils.php)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’ll need a central place for configurations and a custom implementation for complex security mechanisms like JWT and database connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.php
&amp;lt;?php
// Configuration (KEEP THIS FILE OUTSIDE THE WEB ROOT!)
define(‘DB_HOST’, ‘localhost’);
define(‘DB_NAME’, ‘api_db’);
define(‘DB_USER’, ‘api_reader’); // Use a dedicated, low-privilege user
define(‘DB_PASS’, ‘secure_db_password’);
define(‘JWT_SECRET’, ‘YOUR_VERY_LONG_AND_SECURE_SECRET_KEY’);
define(‘RATE_LIMIT_MAX’, 100);
define(‘RATE_LIMIT_TIME’, 60); // seconds
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;utils.php

    &amp;lt;?php
    require_once ‘config.php’;

    // — — Database Connection (PDO is Core PHP) — -
    function get_db_connection() {
    try {
    $pdo = new PDO(“mysql:host=” . DB_HOST . “;dbname=” . DB_NAME . “;charset=utf8mb4”, DB_USER, DB_PASS);
    $pdo-&amp;gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo-&amp;gt;setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    return $pdo;
    } catch (PDOException $e) {
    // Log the error internally and return a generic error to the client
    http_response_code(500);
    echo json_encode([“error” =&amp;gt; “Database connection failed.”]);
    exit();
    }
    }

    // — — JWT Stub (Manual, Simplified Implementation) — -
    function validate_jwt() {
    $auth_header = $_SERVER[‘HTTP_AUTHORIZATION’] ?? ‘’;
    if (!preg_match(‘/Bearer\s(\S+)/’, $auth_header, $matches)) {
    return false;
    }
    $jwt = $matches[1];

    // In Core PHP without libraries, JWT validation is complex.
    // This is a minimal, NON-SECURE stub for demonstration.
    // In production, ALWAYS use a vetted library (like firebase/php-jwt).
    $parts = explode(‘.’, $jwt);
    if (count($parts) !== 3) {
    return false;
    }

    list($header_b64, $payload_b64, $signature_b64) = $parts;

    // In a real scenario, you MUST verify the signature here.
    // e.g., hash_hmac(‘sha256’, “$header_b64.$payload_b64”, JWT_SECRET, true)

    $payload_json = base64_decode($payload_b64);
    $payload = json_decode($payload_json, true);

    // Basic validity checks
    if (!isset($payload[‘exp’]) || $payload[‘exp’] &amp;lt; time()) {
    return false; // Token expired
    }
    if (!isset($payload[‘user_id’])) {
    return false; // Missing necessary claim
    }

    return $payload;
    }

    // — — Output Function for consistent responses — -
    function send_response($status_code, $data) {
    http_response_code($status_code);
    echo json_encode($data);
    exit();
    }
    ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The Main API Endpoint (api.php)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This file contains the logic for routing, authentication, and security checks.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
require_once ‘utils.php’;

header(‘Content-Type: application/json’);

// — — 1. Essential Security Checks — -

// A. Enforce HTTPS
if (empty($_SERVER[‘HTTPS’]) || $_SERVER[‘HTTPS’] === ‘off’) {
send_response(403, [“error” =&amp;gt; “API access requires HTTPS.”]);
}

// B. Rate Limiting (Using files, since no Redis/Memcached is assumed)
$ip = $_SERVER[‘REMOTE_ADDR’];
$log_file = ‘rate_limit_logs/’ . md5($ip) . ‘.log’;
$current_time = time();

// Clean up old requests and count current ones
if (file_exists($log_file)) {
$requests = file($log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$valid_requests = array_filter($requests, function($timestamp) use ($current_time) {
return ($current_time — $timestamp) &amp;lt; RATE_LIMIT_TIME;
});

if (count($valid_requests) &amp;gt;= RATE_LIMIT_MAX) {
send_response(429, [“error” =&amp;gt; “Rate limit exceeded.”]);
}

// Log new request and save valid ones
$valid_requests[] = $current_time;
file_put_contents($log_file, implode(“\n”, $valid_requests));
} else {
// New IP, create log file
if (!is_dir(‘rate_limit_logs’)) mkdir(‘rate_limit_logs’);
file_put_contents($log_file, $current_time);
}

// — — 2. Routing and Authorization — -

$method = $_SERVER[‘REQUEST_METHOD’];
// Use PATH_INFO for clean URLs like /api.php/users/123
$path_info = $_SERVER[‘PATH_INFO’] ?? ‘/’;
$uri_segments = array_values(array_filter(explode(‘/’, $path_info)));

$endpoint = $uri_segments[0] ?? ‘info’;
$resource_id = $uri_segments[1] ?? null;

// Endpoints requiring authentication
$protected_endpoints = [‘users’, ‘products’, ‘orders’];
if (in_array($endpoint, $protected_endpoints)) {
$auth_data = validate_jwt();
if (!$auth_data) {
send_response(401, [“error” =&amp;gt; “Unauthorized: Invalid or missing token.”]);
}
$user_id = $auth_data[‘user_id’];
}

// — — 3. Endpoint Logic with Input Security — -

switch ($endpoint) {
case ‘users’:
if ($method === ‘GET’ &amp;amp;&amp;amp; $resource_id) {
// C. Broken Object Level Authorization (BOLA) Check
if ((int)$resource_id !== $user_id) {
send_response(403, [“error” =&amp;gt; “Forbidden: You can only view your own resource.”]);
}
handle_get_user((int)$resource_id);
} else {
send_response(405, [“error” =&amp;gt; “Method not allowed for this endpoint.”]);
}
break;

case ‘login’:
if ($method === ‘POST’) {
handle_login();
}
break;
// … other endpoints …

default:
send_response(404, [“error” =&amp;gt; “Endpoint not found.”]);
}

// — — 4. Controller Functions — -

function handle_get_user($id) {
$pdo = get_db_connection();

// D. Use Prepared Statements (Crucial for SQL Injection prevention)
$stmt = $pdo-&amp;gt;prepare(“SELECT id, username, email FROM users WHERE id = :id”);
$stmt-&amp;gt;bindParam(‘:id’, $id, PDO::PARAM_INT);

if ($stmt-&amp;gt;execute() &amp;amp;&amp;amp; $user = $stmt-&amp;gt;fetch()) {
// E. Limit Data Exposure (Never return password hash!)
send_response(200, $user);
} else {
send_response(404, [“error” =&amp;gt; “User not found.”]);
}
}

function handle_login() {
$data = json_decode(file_get_contents(‘php://input’), true);

// F. Input Validation and Sanitization
$username = $data[‘username’] ?? ‘’;
$password = $data[‘password’] ?? ‘’;

if (empty($username) || empty($password)) {
send_response(400, [“error” =&amp;gt; “Missing username or password.”]);
}

$pdo = get_db_connection();
$stmt = $pdo-&amp;gt;prepare(“SELECT id, password FROM users WHERE username = :username”);
$stmt-&amp;gt;bindParam(‘:username’, $username);

if ($stmt-&amp;gt;execute() &amp;amp;&amp;amp; $user = $stmt-&amp;gt;fetch()) {
// G. Secure Password Verification
if (password_verify($password, $user[‘password’])) {
// H. Issue a new token (STUB)
$new_jwt = ‘HEADER.PAYLOAD_USER_’ . $user[‘id’] . ‘.SIGNATURE’;
send_response(200, [“token” =&amp;gt; $new_jwt, “message” =&amp;gt; “Login successful”]);
}
}

// Generic failure message to prevent enumeration attacks
send_response(401, [“error” =&amp;gt; “Invalid credentials.”]);
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>php</category>
    </item>
  </channel>
</rss>
