Redes y web
TCP/IP y puertos: cómo escuchan los servicios en tu máquina
Un puerto es una entrada numerada en tu máquina. Entiende cómo TCP y UDP usan puertos y prueba un servidor local en 127.0.0.1.
Lee un intercambio HTTP real línea por línea: métodos, URL, encabezados, clases de estado y cookies, probados con curl en un servidor local.
HTTP es el protocolo que usan navegadores, apps y APIs para intercambiar mensajes. La RFC 9110 define qué significan esos mensajes: métodos, códigos de estado, campos de encabezado y demás. La RFC 9112 define cómo se ve un mensaje HTTP/1.1 como texto plano sobre una conexión TCP. La descripción general de MDN es una introducción más corta, orientada al navegador, y encaja bien junto a las RFC.
El patrón es sencillo. Un cliente envía una solicitud y un servidor devuelve una respuesta. Siempre es el cliente quien inicia el intercambio. HTTP sin más no cifra nada y no recuerda quién eres entre una solicitud y otra. El cifrado viene de TLS, que explica el siguiente artículo. La memoria entre solicitudes viene de las cookies y los tokens, que veremos más abajo.
Casi todo el comportamiento web que vas a estudiar pasa por HTTP. Un formulario envía un valor en el cuerpo, una API confía en un encabezado, una redirección lleva al usuario a otro lugar y una caché guarda la copia equivocada de una página. Cuando después uses un proxy como OWASP ZAP para inspeccionar tráfico, te mostrará justamente estos mensajes. Si no puedes leer una solicitud en bruto, la salida del proxy resulta difícil de aprovechar.
Una solicitud HTTP/1.1 es texto plano con esta forma:
GET / HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: curl/8.12.1
Accept: */*La primera línea es la línea de solicitud: el método, el destino de la solicitud y la versión del protocolo. Cada línea siguiente es un campo de encabezado, escrito como nombre: valor. Una línea vacía termina la sección de encabezados. Si el mensaje tiene cuerpo, aparece después de esa línea vacía. En el cable, cada línea termina en CRLF (un retorno de carro y un salto de línea), algo que el editor oculta.
Una respuesta tiene el mismo formato, con una línea de estado al inicio: versión, código de estado y frase de motivo.
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 15
<h1>hello</h1>El método indica lo que el cliente quiere que se haga. La RFC 9110 define GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS y TRACE. PATCH viene de la RFC 5789.
Al leer tráfico importan dos propiedades:
Estas propiedades son promesas sobre el significado. Quien decide qué pasa de verdad es el servidor. Un manejador de GET que borra un registro rompe ese contrato, y un revisor de seguridad buscará justo ese error.
Toma la URL http://127.0.0.1:8000/missing?lang=en#top y divídela en partes:
http, que indica al cliente qué protocolo usar.127.0.0.1:8000, el host y el puerto opcional./missing.lang=en, después del ?.top, después del #.El cliente no envía al servidor ni el esquema ni el fragmento. La línea de solicitud lleva la ruta y la query. La autoridad va en el encabezado Host. HTTP/1.1 exige Host porque muchos sitios pueden compartir una misma IP, y el servidor usa ese encabezado para decidir qué sitio debe responder.
Los nombres de encabezado no distinguen mayúsculas de minúsculas, así que Content-Type y content-type son el mismo campo (RFC 9110, sección 5.1). Los valores son otra cosa: muchos sí distinguen mayúsculas, así que no des por hecho que no lo hacen.
Encabezados de solicitud que verás con más frecuencia:
Host: obligatorio en toda solicitud HTTP/1.1. La RFC 9112, sección 3.2, dice que el servidor debe responder 400 si falta.User-Agent: el software del cliente.Accept: los tipos de medio que el cliente puede manejar.Cookie: las cookies que el navegador devuelve al servidor.Authorization: credenciales, a menudo un token bearer.Content-Type y Content-Length: describen el cuerpo, cuando lo hay.Encabezados de respuesta que verás con más frecuencia:
Content-Type: el tipo de medio del cuerpo, como text/html.Content-Length: el tamaño del cuerpo en bytes.Date: el reloj del servidor, en GMT.Server: el nombre del software. Muchos servidores lo muestran. En producción suele ocultarse.Last-Modified: cuándo cambió por última vez el recurso según el servidor.Set-Cookie: pide al navegador que guarde una cookie.Los encabezados son datos que controla quien los envía. El navegador manda lo que manda su software, y un script o un proxy puede enviar lo que quiera. X-Forwarded-For es el ejemplo clásico: un proxy lo agrega, pero un cliente también puede enviarlo. Nunca confíes en un encabezado solo porque parezca oficial.
Un código de estado tiene tres dígitos. El primer dígito indica la clase, definida en la RFC 9110, sección 15:
| Clase | Significado | Ejemplos |
|---|---|---|
| 1xx | Informativo. La solicitud se recibió y el proceso continúa | 100 Continue |
| 2xx | Éxito | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirección. El cliente debe dar otro paso | 301 Moved Permanently, 304 Not Modified |
| 4xx | Error del cliente. La solicitud tiene un problema | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed |
| 5xx | Error del servidor. No pudo atender una solicitud válida | 500, 501 Not Implemented, 502, 503 |
Aunque un cliente no reconozca un código concreto, puede actuar según la clase. Un 4xx indica que la solicitud debe cambiar. Un 5xx indica que la misma solicitud podría funcionar más tarde.
Dos códigos se confunden con frecuencia. Un 401 significa que la solicitud no tiene credenciales válidas. Un 403 significa que el servidor entendió la solicitud y la rechaza, incluso con credenciales.
La frase de motivo, como Not Found, es un texto que elige el servidor. La RFC 9112, sección 4, dice que el cliente debe ignorarla. Escribe la lógica sobre el número, nunca sobre las palabras.
Las cookies no forman parte de la RFC 9110. Se definen en la RFC 6265. El ciclo funciona así:
Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Lax.Cookie: session=abc123.Una cookie de sesión es un identificador que entrega el servidor. No es una contraseña y no es privada del usuario. Quien tenga el valor normalmente puede actuar como esa sesión hasta que expire o se revoque. HttpOnly impide que los scripts de la página lean el valor. Secure permite enviarla solo por HTTPS. SameSite limita cuándo el navegador la envía en solicitudes entre sitios. Los artículos de seguridad web volverán sobre estos atributos.
Necesitas curl y Python 3. Git Bash en Windows 11 incluye curl, y la mayoría de sistemas Linux y macOS también lo tienen. El servidor es http.server, que viene integrado en Python. Ejecútalo solo en tu máquina, enlazado a 127.0.0.1, y nunca lo expongas a una red.
La salida de abajo proviene de ejecutar estos comandos en Windows 11 con Git Bash, curl 8.12.1 y Python 3.12.10. En esta máquina python3 no estaba en el PATH de Git Bash, así que el servidor se inició con la ruta completa al ejecutable de Python 3.12. Usa python3, py -3 o python, según tu sistema. Tus fechas, horas y versiones serán distintas. La barra de progreso de curl se quitó de la salida, y los cuerpos HTML largos se acortaron.
En la primera terminal:
mkdir httplab && cd httplab && echo '<h1>hello</h1>' > index.html
python3 -m http.server 8000 --bind 127.0.0.1Deja esta terminal abierta. El servidor escribe una línea de registro por cada solicitud, así que cada solicitud que envíes abajo aparecerá aquí.
En la segunda terminal:
curl -v http://127.0.0.1:8000/Salida:
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.12.1
> Accept: */*
>
* Request completely sent off
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Date: Sun, 08 Nov 2026 08:16:48 GMT
< Content-type: text/html
< Content-Length: 15
< Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
<
<h1>hello</h1>
* shutting down connection #0Léelo en este orden:
* son notas de curl sobre la conexión.> son lo que curl envió. Curl añadió por su cuenta Host, User-Agent y Accept.< son lo que devolvió el servidor.> vacía termina los encabezados de la solicitud. La línea < vacía termina los encabezados de la respuesta.<h1>hello</h1> es el cuerpo. Content-Length: 15 coincide con el archivo, porque echo añadió un salto de línea después de la etiqueta.HTTP 1.0, assume close after body muestra una discrepancia. Curl pidió HTTP/1.1, pero el servidor respondió con HTTP/1.0. El servidor simple de Python habla HTTP/1.0, así que curl cierra la conexión cuando termina de leer el cuerpo.curl -i http://127.0.0.1:8000/missingLa opción -i muestra los encabezados de la respuesta junto con el cuerpo. Salida, con el cuerpo HTML acortado:
HTTP/1.0 404 File not found
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:49 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 335
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 404</p>
<p>Message: File not found.</p>
<p>Error code explanation: 404 - Nothing matches the given URI.</p>
</body>
</html>Mira la primera terminal. Registró:
127.0.0.1 - - [08/Nov/2026 05:16:49] code 404, message File not found
127.0.0.1 - - [08/Nov/2026 05:16:49] "GET /missing HTTP/1.1" 404 -El código de estado es 404, un código de la clase de error del cliente: la ruta no corresponde a ningún recurso. La frase de motivo File not found es el texto que eligió el propio servidor. El cuerpo es una página HTML que el servidor generó para el error. Cualquier estado puede llevar cualquier cuerpo, así que lee ambos.
El registro muestra las 05:16 y el encabezado Date muestra las 08:16. El registro usa la hora local y el encabezado usa GMT, así que los dos difieren en el desfase horario local.
curl -i -X POST http://127.0.0.1:8000/Salida, con el cuerpo HTML acortado:
HTTP/1.0 501 Unsupported method ('POST')
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:49 GMT
Connection: close
Content-Type: text/html;charset=utf-8
Content-Length: 357
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error response</title>
</head>
<body>
<h1>Error response</h1>
<p>Error code: 501</p>
<p>Message: Unsupported method ('POST').</p>
<p>Error code explanation: 501 - Server does not support this operation.</p>
</body>
</html>La línea de registro dice code 501, message Unsupported method ('POST'). La opción -X cambia solo el método. Curl envió los mismos encabezados que antes.
El servidor simple no tiene ningún manejador de POST, así que 501 Not Implemented es la respuesta honesta. La RFC 9110, sección 15.6.2, define el 501 para un servidor que no ofrece la funcionalidad que la solicitud necesita. Una aplicación real que conoce el recurso pero rechaza POST en él normalmente respondería 405 Method Not Allowed. Una respuesta 405 debe incluir un encabezado Allow con los métodos que acepta el recurso.
curl -I http://127.0.0.1:8000/index.htmlSalida:
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.10
Date: Sun, 08 Nov 2026 08:16:50 GMT
Content-type: text/html
Content-Length: 15
Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
La opción -I envía una solicitud HEAD. El estado y los encabezados coinciden con los que devolvería un GET, pero no llega ningún cuerpo. Content-Length sigue diciendo 15, porque describe la representación que devolvería un GET. La RFC 9110, sección 9.3.2, define este comportamiento. El registro muestra "HEAD /index.html HTTP/1.1" 200 -.
HEAD sirve cuando quieres saber si un archivo existe, cuánto pesa o cuándo cambió por última vez, sin descargarlo.
curl -v -H 'Cookie: session=demo123' http://127.0.0.1:8000/Las líneas relevantes de la salida:
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.12.1
> Accept: */*
> Cookie: session=demo123
>
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Date: Sun, 08 Nov 2026 08:18:28 GMT
< Content-type: text/html
< Content-Length: 15
< Last-Modified: Sun, 08 Nov 2026 08:13:48 GMT
< La línea del cuerpo se omite aquí. El servidor respondió con el mismo 200 de antes, porque no tiene lógica de cookies e ignora el encabezado. Ese es el sentido del ejercicio. El navegador envía cookies, pero quien decide qué significan es el servidor. Un servidor que nunca define una cookie no va a actuar sobre una que tú envíes.
Pulsa Ctrl+C en la primera terminal cuando termines.
Host, User-Agent, Cookie y X-Forwarded-For vienen todos del lado del cliente. Valídalos en el servidor.127.0.0.1.-X cambie los encabezados. Cambia solo el método. Para añadir encabezados usa -H.Content-Length y Transfer-Encoding.:8000 de la URL.Un proxy como ZAP muestra estos mismos mensajes, y por eso este artículo sirve de base para los siguientes, sobre vulnerabilidades web. La API de NotHoly, en api.notholy.com/v1, también es una API HTTP con cuerpos JSON, servida sobre TLS, así que los encabezados y códigos de estado de arriba se aplican cuando la llamas.
Redes y web
Un puerto es una entrada numerada en tu máquina. Entiende cómo TCP y UDP usan puertos y prueba un servidor local en 127.0.0.1.
Redes y web
TLS 1.3 aporta a HTTPS confidencialidad, integridad y autenticación del servidor. Explica el handshake, los certificados y lo que el candado no prueba.
Redes y web
Cómo DNS convierte nombres en direcciones: resolvers, servidores autoritativos, caché por TTL, registros A, AAAA, CNAME, MX y TXT, con prueba segura.