Michael Howard y Keith Brown
Este artículo asume que está familiarizado con C++, C# y SQL.
Resumen: Cuando se trata de cuestiones de seguridad, hay muchas situaciones que pueden generar problemas. Probablemente confíe en todo el código que se ejecuta en su red, brinde a todos los usuarios acceso a archivos importantes y nunca se moleste en verificar si el código en sus máquinas ha cambiado. Es posible que tampoco tenga instalado un software antivirus, que no proteja su propio código y que otorgue demasiados permisos a demasiadas cuentas. Incluso puede ser descuidado al utilizar una serie de funciones integradas que permiten intrusiones maliciosas y puede dejar los puertos del servidor abiertos sin ningún tipo de supervisión. Evidentemente, podemos poner muchos más ejemplos. ¿Cuáles son los problemas realmente importantes (es decir, los errores más peligrosos a los que se debe prestar atención inmediata para evitar comprometer sus datos y sistemas)? Los expertos en seguridad Michael Howard y Keith Brown ofrecen diez consejos para ayudarle.
-------------------------------------------------- ----------------------------------
Las cuestiones de seguridad implican muchos aspectos. Los riesgos de seguridad pueden venir de cualquier parte. Es posible que haya escrito un código de manejo de errores ineficaz o que haya sido demasiado generoso al otorgar permisos. Es posible que haya olvidado qué servicios se están ejecutando en su servidor. Puede aceptar todas las entradas del usuario. Etcétera. Para darle una ventaja en la protección de su computadora, red y código, aquí hay diez consejos que puede seguir para una estrategia de red más segura.
1. Confiar en los comentarios de los usuarios lo pone en riesgo
Incluso si no lee el resto, recuerde esto: "No confíe en los comentarios de los usuarios". El problema surge si siempre asumes que los datos son válidos y no maliciosos. La mayoría de las vulnerabilidades de seguridad implican que los atacantes introduzcan datos escritos de forma maliciosa en los servidores.
Confiar en la exactitud de la entrada puede provocar desbordamientos del búfer, ataques de secuencias de comandos entre sitios, ataques de código de inserción SQL y más.
Analicemos en detalle estos posibles vectores de ataque.
2. Evitar el desbordamiento del búfer
Cuando un atacante proporciona una longitud de datos mayor que la esperada por la aplicación, se produce un desbordamiento del búfer y los datos se desbordan en el espacio de la memoria interna. El desbordamiento del búfer es principalmente un problema de C/C++. Son una amenaza, pero normalmente son fáciles de solucionar. Sólo hemos visto dos desbordamientos de búfer que no eran obvios y difíciles de solucionar. El desarrollador no anticipó que los datos proporcionados externamente serían mayores que el búfer interno. El desbordamiento provoca la corrupción de otras estructuras de datos en la memoria, lo que a menudo los atacantes aprovechan para ejecutar código malicioso. Los errores de índice de matriz también pueden provocar desbordamientos y desbordamientos del búfer, pero esto es menos común.
Eche un vistazo al siguiente fragmento de código C++:
vacío HacerAlgo(char *cBuffSrc, DWORD cbBuffSrc) {
char cBuffDest[32];
memcpy(cBuffDest,cBuffSrc,cbBuffSrc);
}
¿Cuál es el problema? De hecho, este código no tiene nada de malo si cBuffSrc y cbBuffSrc provienen de una fuente confiable (como un código que no confía en los datos y, por lo tanto, verifica su validez y tamaño). Sin embargo, si los datos provienen de una fuente que no es confiable y no han sido verificados, entonces un atacante (fuente no confiable) puede fácilmente hacer que cBuffSrc sea mayor que cBuffDest y también configurar cbBuffSrc para que sea mayor que cBuffDest. Cuando memcpy copia los datos en cBuffDest, la dirección de retorno de DoSomething cambia y, debido a que cBuffDest está adyacente a la dirección de retorno en el marco de pila de la función, el atacante puede realizar algunas operaciones maliciosas a través del código.
La forma de compensar es no confiar en la entrada del usuario y no confiar en los datos contenidos en cBuffSrc y cbBuffSrc:
void DoSomething(char *cBuffSrc, DWORD cbBuffSrc) {
constante DWORD cbBuffDest = 32;
char cBuffDest[cbBuffDest];
#ifdef _DEBUG
memset(cBuffDest, 0x33, cbBuffSrc);
#endif
memcpy(cBuffDest, cBuffSrc, min(cbBuffDest, cbBuffSrc));
}
Esta función demuestra tres propiedades de una función escrita correctamente que puede reducir los desbordamientos del búfer. Primero, requiere que la persona que llama proporcione la longitud del búfer. ¡Por supuesto, no puedes confiar ciegamente en este valor! A continuación, en una compilación de depuración, el código detecta si el búfer es realmente lo suficientemente grande como para contener el búfer de origen. De lo contrario, se puede desencadenar una infracción de acceso y el código se puede cargar en el depurador. Al depurar, se sorprenderá de la cantidad de errores que encontrará. Por último, y lo más importante, las llamadas a memcpy son defensivas porque no copian más datos de los que puede contener el búfer de destino.
Como parte del impulso de seguridad de Windows® en Microsoft, creamos una lista de funciones seguras de manejo de cadenas para programadores de C. Puede encontrarlos en Strsafe.h: Safer String Handling in C (inglés).
3. Evite las secuencias de comandos entre sitios
Los ataques de secuencias de comandos entre sitios son un problema único de la Web. Pueden dañar los datos del cliente a través de una vulnerabilidad oculta en una sola página web. Imagine las consecuencias del siguiente fragmento de código ASP.NET:
<script language=c#>
Response.Write("Hola", + Request.QueryString("nombre"));
</script>
¿Cuántas personas han visto un código similar? ¡Pero sorprendentemente tiene problemas! Normalmente, los usuarios accederán a este código mediante una URL similar a la siguiente:
http://explorationair.com/welcome.aspx?name=Michael
El código C# supone que los datos siempre son válidos y solo contienen un nombre. Sin embargo, un atacante podría abusar de este código proporcionando script y código HTML como nombres. Si ingresa la siguiente URL
http://northwindtraders.com/welcome.aspx?name=<script>alert(' ¡Hola!');
</script>
Aparecerá una página web con un cuadro de diálogo que dice "¡Hola!" Podría estar diciendo: "¿Y qué?" Imagine que un atacante pudiera engañar a un usuario para que hiciera clic en un enlace como este, pero la cadena de consulta contenía un script y HTML realmente peligroso, obteniendo así la cookie del usuario y enviándola a un sitio web propiedad de. el atacante ahora tiene acceso a su información privada de cookies, o algo peor.
Para evitar esto, hay dos formas. La primera es desconfiar del input y limitar estrictamente lo que contiene el nombre de usuario. Por ejemplo, puede utilizar expresiones regulares para comprobar que el nombre sólo contiene un subconjunto común de caracteres y no es demasiado grande.
de
código C# muestra cómo realizar este paso:
Regex r = new Regex(@"^[w]{1,40}$");
// ¡bien! La cuerda está bien.
} demás {
// ¡no es bueno! Cadena no válida
}
Este código utiliza expresiones regulares para verificar que una cadena contenga solo de 1 a 40 letras o números. Ésta es la única forma segura de determinar si un valor es correcto.
¡No hay forma de que HTML o script puedan engañar a esta expresión regular! No utilice expresiones regulares para buscar caracteres no válidos y rechace solicitudes si se encuentran dichos caracteres no válidos, porque es fácil pasar por alto algo.
La segunda precaución es codificar en HTML todas las entradas como salidas. Esto reduce las etiquetas HTML peligrosas a caracteres de escape más seguros. Puede utilizar HttpServerUtility.HtmlEncode en ASP.NET o Server.HTMLEncode en ASP para escapar de cualquier cadena potencialmente problemática.
4. No solicite permisos sa
El último ataque de confianza de entrada que discutiremos es la inserción de código SQL. Muchos desarrolladores escriben código que toma información y la utiliza para crear consultas SQL que se comunican con un almacén de datos backend como Microsoft® SQL Server™ u Oracle.
Eche un vistazo al siguiente fragmento de código:
void DoQuery(string Id) {
SqlConnection sql=nueva SqlConnection(@"fuente de datos=localhost;" +
"id de usuario=sa;contraseña=contraseña;");
sql.Open();
sqlstring= "SELECCIONAR enviado" +
" DESDE el envío DONDE id='" + Id + "'";
SqlCommand cmd = nuevo SqlCommand(sqlstring,sql);
•••
Este código tiene tres defectos graves. Primero, establece una conexión desde el servicio web a SQL Server como cuenta de administrador del sistema sa. Pronto verás los peligros de esto. Segundo punto, ¡preste atención a la práctica inteligente de utilizar "contraseña" como contraseña para la cuenta sa!
Pero la verdadera preocupación es la concatenación de cadenas que construye las sentencias SQL. Si el usuario ingresa 1001 como ID, obtendrá la siguiente declaración SQL, que es completamente válida.
SELECCIONE hasshipped FROM envío DONDE id = '1001'
Pero los atacantes son mucho más creativos que eso. Ingresarían una "tabla DROP '1001' de envío --" para el ID, lo que ejecutaría una consulta como esta:
SELECCIONAR enviado DESDE
envío DONDE id = '1001'
Envío de tabla DROP -- ';
Cambia la forma en que funciona la consulta. Este código no solo intenta determinar si algo se ha enviado, sino que también procede a eliminar (eliminar) la tabla de envío. Operador: es el operador de comentarios en SQL, lo que facilita a los atacantes la construcción de una serie de declaraciones SQL válidas pero peligrosas.
En este momento, quizás te preguntes cómo cualquier usuario puede eliminar la tabla en la base de datos de SQL Server. Por supuesto que tienes razón, sólo los administradores pueden hacer ese trabajo. Pero aquí te estás conectando a la base de datos como sa, y sa puede hacer lo que quiera en la base de datos de SQL Server. Nunca se conecte a SQL Server como sa desde ninguna aplicación; el método correcto es utilizar la autenticación integrada de Windows, si corresponde, o conectarse como una cuenta predefinida con los permisos adecuados.
Solucionar problemas de código de inserción SQL es fácil. Utilizando parámetros y procedimientos almacenados de SQL, el siguiente código muestra cómo crear dicha consulta y cómo usar expresiones regulares para confirmar que la entrada es válida, ya que nuestra transacción especifica que el ID del envío solo puede tener de 4 a 10 dígitos:
Regex r = nueva expresión regular(@"^d{4,10}$");
si (!r.Match(Id).Éxito)
lanzar nueva excepción ("ID no válida");
SqlConnection sqlConn = nueva SqlConnection (strConn);
cadena str="sp_HasShipped";
SqlCommand cmd = nuevo SqlCommand(str,sqlConn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ID",Id);
Los desbordamientos de búfer, las secuencias de comandos entre sitios y los ataques de código de inserción SQL son ejemplos de problemas de entrada confiable. Todos estos ataques se mitigan mediante un mecanismo que considera que toda entrada es dañina a menos que se demuestre lo contrario.
5. ¡Presta atención al código de cifrado!
Echemos un vistazo a algo que podría sorprendernos. Descubrí que más del treinta por ciento del código de seguridad que examinamos tenía vulnerabilidades de seguridad. Quizás la vulnerabilidad más común sea su propio código de cifrado, que probablemente sea vulnerable. Nunca cree su propio código de cifrado, es una tontería. No crea que solo porque tiene su propio algoritmo de cifrado otros no pueden descifrarlo. Los atacantes tienen acceso a depuradores y también tienen el tiempo y el conocimiento para determinar cómo funcionan los sistemas, a menudo rompiéndolos en cuestión de horas. Debe utilizar Win32® CryptoAPI, el espacio de nombres System.Security.Cryptography proporciona una serie de algoritmos de cifrado excelentes y probados.
6. Reducir la posibilidad de ser atacado.
Si más del 90% de los usuarios no lo solicitan, no se debe instalar una función de forma predeterminada. Internet Information Services (IIS) 6.0 sigue esta recomendación de instalación, sobre la cual puede leer en el artículo de Wayne Berry "Las innovaciones en Internet Information Services le permiten proteger firmemente los datos seguros y los procesos del servidor", publicado este mes. La idea detrás de esta estrategia de instalación es que no prestará atención a los servicios que no está utilizando y, si esos servicios se están ejecutando, otros podrían explotarlos. Si una función está instalada de forma predeterminada, debe ejecutarse según el principio de autorización mínima. Es decir, no permita que se ejecuten aplicaciones con privilegios de administrador a menos que sea necesario. Lo mejor es seguir este consejo.
7. Utilice el principio de autorización mínima.
Los sistemas operativos y Common Language Runtimes tienen una política de seguridad por varias razones. Mucha gente supone que la razón principal por la que existe esta política de seguridad es para evitar que los usuarios causen daños intencionalmente: accediendo a archivos a los que no están autorizados, reconfigurando la red para adaptarla a sus necesidades y otros comportamientos atroces. Sí, este tipo de ataque interno es común y es necesario protegerse contra él, pero hay otra razón para apegarse a esta estrategia de seguridad. Es decir, construir barreras defensivas alrededor del código para evitar que los usuarios causen estragos en la red mediante acciones intencionales o (como suele suceder) no intencionales. Por ejemplo, un archivo adjunto descargado por correo electrónico, cuando se ejecuta en la máquina de Alice, está restringido a los recursos a los que Alice puede acceder. Si un archivo adjunto contiene un caballo de Troya, una buena estrategia de seguridad es limitar el daño que puede causar.
Cuando diseña, crea e implementa aplicaciones de servidor, no puede dar por sentado que todas las solicitudes provienen de usuarios legítimos. Si un tipo malo le envía una solicitud maliciosa (con suerte, no) y su código se comporta mal, querrá que su aplicación tenga todas las defensas posibles para limitar el daño. Por lo tanto, creemos que su empresa está implementando políticas de seguridad no solo porque no confía en usted ni en su código, sino también para protegerse de código externo malicioso.
El principio de autorización mínima sostiene que los permisos mínimos requeridos por el código deben otorgarse en el menor tiempo posible. Dicho esto, levante tantos muros protectores como sea posible alrededor de su código en todo momento. Cuando sucede algo malo, como garantiza la Ley de Murphy, se alegrará de que esos muros protectores estén en su lugar. Por lo tanto, a continuación se muestran algunos métodos específicos para ejecutar código utilizando el principio de autorización mínima.
Elija un entorno seguro para el código de su servidor que solo le permita acceder a los recursos necesarios para realizar su trabajo. Si algunas partes de su código requieren permisos elevados, considere aislar esa parte del código y ejecutarla por separado con permisos más altos. Para separar de forma segura este código que se ejecuta con información de autenticación de sistema operativo diferente, es mejor ejecutar este código en un proceso separado (ejecutándose en un entorno seguro con privilegios más altos). Esto significa que necesitará comunicación entre procesos (como comunicación remota COM o Microsoft .NET) y deberá diseñar la interfaz para ese código para minimizar los viajes de ida y vuelta.
Si separa el código en ensamblados en un entorno .NET Framework, considere los niveles de permiso necesarios para cada fragmento de código. Descubrirá que es un proceso sencillo: separe el código que requiere mayores privilegios en un ensamblado separado que le otorgue más privilegios, mientras deja la mayoría de los ensamblados restantes ejecutándose con privilegios más bajos para que pueda agregar más protecciones alrededor de su código. Al hacer esto, no olvide que, debido a la pila de Seguridad de acceso al código (CAS), está limitando los permisos no solo de su propio ensamblado, sino también de cualquier ensamblado al que llame.
Mucha gente crea sus propias aplicaciones para poder conectar nuevos componentes a sus productos después de probarlos y ponerlos a disposición de los clientes. Proteger este tipo de aplicaciones es muy difícil porque no se pueden probar todas las rutas de código posibles para encontrar errores y agujeros de seguridad. Sin embargo, si su aplicación está alojada, CLR proporciona una característica excelente que puede usarse para cerrar estos puntos de extensibilidad. Al declarar un objeto de permiso o un conjunto de permisos y llamar a PermitOnly o Deny, agrega una marca a su pila que bloqueará la concesión de permisos a cualquier código que llame. Al hacer esto antes de llamar a un complemento, puede limitar las tareas que el complemento puede realizar. Por ejemplo, un complemento para calcular los pagos a plazos no requiere ningún acceso al sistema de archivos. Este es sólo otro ejemplo de privilegio mínimo, mediante el cual te proteges de antemano. Asegúrese de tener en cuenta estas restricciones y tenga en cuenta que los complementos con mayores privilegios pueden usar declaraciones Assert para evadir estas restricciones.
8. Sea consciente de los patrones de fracaso
y acéptelos. Otros odian escribir código de manejo de errores tanto como usted. Hay muchas razones por las que el código puede fallar y puede resultar frustrante sólo pensar en ellas. La mayoría de los programadores, incluidos nosotros, prefieren centrarse en la ruta de ejecución normal. Ahí es donde realmente se hace el trabajo. Hagamos este manejo de errores lo más rápido y sin complicaciones posible, y luego pasemos a la siguiente línea de código real.
Desafortunadamente, esta emoción no es segura. En cambio, debemos prestar más atención a los patrones de falla en nuestro código. Este código a menudo se escribe con poca atención en profundidad y, a menudo, no se prueba por completo. ¿Recuerda la última vez que estuvo absolutamente seguro de haber depurado cada línea de código de una función, incluido cada pequeño controlador de errores que contenía?
El código no probado a menudo genera vulnerabilidades de seguridad. Hay tres cosas que pueden ayudarle a mitigar este problema. Primero, preste a esos pequeños controladores de errores la misma atención que a su código normal. Considere el estado del sistema cuando se ejecute su código de manejo de errores. ¿Está el sistema en un estado eficiente y seguro? En segundo lugar, una vez que haya escrito una función, revísela y depúrela minuciosamente varias veces, asegurándose de probar todos los controladores de errores. Tenga en cuenta que incluso con tales técnicas, es posible que no se descubran errores de sincronización muy sutiles. Es posible que deba pasar un parámetro de error a su función o ajustar el estado del sistema de alguna manera para permitir que se ejecute su controlador de errores. Si se toma el tiempo para revisar su código, puede reducir la velocidad y tener tiempo suficiente para ver su código y el estado de su sistema mientras se ejecuta. Al revisar cuidadosamente el código en el depurador, descubrimos muchas fallas en nuestra lógica de programación. Esta es una tecnología probada. Utilice esta técnica. Finalmente, asegúrese de que su conjunto de pruebas provoque que su función falle. Intente tener un conjunto de pruebas que examine cada línea de código de la función. Esto puede ayudarle a detectar patrones, especialmente al automatizar sus pruebas y ejecutarlas cada vez que crea su código.
Hay una cosa muy importante que decir acerca de los modos de falla. Asegúrese de que su sistema esté en el estado más seguro posible cuando su código falle. Parte del código problemático se muestra a continuación:
bool accessGranted = true; // ¡Demasiado optimista!
intentar {
// Ver si podemos acceder a c:test.txt
nuevo FileStream(@"c:test.txt",
ModoArchivo.Abrir,
FileAccess.Read).Close();
}
captura (SecurityException x) {
// Acceso denegado
accesoConcedido = falso;
}
atrapar (...) {
// Algo más pasó
}
Aunque estemos usando CLR, todavía podemos acceder al archivo. En este caso, no se lanza una SecurityException. Pero, ¿qué pasa si, por ejemplo, la Lista de control de acceso discrecional (DACL) del archivo no nos permite el acceso? En este momento, se lanzará otro tipo de excepción. Pero debido a las suposiciones optimistas de la primera línea de código, nunca lo sabremos.
Una mejor manera de escribir este código es ser cauteloso:
bool accessGranted = false // ¡Ten cuidado!
intentar {
// Ver si podemos acceder a c:test.txt
nuevo FileStream(@"c:test.txt",
ModoArchivo.Abrir,
FileAccess.Read).Close();
// Si todavía estamos aquí, ¡genial!
accesoConcedido = verdadero;
}
catch (...) {}
Esto será más estable porque no importa cómo fallemos, siempre volveremos al modo más seguro.
9. La suplantación es muy vulnerable
Al escribir aplicaciones de servidor, a menudo se encontrará utilizando, directa o indirectamente, una característica útil de Windows llamada suplantación. La suplantación permite que cada subproceso de un proceso se ejecute en un entorno de seguridad diferente, normalmente el del cliente. Por ejemplo, cuando un redirector del sistema de archivos recibe una solicitud de archivo a través de la red, autentica al cliente remoto, verifica que la solicitud del cliente no viole la DACL en el recurso compartido y luego adjunta la bandera del cliente al hilo que maneja la solicitud. . para simular al cliente. Luego, este hilo puede acceder al sistema de archivos local en el servidor utilizando el entorno de seguridad del cliente. Esto es conveniente ya que el sistema de archivos local ya es seguro. Realiza una verificación de acceso teniendo en cuenta el tipo de acceso solicitado, el DACL del archivo y el indicador de suplantación del hilo. Si la verificación de acceso falla, el sistema de archivos local lo informa al redirector del sistema de archivos, que luego envía un error al cliente remoto. Sin duda, esto es conveniente para el redirector del sistema de archivos, ya que simplemente pasa la solicitud al sistema de archivos local y le permite realizar sus propias comprobaciones de acceso, como si el cliente fuera local.
Todo esto está muy bien para una puerta de enlace simple como un redirector de archivos. Pero las simulaciones se utilizan a menudo en otras aplicaciones más complejas. Tomemos como ejemplo una aplicación web. Si escribe un programa ASP clásico no administrado, una extensión ISAPI o una aplicación ASP.NET y tiene la siguiente especificación en su archivo Web.config
<identity impersonate='true'>,
entonces su entorno de ejecución tendrá dos entornos de seguridad diferentes: Tendrá un etiqueta de proceso y una etiqueta de hilo En términos generales, la etiqueta de hilo se utilizará para verificar el acceso (consulte la Figura 3). Suponiendo que está escribiendo una aplicación ISAPI que se ejecuta en un proceso de servidor web y que la mayoría de las solicitudes no están autenticadas, la etiqueta de su hilo podría ser IUSR_MACHINE, pero su etiqueta de proceso es SISTEMA. Supongamos que su código puede ser explotado por un mal actor mediante un desbordamiento del búfer. ¿Crees que se contentará con ejecutarse simplemente como IUSR_MACHINE? Por supuesto que no. Lo más probable es que su código de ataque llame a RevertToSelf para eliminar el indicador de suplantación con la esperanza de aumentar su nivel de privilegio. En este caso, lo logrará fácilmente. También puede llamar a CreateProcess. No copia la etiqueta del nuevo proceso de la etiqueta de suplantación, sino de la etiqueta del proceso para que el nuevo proceso pueda ejecutarse como SISTEMA.
Entonces, ¿cómo solucionar este pequeño problema? Además de garantizar que no se produzcan desbordamientos del búfer en primer lugar, recuerde el principio de autorización mínima. Si su código no requiere privilegios tan grandes como SISTEMA, no configure su aplicación web para que se ejecute en un proceso de servidor web. Si simplemente configura su aplicación web para que se ejecute en un entorno de aislamiento medio o alto, su etiqueta de proceso será IWAM_MACHINE. En realidad, no tienes ningún permiso, por lo que este ataque casi no tiene ningún efecto. Tenga en cuenta que en IIS 6.0 (que pronto será un componente de Windows .NET Server), el código escrito por el usuario no se ejecutará como SISTEMA de forma predeterminada. Teniendo en cuenta que los desarrolladores cometen errores, cualquier ayuda que el servidor web pueda brindar para reducir los permisos otorgados al código es útil en caso de que haya un problema de seguridad en el código.
Aquí hay otro problema que pueden encontrar los programadores COM. COM tiene una mala tendencia a ignorar los hilos. Si llama a un servidor COM en proceso y su modelo de subproceso no coincide con el modelo del subproceso que llama, COM realiza la llamada en otro subproceso. COM no propaga el indicador de suplantación en el subproceso que llama, por lo que el resultado es que la llamada se ejecuta en el contexto de seguridad del proceso en lugar de en el contexto de seguridad del subproceso que llama. ¡Qué sorpresa!
He aquí otro ejemplo de los peligros de la simulación. Supongamos que su servidor acepta solicitudes enviadas a través de canalizaciones con nombre, DCOM o RPC. Usted autentica a los clientes y se hace pasar por ellos, abriendo objetos del kernel en su nombre mediante la suplantación. Y olvidó cerrar uno de los objetos (como un archivo) cuando el cliente se desconectó. Cuando llega el siguiente cliente, lo autentificas y te haces pasar por él, y ¿adivinas qué sucede? Aún puede acceder a archivos que el cliente anterior "perdió" incluso si el nuevo cliente no obtuvo acceso al archivo. Por motivos de rendimiento, el kernel sólo realiza comprobaciones de acceso a un objeto la primera vez que se abre. Aún puede acceder a este archivo incluso si luego cambia su entorno de seguridad porque se está haciendo pasar por otro usuario.
Todas las situaciones mencionadas anteriormente son para recordarle que la simulación brinda comodidad a los desarrolladores de servidores, pero esta conveniencia tiene grandes peligros ocultos. Cuando ejecuta un programa con una bandera simulada, asegúrese de prestar especial atención a su código.
10. Escriba aplicaciones que los usuarios no administradores puedan utilizar.
Esto es realmente un corolario del principio de autorización mínima. Si los programadores continúan desarrollando código que requiere un administrador para ejecutarse correctamente en Windows, no podemos esperar mejorar la seguridad del sistema. Windows tiene un conjunto muy sólido de características de seguridad, pero los usuarios no pueden aprovecharlas si tienen que ser administradores para operarlas.
¿Cómo puedes mejorar? Primero, pruébalo tú mismo, sin ejecutarlo como administrador. Pronto aprenderá lo complicado que resulta utilizar un programa que no fue diseñado teniendo en cuenta la seguridad. Un día, yo (Keith) instalé un software proporcionado por el fabricante de mi dispositivo portátil que sincronizaba datos entre mi computadora de escritorio y mi dispositivo portátil. Como de costumbre, salí de mi cuenta de usuario normal, volví a iniciar sesión con la cuenta de administrador integrada, instalé el software, luego volví a iniciar sesión en mi cuenta normal e intenté ejecutar el software. Como resultado, la aplicación muestra un cuadro de diálogo que dice que no se puede acceder a un archivo de datos requerido y luego muestra un mensaje de infracción de acceso. Amigos, este es el producto de software de un importante fabricante de dispositivos portátiles. ¿Hay alguna excusa para este error?
Después de ejecutar FILEMON desde http://sysinternals.com (en inglés), descubrí rápidamente que la aplicación estaba intentando abrir un archivo de datos para acceso de escritura que estaba instalado en el mismo directorio que el medio ejecutable de la aplicación. Cuando las aplicaciones se instalan en el directorio Archivos de programa como se esperaba, no deben intentar escribir datos en ese directorio. Archivos de programa tiene una política de control de acceso tan restrictiva por una razón. No queremos que los usuarios escriban en estos directorios, ya que esto facilitaría que un usuario deje un troyano para que otro lo ejecute. De hecho, esta convención es uno de los requisitos básicos de firma de Windos XP (consulte http://www.microsoft.com/winlogo [inglés]).
Escuchamos a muchos programadores dar excusas de por qué eligen ejecutar como administrador cuando desarrollan código. Si seguimos ignorando este problema, sólo empeoraremos las cosas. Amigos, no necesitan derechos de administrador para editar un archivo de texto. Tampoco se requieren derechos de administrador para editar o depurar un programa. Cuando necesite privilegios de administrador, utilice la función RunAs del sistema operativo para ejecutar 玎com.asp?TARGET=/winlogo/">http://www.microsoft.com/winlogo [inglés]).
Escuchamos esto demasiado. Los programadores dan excusas. Por qué eligen ejecutarse como administrador al desarrollar código. Si continuamos ignorando este problema, solo empeorará las cosas. Editar un archivo de texto no requiere privilegios de administrador o depurar un programa no requiere privilegios de administrador. privilegios, utilice la función RunAs del sistema operativo para ejecutar un programa separado con privilegios elevados (consulte la columna Resúmenes de seguridad [inglés] de noviembre de 2001). Si está escribiendo herramientas para desarrolladores, tiene una responsabilidad adicional para este grupo. detener este círculo vicioso de escribir código que sólo se puede ejecutar como administrador. El objetivo tiene que cambiar fundamentalmente.
Para obtener más información sobre cómo los desarrolladores pueden ejecutar fácilmente como no administradores, consulte el sitio web de Keith en http://www.develop. com/kbrown (en inglés) Consulte el libro de Michael Writing Secure Code (Microsoft Press, 2001), que proporciona consejos sobre cómo escribir aplicaciones que se ejecuten bien en un entorno sin administrador.