Terminal de Linux ejecutando chmod sobre un archivo y devolviendo Permission denied al gestionar permisos en Linux con chmod
Un chmod u-r deja el archivo sin permiso de lectura y cat responde "Permission denied": permisos en acción.

Administrar permisos en Linux con chmod (de 777 al hardening)

En Linux, los permisos deciden quién puede leer, cambiar o ejecutar cada archivo: son el control de acceso más básico del sistema. Entender y aplicar chmod en Linux bien es la diferencia entre un servidor ordenado y uno donde “cualquiera puede hacer cualquier cosa”.

En esta guía vamos de la mecánica —cómo se leen y se cambian los permisos— al hardening real: por qué chmod 777 es peligroso, cómo aplicar el privilegio mínimo, qué permisos usar en un servidor web y cómo automatizarlo y auditarlo, incluso en Docker y CI/CD.

El comando chmod en Linux cambia los permisos de un archivo o directorio: define quién puede leerlo (r), escribirlo (w) o ejecutarlo (x), y para quién —propietario, grupo u otros—. Se usa en modo simbólico (u+x) o numérico/octal (755), y aplicarlo con criterio es la base del control de acceso y del hardening del sistema.

Salida de ls -l mostrando los permisos de archivos con chmod en Linux
Los diez caracteres de ls -l: tipo de archivo + permisos de usuario, grupo y otros.

Qué son los permisos en Linux y por qué importan

Cada archivo y directorio en Linux tiene un dueño, un grupo y un conjunto de permisos que definen quién puede leerlo, modificarlo o ejecutarlo. Ese modelo es simple, pero es lo que sostiene la seguridad del sistema: un permiso de más en el archivo equivocado abre la puerta a que otro usuario —o un atacante— lea un secreto o ejecute código.

Los permisos se organizan en tres clases de destinatarios (usuario, grupo y otros) y tres tipos de acción (lectura, escritura y ejecución). Usar chmod en Linux es decidir, para cada archivo, quién puede leer, escribir o ejecutar.

Cómo leer ls -l: de -rw-r--r-- a la práctica

El comando ls lista las entradas del directorio actual con sus permisos. Ejecuta:

ls -l

La salida de cada entrada arranca con diez caracteres, por ejemplo -rw-rw-r--. El primero indica el tipo (- archivo, d directorio); los nueve siguientes son los permisos, en tres grupos de tres:

user (usuario)group (grupo)other (otros)
r w xr w xr w x
  • r (read): leer el contenido del archivo.
  • w (write): escribir o modificar el archivo.
  • x (execute): ejecutar el archivo si es un programa o script; en un directorio, atravesarlo para acceder a su contenido.

Cuando un permiso está revocado, su letra se reemplaza por un guion (-). Las tres clases determinan a quién se aplica: u (user) al propietario, g (group) al grupo asignado, o (others) a todos los demás usuarios del sistema.

Un ejemplo de lectura: si el usuario tiene rw-, el propietario lee y escribe pero no ejecuta; si el grupo tiene r-x, lee y ejecuta; si otros tienen r--, cualquiera en el sistema solo puede leer. Además de los permisos, ls -l te muestra el propietario, el grupo, el tamaño en bytes, la fecha de modificación y el nombre.

chmod en modo simbólico y numérico (octal)

El comando chmod (change mode) cambia esos permisos. En modo simbólico se construye así:

  1. A quién afecta: u, g, o, o combinaciones (a = todos).
  2. La acción: + añade, - revoca, = asigna exactamente.
  3. Qué permiso: r, w, x, o combinaciones.
  4. Varias cláusulas se reúnen en el mismo argumento de modo separándolas con comas; al final va el nombre del archivo.

Por ejemplo, para revocar la lectura al usuario en Geek.txt:

chmod u-r Geek.txt

El modo numérico (octal) representa cada terna con un dígito, sumando 4 (r) + 2 (w) + 1 (x):

OctalBinarioPermisos
0000
1001–x
2010-w-
3011-wx
4100r–
5101r-x
6110rw-
7111rwx

Los dos modos son equivalentes en los nueve bits de permiso —los bits especiales no se tocan—. Estas parejas hacen lo mismo:

# Lectura, escritura y ejecución (7) a todos
chmod ugo+rwx archivo
chmod 777 archivo

# Usuario lee (4), grupo escribe y ejecuta (3), otros leen y ejecutan (5)
chmod u=r,g=wx,o=rx archivo
chmod 435 archivo

# Usuario y grupo todo (7), otros leen y ejecutan (5)
chmod ug+rwx,o=rx archivo
chmod 775 archivo

La referencia completa de los modos simbólicos, numéricos y de los bits especiales vive en la man page oficial de chmod.

Permisos típicos y cuándo usarlos

En la práctica, unos pocos valores cubren casi todo. Esta es la tabla de referencia rápida que conviene tener a mano:

PermisoPropietarioGrupoOtrosUso típicoRiesgo
700rwxDirectorios y scripts privados de un solo usuarioMuy bajo
750rwxr-xScripts que comparte un grupo, no el restoBajo
755rwxr-xr-xDirectorios y ejecutables públicosMedio
644rw-r–r–Archivos web (HTML, CSS, PHP)Bajo
600rw-Secretos: claves, .env, credencialesMuy bajo
777rwxrwxrwxNinguno recomendableCrítico: evítalo

