View on GitHub

apunts

Apunts DWES

Seguretat en PHP (i altres coses)

Taula de continguts

Ocultant dades d’entorn

Video

Una de les pràctiques habituals i necessàries en les aplicacions PHP és l’emmagatzematge de les variables d’entorn i la seua posterior recuperació en el codi font de les pàgines.

Per a poder incorporar d’una manera àgil les variables d’entorn en aplicacions PHP usarem una llibreria que ja ve preparada amb les funcionalitats bàsiques que podrem arribar a necessitar en el dia a dia. La llibreria es diu “PHP dotenv” i bàsicament permet la lectura senzilla de les variables d’entorn en arxius d’extensió “.*env”.

L’ús de variables d’entorn en les aplicacions web és important per a la separació del codi per responsabilitats: no hem de mesclar el codi de l’aplicació amb els valors de configuració.

Les variables de configuració canviaran generalment segons l’entorn d’execució, és a dir, quan una aplicació està sent executada en diferents servidors. Per exemple, el servidor local per a desenvolupament i el servidor remot on es col·locarà el lloc web accessible als usuaris d’Internet. Per tant, separar-les en arxius independents permetrà que el mateix codi funcione en qualsevol lloc, sense necessitat de modificacions, independentment dels valors de configuració que es tinguen en cada entorn.

Pensem en els valors de connexió amb la base de dades (host, user, password…). Aqueixos valors segurament seran diferents en el servidor de desenvolupament i en el servidor de producció. Altres elements que seria important tindre separats en variables d’entorn són les credencials d’accés a diversos serveis, claus APIs (API Keys), el nom del host on s’està executant l’aplicació, etc.

Artxius .env

Els arxius .env es poden considerar un estàndard per a l’emmagatzematge de variables d’entorn. Aquests arxius tenen un format molt senzill i fàcil d’escriure i de llegir.

La sintaxi dels .*env conté parells clau (nom de variable) i valor, separats per un caràcter “=”. Cada variable en una línia.

MODE="development"
HOST="projecte.my"

DB_USER="batoi"
DB_PASSWORD="1234"
DB_HOST="localhost"

L’arxiu .env generalment ho col·locaràs en l’arrel del teu projecte, en un arxiu sense nom. La ruta del .env no ha d’estar accessible pel Servidor Web, sinó que ha de ser un directori per damunt, perquè els usuaris no puguen accedir a la configuració de les nostres variables d’entorn. Fixa’t també que el nom de l’arxiu començarà amb un punt i després les tres lletres “env”. En sistemes com Linux o Mac, l’arxiu que comença per “.” es considera ocult.

El que és important és que aquests arxius no es troben a l’abast dels usuaris, per la qual cosa mai haurien d’estar dins de la carpeta de publicació, sinó algun directori per damunt en el servidor. És a dir, es col·locarà en l’arrel del projecte, però mai dins de la carpeta arrel de publicació, perquè si foren allí els usuaris podrien accedir a aqueixos valors component la ruta com “example.com/.env”. Òbviament, els valors de configuració desitgem que estiguen segurs, per la qual cosa no han de ser accessibles pel públic en general.

Llibreria PHP DOTENV

PHP dotenv https://github.com/vlucas/phpdotenv fa la tasca d’obrir l’arxiu on les variables d’entorn s’emmagatzemen i processar el seu contingut, per a produir les variables d’entorn i consumir-les còmodament dins de les aplicacions.

Pots instal·lar fàcilment mitjançant

composer require vlucas/phpdotenv

Càrrega d’arxius amb variables d’entorn

PHP dotenv ens permet tindre diversos arxius de configuració. Per a accedir al contingut dels arxius .env utilitzem el següent codi:

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

Accés a les variables d’entorn

Una vegada que hem carregat els arxius de variables d’entorn, estaran disponibles les seues variables i valors en el codi de les aplicacions per mitjà de diversos mètodes diferents.

La funció getenv(), en la qual passem la cadena de la variable d’entorn que volem accedir.

A través del array *superglobal $_ENV A través del array *superglobal $_SERVER

$s3_bucket = getenv('S3_BUCKET');
$s3_bucket = $_ENV['S3_BUCKET'];
$s3_bucket = $_SERVER['S3_BUCKET'];

