Skip to content

Latest commit

 

History

History
322 lines (246 loc) · 12 KB

ENV & CROSS Origin Lesson-2.md

File metadata and controls

322 lines (246 loc) · 12 KB
  1. What is a .env File? A .env file is a text file used to store environment variables, which are key-value pairs that can be used to configure different settings for your application. For example, storing database credentials, API keys, or app secrets that should not be hard-coded in your PHP files.

The .env file is not meant to be committed to version control (e.g., Git), so sensitive information is kept private.

  1. Structure of a .env File The .env file contains key-value pairs, each on a new line. Here's an example of what a .env file might look like:
DB_HOST=localhost
DB_NAME=test_db
DB_USER=root
DB_PASSWORD=secret_password
APP_DEBUG=true
APP_ENV=development
  1. How to Use .env Files in PHP PHP does not natively support .env files, but there are libraries like vlucas/phpdotenv that can load these files and parse the variables into your PHP environment. This allows you to use them as environment variables.

Step-by-Step Guide:

Step 1: Install phpdotenv To get started, you need to install the phpdotenv package, which is a popular library that loads .env files into the environment.

Using Composer:

composer require vlucas/phpdotenv

Step 2: Create a .env File Create a .env file in your project’s root directory (same directory as index.php or config.php).

Example .env file:

DB_HOST=localhost
DB_NAME=my_database
DB_USER=root
DB_PASSWORD=secret_password

Step 3: Loading the .env File in PHP

Now, in your PHP application, you will load this .env file and access the variables.

<?php
// Include the autoloader from Composer
require 'vendor/autoload.php';

// Load the .env file
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

// Access the environment variables
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPassword = getenv('DB_PASSWORD');

// Example usage: connecting to a database using PDO
try {
    $pdo = new PDO("mysql:host=$dbHost;dbname=$dbName", $dbUser, $dbPassword);
    echo "Connected to the database successfully!";
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}
?>

In this example:

  • We load the .env file using phpdotenv.
  • Then we access environment variables using getenv('VARIABLE_NAME').

Important Notes:

  • Dotenv\Dotenv::createImmutable(__DIR__): This loads the .env file from the current directory.
  • getenv(): This retrieves the value of an environment variable.

Step 4: Using .env Values in Your Application

Once the .env values are loaded into the environment, you can use them throughout your application. For example:

<?php
// Access the database credentials from the environment
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPassword = getenv('DB_PASSWORD');

// Example: Creating a PDO connection
try {
    $pdo = new PDO("mysql:host=$dbHost;dbname=$dbName", $dbUser, $dbPassword);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo "Database connection established!";
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}
?>
  1. Benefits of Using .env Files
  • Security: Sensitive information (like database credentials) is not hard-coded into your codebase, reducing the risk of exposing sensitive data.
  • Environment Specific Configurations: Easily switch between different environments (development, testing, production) by changing the values in your .env file, without modifying the code.
  • Convenience: Store configuration values that are shared across the application and access them in one centralized place.
  1. How to Handle Different Environments You can create different .env files for different environments. For example:
  • .env for development.
  • .env.production for production.
  • .env.testing for testing.

When deploying to production, you might want to use .env.production instead of .env, so your application connects to production databases, APIs, etc.

To use the appropriate .env file for each environment, you can load the file conditionally:

$envFile = '.env'; // Default environment file

if ($_SERVER['APPLICATION_ENV'] == 'production') {
    $envFile = '.env.production'; // Use the production environment file
}

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, $envFile);
$dotenv->load();
  1. Best Practices for .env Files

  2. Never commit .env files to version control: Add .env to your .gitignore file to keep it out of Git.

  3. Set default values: Use fallback values or validations to handle cases where an environment variable might be missing.

  4. Secure your .env files: Ensure that .env files are stored in secure locations with restricted access, especially in production environments.

  5. Example with Full Setup

Let’s go through an example of setting up a PHP application with database credentials stored in .env.

.env

DB_HOST=localhost
DB_NAME=test_db
DB_USER=root
DB_PASSWORD=secret_password

index.php

<?php
// Include the Composer autoloader
require 'vendor/autoload.php';

// Load environment variables from .env file
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

// Get the environment variables
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPassword = getenv('DB_PASSWORD');

try {
    // Connect to the database using PDO
    $pdo = new PDO("mysql:host=$dbHost;dbname=$dbName", $dbUser, $dbPassword);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    echo "Connected to the database successfully!";
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}
?>

  1. Summary of Key Concepts
  • .env file: Stores environment-specific configuration (e.g., database credentials, API keys).
  • phpdotenv: A library that loads .env files into PHP as environment variables.
  • Environment Variables: Accessed using getenv() in PHP after being loaded from .env.
  • Security: Protects sensitive data by not hard-coding them into the codebase.