La regla mental: da el menor permiso que deje funcionar la tarea. Casi nada necesita 777.

chmod 777: por qué es peligroso

“Si pongo 777 funciona, así que no hay problema.” Es el atajo que más incidentes causa. chmod 777 otorga lectura, escritura y ejecución al propietario, al grupo y a todos los demás: es el estado en el que cualquiera puede hacer cualquier cosa.

El peligro se ve claro en un servidor web. Para que el riesgo se materialice deben darse varias condiciones: que la subida de archivos no esté validada, que el archivo caiga dentro del webroot y que el servidor esté configurado para ejecutar lo que se sube (por ejemplo, interpretar PHP).

Cuando se cumplen, un atacante puede subir un archivo PHP malicioso a esa carpeta 777 y ejecutarlo, y a partir de ahí correr comandos arbitrarios en el servidor. El 777 no es toda la cadena, pero es la pieza que la habilita.

Este riesgo —permisos excesivos en recursos web— está catalogado en la guía OWASP WSTG – Test File Permission, que además detalla cómo probarlo y remediarlo con permisos mínimos.

Hay un segundo escenario, más silencioso: un script world-writable ejecutado con privilegios. Si un script que corre root (por cron, un servicio de systemd o una tarea de despliegue) tiene permisos 777, cualquier usuario sin privilegios del sistema puede modificar su contenido; la próxima vez que root lo ejecute, correrá el código inyectado con permisos de root.

La escalada de privilegios no explota una vulnerabilidad del kernel: explota un permiso mal puesto.

Flujo de escalada de privilegios por un script con permisos chmod 777
Un script 777 ejecutado por root convierte un permiso mal puesto en escalada de privilegios.

Conviene distinguir el 777 en un archivo del 777 en un directorio: en un directorio, el permiso de escritura (w) junto con el de acceso (x) para otros significa que cualquiera puede crear o borrar archivos dentro, aunque no sea su dueño.

Para los directorios verdaderamente compartidos (tipo /tmp) existe el sticky bit, que tratamos en detalle en nuestra guía de permisos SUID, SGID y sticky bit; aquí basta con la regla: fuera de esos casos, un directorio no debería ser world-writable.

Privilegio mínimo aplicado a chmod y chown

El principio que ordena todo esto es el privilegio mínimo (Principle of Least Privilege): otorgar solo los permisos necesarios, a los usuarios necesarios, y nada más. El privilegio mínimo es un control de seguridad formal, el AC-6 de NIST SP 800-53. Aplicado a chmod y chown, se traduce en tres hábitos:

  • Directorios en 755, archivos en 644 como base; sube o baja solo cuando la tarea lo exija.
  • Ejecución solo donde se necesita ejecutar (scripts de shell, binarios), nunca por defecto.
  • Propietario correcto, no solo permisos correctos: de nada sirve un 644 si el dueño es un usuario que no debería tocarlo; por eso conviene saber administrar usuarios y grupos en Linux.

Reducir permisos y fijar el propietario adecuado recorta de golpe la superficie de acceso no autorizado y limita hasta dónde se puede propagar un incidente.

Permisos seguros para servidores web (755/644 y variantes)

La receta base de un sitio web es directorios en 755 y archivos en 644. El porqué: el servidor (Apache, nginx, PHP-FPM) necesita leer los archivos y entrar a los directorios (la x de un directorio permite atravesarlo), pero no necesita conceder escritura al grupo ni a otros. Con 644 en archivos y 755 en directorios, solo el propietario conserva el bit de escritura y el contenido se sirve sin que nadie externo lo modifique.

Directorios en 755:

chmod 755 uploads/ public/ logs/

Comprobación:

ls -ld uploads/
# drwxr-xr-x 2 www-data www-data 4096 Jul 4 10:00 uploads

Archivos en 644:

chmod 644 config.php index.php style.css

Comprobación:

ls -l config.php
# -rw-r--r-- 1 www-data www-data 1234 Jul 4 10:00 config.php

En las comprobaciones, el propietario y el grupo aparecen como www-data porque se fijan por separado (lo vemos en la sección 8): chmod cambia el modo, no el dueño.

Verificación con ls de los permisos 755 y 644 aplicados con chmod
Permisos 755 en directorios y 644 en archivos, verificados con ls -l.

Los archivos ejecutables (scripts) son la excepción: solo a ellos se les da ejecución, con 755:

chmod 755 backup.sh

Donde backup.sh contiene, por ejemplo:

#!/bin/bash
tar -czf backup.tar.gz /var/www/html

Para los directorios que sí deben ser escribibles por la aplicación (uploads, cache, logs), no recurras al 777: asigna la propiedad de esas rutas concretas al usuario del servicio (www-data) y mantén el permiso lo más cerrado posible; eso no obliga a que todo el árbol pertenezca al servicio. Un archivo de configuración con secretos va en 600, para que solo su dueño lo lea.