Sessions i seguretat

Video

Com Http és un protocol sense estat, les diferents peticions d’un client a un servidor són independents, no estan relacionades entre si. Per associar-les s’utilitzen les sessions.

El terme sessió fa referència al conjunt d’informació relativa a un usuari concret. Aquesta informació pot ser tan simple com el nom del propi usuari, o més complexa, com els articles que ha dipositat en la cistella de compra d’una tenda online.

Cada usuari diferent d’un lloc web té la seua pròpia informació de sessió.Per a distingir una sessió d’una altra s’usen els identificadors de sessió (SID). Un SID és un atribut que s’assigna a cadascun dels visitants d’un lloc web i ho identifica. Si el servidor web utilitza el SID d’un usuari, per a relacionar-ho amb la informació que posseeix sobre ell, que es manté en la sessió de l’usuari.

El procés de maneig de sessions en PHP està automatitzat.

Accedir a les dades de la sessió

// Iniciem la sessió o recuperem l'anterior sessió existent 
session_start(); 
// Comprovem si la variable ja existeix 
if (isset($_SESSION['visites']))
	$_SESSION['visites']++; 
else
	$_SESSION['visites'] = 0;

Eliminar una variable de la sessió

	unset($_SESSION['visites']);
Objectes i arrays

Si volem guardar un objecte o un array en una variable de sessió primer l’hem de transformar amb serialize() i despres, quan el recuperem, haurem d’utilitzar unserialize()

Autenticació amb sessions

Video

register.php

<?php
if (isPost() && cfsr()){
        try {
            // Comprovació d'errors
        } catch ( CheckFieldException $e) {
            $errors[$e->getField()] = $e->getMessage();
        }

       if (!count($errors)){
           $password = password_hash($password,PASSWORD_DEFAULT );
           $query->insert('users',compact('name','email','password'));
           header('Location: /');
       }
    }
    require_once('register.view.php');
?>

login.php

 <?php 
 	session_start()
	if (isPost() && cfsr()){
       //comprovació d'errors
       if (!count($errors)){
           $user = $query->login('users',$email,$password);
           $_SESSION['user'] = serialize($user);
           header('Location: /');
       }
    }

    require_once('login.view.php'); ```
 
logout.php
 
```php
<?php
    session_start();
    unset($_SESSION['user']);
    session_destroy();
    header('Location: /');

index.php

<?php
	session_start();
   $user = unserialize($_SESSION['user']);
   if (!$user) {
        header('Location: /login.php');
   }
   ...  

queryBuilder.php

<?php
	public function login($table,$email,$password){
        $stpdo = $this->conn->prepare("SELECT * FROM $table WHERE email = :email");
        $stpdo->bindValue(":email",$email);
        $stpdo->execute();
        $user = $stpdo->fetch(\PDO::FETCH_OBJ);
        if (password_verify($password, $user->password)) return $user;
        return null;
    }    

La informació d’autenticació s’ha d’utlitzar en un protocol com a HTTPS que permeta xifrar les comunicacions amb el servidor web i amb contrasenya protegida amb hash.

Enviament de correu electronic

Video

Encara que la funció mail() permet l’enviament de correus electrònics, és habitual utilitzar alguna llibreria que s’ocupe dels detalls del format.

 composer require phpmailer/phpmailer

o modificant el composer.json per afegir la linea “phpmailer/phpmailer”: “~6.1” dins del require

 "require": {
        "filp/whoops": "^2.4",
        "phpmailer/phpmailer": "~6.1"
    },

i executant els composer update

En principi es possible enviar un correu utilitzant la configuració de sendmail (en Linux) o un servidor SMTP local, en la pràctica els filtres antispam fan que no arriben els correus enviats des de servidors no registrats correctament. A més des de les aules tampoc es pot fer.

Si no es disposa d’un servidor de correu en Internet, l’opció més comoda per tal d’enviar un correu és utilitzar un compte de Gmail. En Gmail cal activar l’opció “Permetre aplicacions meyns segures” en la secció d’ajustos de compte. Açò va a canviar en breu i no es podrà utilitzar sinó que haurem d’identificar-nos amb Oauth. Teniu informació disponible en el github de php mailer.