Cross-Origin Resource Sharing (CORS) is a security feature implemented by browsers to control which domains can access resources on a server. When making requests to a server from a different domain (cross-origin requests), the browser will enforce CORS to ensure that only trusted websites can access the server's resources.

In raw PHP, you need to handle CORS manually by setting the appropriate HTTP headers to control which domains are allowed to make requests to your server.

  1. Basic CORS Setup

To allow a single domain to access your API, you need to set the Access-Control-Allow-Origin header.

Example to allow one domain:

header("Access-Control-Allow-Origin: https://example.com");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");

This will allow only https://example.com to access your API.

  1. Allowing Multiple Domains

If you want to allow multiple domains to access your API, you need to check the Origin header of the incoming request. Based on the origin, you can dynamically set the Access-Control-Allow-Origin header.

Example to allow multiple domains:

// Array of allowed origins
$allowedOrigins = [
    'https://example1.com',
    'https://example2.com',
    'https://example3.com'
];

// Get the origin of the incoming request
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

// Check if the origin is in the allowed list
if (in_array($origin, $allowedOrigins)) {
    header("Access-Control-Allow-Origin: $origin");
} else {
    header("Access-Control-Allow-Origin: null"); // Or handle it as per your needs
}

header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
  1. Handling Preflight Requests (OPTIONS Method)

Before making certain types of cross-origin requests (e.g., with custom headers or methods like PUT), browsers will send a preflight request using the OPTIONS method. This is an HTTP request to ask the server if the actual request is safe to send.

To handle this, you need to send the appropriate headers in response to an OPTIONS request.

Example to handle OPTIONS requests (Preflight):

// If the request method is OPTIONS, respond with the appropriate headers
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    // Allow specific origins, methods, and headers
    header("Access-Control-Allow-Origin: *"); // Allow any origin
    header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
    header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
    // Exit after sending the response
    exit();
}
  1. Allowing Credentials (Cookies and Authorization Headers)

By default, browsers do not send cookies or HTTP authentication credentials with cross-origin requests. To enable this, you need to set the Access-Control-Allow-Credentials header to true.

You also need to specify the exact origin (you can’t use * when sending credentials).

Example for allowing credentials:

header("Access-Control-Allow-Origin: https://example.com"); // Specific origin, not *
header("Access-Control-Allow-Credentials: true"); // Allow cookies/auth headers
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
  1. CORS with Custom Headers

If you're working with custom headers (such as Authorization or X-Custom-Header), you must explicitly list those in the Access-Control-Allow-Headers header.

Example with custom headers:

header("Access-Control-Allow-Origin: https://example.com");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Custom-Header");
  1. Full Example: Handling CORS for Multiple Domains

Here’s a full example to show how you can handle CORS for multiple domains, preflight requests, and allow credentials:

// Array of allowed origins
$allowedOrigins = [
    'https://example1.com',
    'https://example2.com',
    'https://example3.com'
];

// Get the origin of the incoming request
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

// Set CORS headers if the origin is in the allowed list
if (in_array($origin, $allowedOrigins)) {
    header("Access-Control-Allow-Origin: $origin");
    header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
    header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Custom-Header");
    header("Access-Control-Allow-Credentials: true"); // Allow credentials (cookies, etc.)
} else {
    header("Access-Control-Allow-Origin: null"); // Or handle it as per your needs
}

// Handle OPTIONS method for preflight request
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    // Preflight headers are sent, exit to prevent further processing
    exit();
}

// Handle GET, POST, PUT, DELETE, etc., requests below...
  1. Security Considerations
  • Restrict Origins: Avoid using * (wildcard) in Access-Control-Allow-Origin for security reasons, especially when allowing credentials. Always specify the allowed domains explicitly.
  • Preflight Caching: If you have frequent preflight requests (OPTIONS requests), you can use the Access-Control-Max-Age header to cache the result for a certain period. For example:
    header("Access-Control-Max-Age: 3600"); // Cache for 1 hour
  • Allow Only Required Headers: Limit the headers allowed in the Access-Control-Allow-Headers to only those that your API actually needs, reducing the surface area for attacks.
  1. CORS with Other Headers

Some other common headers used with CORS are:

  • Access-Control-Expose-Headers: To specify which headers should be exposed to the client (for example, if you want to allow access to X-Total-Count or X-Rate-Limit headers).

    header("Access-Control-Expose-Headers: X-Total-Count, X-Rate-Limit");
  • Access-Control-Allow-Methods: Defines which HTTP methods are allowed. We typically allow GET, POST, PUT, DELETE, and OPTIONS.