Automatización con find: permisos en lote y auditoría

Aplicar los permisos uno a uno no escala. Al desplegar una aplicación, se hace en lote separando directorios de archivos:

# Directorios a 755
find /var/www/html -type d -exec chmod 755 {} +

# Archivos a 644
find /var/www/html -type f -exec chmod 644 {} +

Este pase es una normalización base: asigna 755 a todos los directorios y 644 a todos los archivos que encuentra, así que reaplica después las excepciones (los scripts que deban quedar en 755 y los secretos en 600).

Si tu modelo de despliegue asigna todo el árbol al usuario del servicio, la propiedad se fija de una vez:

sudo chown -R www-data:www-data /var/www/html
ls -la /var/www/html

La otra cara de find es la auditoría: buscar lo que no debería existir. Para detectar permisos world-writable y modos 777:

# Archivos world-writable bajo /var/www
find /var/www -type f -perm -o=w

# Todo lo que esté exactamente en 777
find /var/www/html -perm 777

Convertir esa auditoría en un hábito (o en un paso automático) es lo que evita que un 777 “temporal” se quede para siempre.

Permisos en Docker y CI/CD

En contenedores, los permisos se fijan durante la construcción, en el Dockerfile. El patrón: copiar el contenido asignando ya la propiedad a www-data y normalizar directorios a 755 y archivos a 644.

FROM php:8.3-apache
WORKDIR /var/www/html
COPY --chown=www-data:www-data . .
RUN find /var/www/html -type d -exec chmod 755 {} + \
    && find /var/www/html -type f -exec chmod 644 {} +

Aquí no fijamos USER www-data: la imagen php:8.3-apache necesita arrancar como root para enlazar el puerto 80 (un puerto privilegiado) y, hecho eso, degrada los procesos de trabajo a www-data por su cuenta. Forzar USER www-data impediría enlazar el puerto y el contenedor no arrancaría. Como en el despliegue, esta normalización es la base y no conserva por sí sola las excepciones ya vistas.

En CI/CD, la idea es que el pipeline falle si aparece un 777 en el árbol del proyecto. El script siguiente busca coincidencias exactas con el modo 777 —no todo lo world-writable; para eso se cambia a -perm -o=w—:

#!/bin/bash
FOUND=$(find /var/www/html -perm 777)
if [ -n "$FOUND" ]; then
  echo "Peligro: hay archivos con permisos 777"
  echo "$FOUND"
  exit 1
fi
echo "Revisión de permisos OK"

Integrado en GitHub Actions:

name: Security Check
on: [push]
jobs:
  permission-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check permissions
        run: |
          if find . -perm 777 | grep .; then
            echo "777 permissions found"
            exit 1
          fi

Con este control, un modo exactamente 777 deja de depender de que alguien lo recuerde: el pipeline falla si lo encuentra en el árbol que examina.

Errores comunes y cómo solucionarlos

  • “Permission denied” al ejecutar un script: revisa si falta la x (chmod +x script.sh) o si el problema es de propietario, no de permisos; el mensaje por sí solo no distingue cuál de los dos. Comprueba ambos con ls -l.
  • 644 en un directorio: un directorio sin x no se puede atravesar. La base para directorios públicos es 755; 700 y 750 siguen siendo válidos cuando el acceso debe restringirse al propietario o al grupo.
  • chmod -R 777 “para que funcione”: nunca. El recursivo propaga el error a todo el árbol; usa find separando archivos y directorios.
  • Cambios recursivos con -R: revisa antes qué abarca la ruta; un -R en el directorio equivocado reescribe permisos que no querías tocar. (Si necesitas ajustar el valor por defecto con el que se crean los archivos, ese es el terreno de umask.)

Preguntas frecuentes (FAQ)

¿Qué significa chmod 755?

Da rwx al propietario y r-x al grupo y a otros. Es el permiso típico de directorios y de archivos ejecutables públicos.

¿Qué significa chmod 644?

Da rw- al propietario y r-- al grupo y a otros. El propietario conserva escritura; el grupo y otros solo leen. Es el permiso estándar de los archivos de un sitio web.

¿Por qué chmod 777 es peligroso?

Porque permite a cualquiera leer, escribir y ejecutar. En un servidor web mal configurado habilita subir y ejecutar código malicioso, y en un script ejecutado por root abre la puerta a la escalada de privilegios.

¿Es seguro usar chmod -R 777?

No. Propaga el permiso más inseguro a todo un árbol de archivos y directorios. Si necesitas aplicar permisos en lote, usa find separando -type f (644) y -type d (755).

¿Cómo cambio permisos de forma recursiva de manera segura?

Con find: find ruta -type d -exec chmod 755 {} + para directorios y find ruta -type f -exec chmod 644 {} + para archivos. Ese pase es una normalización base: reaplica después las excepciones (scripts 755, secretos 600).

¿Cómo veo los permisos de un archivo?

Con ls -l (o ls -ld para un directorio): los primeros diez caracteres muestran el tipo y los permisos por usuario, grupo y otros.

Mi Carro Close (×)

Tu carrito está vacío
Ver tienda