El següent programa permet enviar un correu mitjançant Google:

<?php 	
 	use PHPMailer\PHPMailer\PHPMailer;
	use PHPMailer\PHPMailer\SMTP;
	use PHPMailer\PHPMailer\Exception;
 	require "vendor/autoload.php";
 	
	try {
	    //Server settings
	    $mail->SMTPDebug = SMTP::DEBUG_SERVER;                      // Enable verbose debug output
	    $mail->isSMTP();                                            // Send using SMTP
	    $mail->Host       = 'smtp.gmail.com';                    // Set the SMTP server to send through
	    $mail->SMTPAuth   = true;                                   // Enable SMTP authentication
	    $mail->Username   = '2daw2021batoi@gmail.com';                     // SMTP username
	    $mail->Password   = 'batoi_1234';                               // SMTP password
	    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;         // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged
	    $mail->Port       = 587;                                    // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above

	    //Recipients
	    $mail->setFrom('from@example.com', 'Mailer');
	    $mail->addAddress('joe@example.net', 'Joe User');     // Add a recipient
	    $mail->addAddress('ellen@example.com');               // Name is optional
	    $mail->addReplyTo('info@example.com', 'Information');
	    $mail->addCC('cc@example.com');
	    $mail->addBCC('bcc@example.com');

	    // Attachments
	    $mail->addAttachment('/var/tmp/file.tar.gz');         // Add attachments
	    $mail->addAttachment('/tmp/image.jpg', 'new.jpg');    // Optional name

	    // Content
	    $mail->isHTML(true);                                  // Set email format to HTML
	    $mail->Subject = 'Here is the subject';
	    $mail->Body    = 'This is the HTML message body <b>in bold!</b>';
	    $mail->AltBody = 'This is the body in plain text for non-HTML mail clients';

	    $mail->send();
	    echo 'Message has been sent';
	} catch (Exception $e) {
	    echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
	}

On us he possat unes credencials que podeu gastar per al curs i no tenir que obrir un compte cadascú.

Composer

Composer ens resol dos problemes:

Gestió de les dependències

Instal·lació

$curl -sS https://getcomposer.org/installer | php  
sudo mv composer.phar /usr/local/bin/composer

Preparar l’arxiu composer.json

 {"require": { "monolog/monolog": "1.2." } }

Exemple d’ús

{ "require": {"monolog/monolog": "1.0." } } 

Estem indicant que el projecte depèn d’un paquet anomenat monolog/monolog i que li serveix qualsevol versió la numeració de la qual comence per 1.0

Noms de paquets

Versions de paquets

Versionat semàntic (Semver)

Instal·lant les dependències

 composer install

L’arxiu composer.lock

Actualitzar versions

 composer update monolog/monolog

Afegint dependències

 composer require monolog/monolog:1.

Packagist

Càrrega automàtica de classes

 require 'vendor/autoload.php';

Ús de la llibreria

$log = new Monolog\Logger('name'); 

$log->pushHandler(
new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING) );

$log->addWarning('Foo');

Espais de noms

Introducció als namespaces

Definir espais de noms

namespace MiProyecto;

const CONNECTAR_OK = 1; 

class Connexió { / ... / } 

function connectar() { / ... / }

Ubicació de la declaració

 <html> <?php namespace MiProyecto;
 
  // error fatal - el espacio de nombres debe ser la primera sentencia del script ?>

Declarar subespacios de noms

namespace MiProyecto\Sub\Nivell;

const CONNECTAR_OK = 1; 

class Connexió { / ... / } 

function connectar() { / ... / }

Ús de namespaces en els nostres projectes

namespace CursoPhp7\Core; Class Request { ... }

Usar elements que estan dins de namespaces

nom complet CursoPhp7\Core\Request
use CursoPhp7\Core\Request;
use CursoPhp7\Core\Request as Req;

Afegir els uses

return $statement->fetchAll(
\PDO::FETCH_CLASS, 'Agenda\\Entities\\'.$classEntity);

Autoloading amb Composer

Càrrega automàtica de les nostres classes

Ús de PSR-4

Exemple
{
"autoload": {"psr-4": {"Acme\\": "src/"} } }

Incloure el autoloader